#!/usr/bin/env node /** * TDD Tests for Enhanced DOM Event System Features */ const { TestRunner } = require('./test_runner.js'); const runner = new TestRunner(); // Test enhanced DOM event system advanced features runner.describe('Enhanced DOM Event System Advanced Features', () => { runner.it('should track event statistics and history', 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'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); // Verify event tracking capabilities runner.expect(typeof renderer.getEventStats).toBe('function'); runner.expect(Array.isArray(renderer.eventHistory)).toBeTruthy(); runner.expect(typeof renderer.eventStats).toBe('object'); // Initial state should be empty const initialStats = renderer.getEventStats(); runner.expect(initialStats.totalEvents).toBe(0); runner.expect(initialStats.recentEvents.length).toBe(0); } }); runner.it('should track section-click events with detailed data', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); // Simulate click and verify tracking const sectionElement = container.querySelector('[data-section-id]'); if (sectionElement) { const clickEvent = new Event('click', { bubbles: true }); sectionElement.dispatchEvent(clickEvent); const stats = renderer.getEventStats(); runner.expect(stats.stats['section-click']).toBe(1); runner.expect(stats.recentEvents.length).toBe(1); runner.expect(stats.recentEvents[0].type).toBe('section-click'); runner.expect(stats.recentEvents[0].data.sectionId).toBeTruthy(); } } }); runner.it('should track hover events separately for enter/leave', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); const sectionElement = container.querySelector('[data-section-id]'); if (sectionElement) { // Simulate hover enter and leave const mouseEnterEvent = new Event('mouseenter'); const mouseLeaveEvent = new Event('mouseleave'); sectionElement.dispatchEvent(mouseEnterEvent); sectionElement.dispatchEvent(mouseLeaveEvent); const stats = renderer.getEventStats(); runner.expect(stats.stats['section-hover-enter']).toBe(1); runner.expect(stats.stats['section-hover-leave']).toBe(1); } } }); runner.it('should track keyboard shortcuts with action data', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); manager.startEditing(sections[0].id); const textarea = container.querySelector('textarea'); if (textarea) { // Simulate Ctrl+Enter const keyEvent = new KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true, bubbles: true }); textarea.dispatchEvent(keyEvent); const stats = renderer.getEventStats(); runner.expect(stats.stats['keyboard-shortcut']).toBe(1); const shortcutEvent = stats.recentEvents.find(e => e.type === 'keyboard-shortcut'); if (shortcutEvent) { runner.expect(shortcutEvent.data.shortcut).toBe('ctrl+enter'); runner.expect(shortcutEvent.data.action).toBe('accept'); } } } }); runner.it('should make sections draggable with proper attributes', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); const sectionElements = container.querySelectorAll('[data-section-id]'); runner.expect(sectionElements.length).toBeGreaterThan(0); if (sectionElements.length > 0) { const sectionElement = sectionElements[0]; // Check draggable attribute runner.expect(sectionElement.draggable).toBeTruthy(); // Check accessibility attributes runner.expect(sectionElement.tabIndex).toBe(0); runner.expect(sectionElement.getAttribute('role')).toBe('article'); runner.expect(sectionElement.getAttribute('aria-label')).toBeTruthy(); // Check for drag handle const dragHandle = sectionElement.querySelector('.ui-edit-drag-handle'); runner.expect(dragHandle).toBeTruthy(); } } }); runner.it('should support context menu with proper menu items', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); const sectionElement = container.querySelector('[data-section-id]'); if (sectionElement) { // Simulate right-click const contextMenuEvent = new Event('contextmenu', { bubbles: true }); Object.defineProperty(contextMenuEvent, 'clientX', { value: 100 }); Object.defineProperty(contextMenuEvent, 'clientY', { value: 200 }); sectionElement.dispatchEvent(contextMenuEvent); // Check if context menu was created const contextMenu = document.querySelector('.ui-edit-context-menu'); runner.expect(contextMenu).toBeTruthy(); if (contextMenu) { // Should have menu items const menuItems = contextMenu.querySelectorAll('div'); runner.expect(menuItems.length).toBeGreaterThan(3); // At least 4 items // Clean up contextMenu.remove(); } // Should track the event const stats = renderer.getEventStats(); runner.expect(stats.stats['section-context-menu']).toBe(1); } } }); runner.it('should support drag and drop event tracking', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Section 1\n\nContent 1\n\n# Section 2\n\nContent 2'); const sectionElements = container.querySelectorAll('[data-section-id]'); if (sectionElements.length >= 2) { const source = sectionElements[0]; const target = sectionElements[1]; // Simulate drag start const dragStartEvent = new Event('dragstart'); Object.defineProperty(dragStartEvent, 'dataTransfer', { value: { setData: () => {}, effectAllowed: null } }); source.dispatchEvent(dragStartEvent); // Simulate drag over const dragOverEvent = new Event('dragover'); Object.defineProperty(dragOverEvent, 'dataTransfer', { value: { dropEffect: null } }); Object.defineProperty(dragOverEvent, 'preventDefault', { value: () => {} }); target.dispatchEvent(dragOverEvent); const stats = renderer.getEventStats(); runner.expect(stats.stats['section-drag-start']).toBe(1); runner.expect(stats.stats['section-drag-over']).toBe(1); } } }); runner.it('should handle multiple keyboard shortcuts correctly', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); manager.startEditing(sections[0].id); const textarea = container.querySelector('textarea'); if (textarea) { // Test different shortcuts const shortcuts = [ { key: 'Enter', ctrlKey: true, expected: 'ctrl+enter' }, { key: 's', ctrlKey: true, expected: 'ctrl+s' }, { key: 'Escape', ctrlKey: false, expected: 'escape' } ]; for (const shortcut of shortcuts) { // Need to restart editing for each test if (!sections[0].isEditing()) { manager.startEditing(sections[0].id); } const keyEvent = new KeyboardEvent('keydown', { key: shortcut.key, ctrlKey: shortcut.ctrlKey, bubbles: true }); textarea.dispatchEvent(keyEvent); } const stats = renderer.getEventStats(); runner.expect(stats.stats['keyboard-shortcut']).toBeGreaterThanOrEqual(3); } } }); runner.it('should support event history with timestamps', async () => { if (global.DOMRenderer && global.SectionManager) { const container = document.createElement('div'); const manager = new global.SectionManager(); const renderer = new global.DOMRenderer(manager, container); const sections = manager.createSectionsFromMarkdown('# Test Section\n\nTest content'); // Generate multiple events const sectionElement = container.querySelector('[data-section-id]'); if (sectionElement) { // Click event const clickEvent = new Event('click', { bubbles: true }); sectionElement.dispatchEvent(clickEvent); // Hover events const mouseEnterEvent = new Event('mouseenter'); const mouseLeaveEvent = new Event('mouseleave'); sectionElement.dispatchEvent(mouseEnterEvent); sectionElement.dispatchEvent(mouseLeaveEvent); const stats = renderer.getEventStats(); runner.expect(stats.totalEvents).toBe(3); runner.expect(stats.recentEvents.length).toBe(3); // Check that events have timestamps const hasTimestamps = stats.recentEvents.every(event => event.timestamp && typeof event.timestamp === 'string' ); runner.expect(hasTimestamps).toBeTruthy(); // Check that events are properly typed const eventTypes = stats.recentEvents.map(e => e.type); runner.expect(eventTypes.includes('section-click')).toBeTruthy(); runner.expect(eventTypes.includes('section-hover-enter')).toBeTruthy(); runner.expect(eventTypes.includes('section-hover-leave')).toBeTruthy(); } } }); }); // Run the tests if (require.main === module) { console.log('⚡ Running Enhanced DOM Event System Advanced Feature Tests'); runner.run().then(() => { console.log('✅ Enhanced DOM event system tests complete!'); }); } module.exports = runner;