#!/usr/bin/env node /** * Test Responsive Overlay UI * * Tests the new overlay editing UI with responsive button positioning: * - Overlay positions directly on original content * - Buttons in margin on wide displays (>1024px) * - Buttons beneath editor on narrow displays (≤1024px) */ const { TestRunner } = require('./test_runner.js'); const runner = new TestRunner(); runner.describe('Responsive Overlay UI Tests', () => { runner.it('should create overlay container with proper styling for text editor', async () => { // Load editor delete require.cache[require.resolve('/home/worsch/markitect_project/markitect/static/editor.js')]; require('/home/worsch/markitect_project/markitect/static/editor.js'); if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); container.innerHTML = '
'; document.body.appendChild(container); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); // Create section with text content const textMarkdown = '# Test Heading\n\nThis is test content for overlay UI.'; const sections = manager.createSectionsFromMarkdown(textMarkdown); const textSection = sections[0]; // Mock original element height const mockElement = document.createElement('div'); mockElement.setAttribute('data-section-id', textSection.id); mockElement.style.height = '150px'; renderer.findSectionElement = () => mockElement; // Show editor renderer.showEditor(textSection.id, textSection.currentMarkdown); // Verify overlay container exists const overlayContainer = mockElement.querySelector('.ui-edit-overlay-container'); runner.expect(overlayContainer).toBeTruthy(); runner.expect(overlayContainer.style.position).toBe('relative'); runner.expect(overlayContainer.style.zIndex).toBe('1000'); runner.expect(overlayContainer.style.display).toBe('flex'); // Verify editor content area const editorContent = overlayContainer.querySelector('.ui-edit-editor-content'); runner.expect(editorContent).toBeTruthy(); runner.expect(editorContent.style.flex).toBe('1'); // Verify controls are positioned correctly const controls = overlayContainer.querySelector('.ui-edit-controls'); runner.expect(controls).toBeTruthy(); runner.expect(controls.style.flexDirection).toBe('column'); runner.expect(controls.style.minWidth).toBe('100px'); // Verify buttons exist const acceptBtn = controls.querySelector('.ui-edit-button-accept'); const cancelBtn = controls.querySelector('.ui-edit-button-cancel'); const resetBtn = controls.querySelector('.ui-edit-button-reset'); runner.expect(acceptBtn).toBeTruthy(); runner.expect(cancelBtn).toBeTruthy(); runner.expect(resetBtn).toBeTruthy(); // Cleanup document.body.removeChild(container); } }); runner.it('should create overlay container with proper styling for image editor', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); container.innerHTML = ''; document.body.appendChild(container); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); // Create section with image content const imageMarkdown = ''; const sections = manager.createSectionsFromMarkdown(imageMarkdown); const imageSection = sections[0]; // Mock original element height const mockElement = document.createElement('div'); mockElement.setAttribute('data-section-id', imageSection.id); mockElement.style.height = '250px'; renderer.findSectionElement = () => mockElement; // Show image editor renderer.showImageEditor(imageSection.id, imageSection); // Verify overlay container exists with image editor classes const overlayContainer = mockElement.querySelector('.ui-edit-image-editor-container.ui-edit-overlay-container'); runner.expect(overlayContainer).toBeTruthy(); runner.expect(overlayContainer.style.position).toBe('relative'); runner.expect(overlayContainer.style.zIndex).toBe('1000'); runner.expect(overlayContainer.style.display).toBe('flex'); // Verify image editor content area const imageContent = overlayContainer.querySelector('.ui-edit-image-content'); runner.expect(imageContent).toBeTruthy(); runner.expect(imageContent.style.flex).toBe('1'); // Verify image preview exists const imagePreview = imageContent.querySelector('.ui-edit-image-preview'); runner.expect(imagePreview).toBeTruthy(); // Verify controls are positioned correctly const controls = overlayContainer.querySelector('.ui-edit-controls'); runner.expect(controls).toBeTruthy(); runner.expect(controls.style.flexDirection).toBe('column'); // Cleanup document.body.removeChild(container); } }); runner.it('should include responsive CSS for narrow displays', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); container.innerHTML = ''; document.body.appendChild(container); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); // Create section const textMarkdown = '# Test Content'; const sections = manager.createSectionsFromMarkdown(textMarkdown); const textSection = sections[0]; const mockElement = document.createElement('div'); mockElement.setAttribute('data-section-id', textSection.id); renderer.findSectionElement = () => mockElement; // Show editor (this adds responsive CSS) renderer.showEditor(textSection.id, textSection.currentMarkdown); // Verify responsive style was added to document head const responsiveStyles = Array.from(document.head.querySelectorAll('style')).find(style => style.textContent.includes('@media (max-width: 1024px)') ); runner.expect(responsiveStyles).toBeTruthy(); // Verify CSS content includes proper responsive rules const cssText = responsiveStyles.textContent; runner.expect(cssText.includes('.ui-edit-overlay-container')).toBeTruthy(); runner.expect(cssText.includes('flex-direction: column !important')).toBeTruthy(); runner.expect(cssText.includes('.ui-edit-controls')).toBeTruthy(); runner.expect(cssText.includes('flex-direction: row !important')).toBeTruthy(); runner.expect(cssText.includes('justify-content: flex-end !important')).toBeTruthy(); // Cleanup document.body.removeChild(container); } }); runner.it('should maintain minimum height based on original content', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); container.innerHTML = ''; document.body.appendChild(container); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); // Create section const textMarkdown = '# Test\n\nMultiple\nlines\nof\ncontent\nto\ntest\nheight\npreservation.'; const sections = manager.createSectionsFromMarkdown(textMarkdown); const textSection = sections[0]; // Mock element with specific height const mockElement = document.createElement('div'); mockElement.setAttribute('data-section-id', textSection.id); Object.defineProperty(mockElement, 'offsetHeight', { get: () => 300 }); renderer.findSectionElement = () => mockElement; // Show editor renderer.showEditor(textSection.id, textSection.currentMarkdown); // Verify overlay container maintains minimum height const overlayContainer = mockElement.querySelector('.ui-edit-overlay-container'); runner.expect(overlayContainer.style.minHeight).toBe('300px'); // Verify textarea adapts to content height const textarea = overlayContainer.querySelector('.ui-edit-textarea'); const textareaMinHeight = parseInt(textarea.style.minHeight); runner.expect(textareaMinHeight).toBeGreaterThan(100); // Should be based on original height // Cleanup document.body.removeChild(container); } }); runner.it('should handle button styling for vertical layout in margin', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); container.innerHTML = ''; document.body.appendChild(container); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const textMarkdown = '# Test Content'; const sections = manager.createSectionsFromMarkdown(textMarkdown); const textSection = sections[0]; const mockElement = document.createElement('div'); mockElement.setAttribute('data-section-id', textSection.id); renderer.findSectionElement = () => mockElement; renderer.showEditor(textSection.id, textSection.currentMarkdown); // Verify button styling const buttons = mockElement.querySelectorAll('.ui-edit-controls button'); runner.expect(buttons.length).toBe(3); // Accept, Cancel, Reset buttons.forEach(button => { const styles = button.style; runner.expect(styles.width).toBe('100%'); runner.expect(styles.padding).toBe('8px 12px'); runner.expect(styles.whiteSpace).toBe('nowrap'); }); // Cleanup document.body.removeChild(container); } }); }); // Run the tests if (require.main === module) { console.log('📱 Running Responsive Overlay UI Tests'); runner.run().then(() => { const results = runner.results; const failed = results.filter(r => r.status === 'FAIL').length; if (failed > 0) { console.log(`❌ ${failed} test(s) failed - responsive overlay UI needs attention`); } else { console.log('✅ All responsive overlay UI tests passed!'); } }); } module.exports = runner;