Complete cleanup and modernization of JavaScript testing infrastructure with comprehensive automated test coverage and improved output formatting. JavaScript Development Files Cleanup: - Moved 53 manual development/debugging test files to history/javascript-dev-tests/ - Added comprehensive README documenting archived files and their purposes - Cleaned main project directory of development artifacts New Automated Test Suite (68 tests): - keyboard-shortcuts.test.js: Tests Ctrl+Enter, Escape, accessibility features (8 tests) - section-splitting.test.js: Tests heading detection, content parsing, ID generation (14 tests) - image-editing.test.js: Tests dialog positioning, alt text, reset functionality (19 tests) - button-events.test.js: Tests click handling, state management, event delegation (21 tests) Integration Test Fixes: - Fixed 13 failing integration tests by properly mocking component dependencies - Updated tests to match actual component APIs instead of assumed interfaces - Improved error handling and test reliability Enhanced Test Output Formatting: - Updated testdrive-jsui-test-all target to show clear test count summaries - Separated JavaScript (68 tests) and Python (11 tests) results distinctly - Added combined summary showing total coverage (79 tests) - Improved error handling and visual formatting Main Makefile Improvements: - Fixed default target issue by adding .DEFAULT_GOAL := help - Restored proper make help behavior when called without arguments Key Achievements: - Replaced 53 manual test files with 68 automated tests - Achieved 100% test pass rate (79/79 tests passing) - Enhanced CI/CD integration with clear test reporting - Preserved all critical UI functionality in automated test coverage - Improved developer experience with clearer test output Testing Status: - ✅ 68 JavaScript tests (Jest) - Core UI functionality - ✅ 11 Python tests (pytest) - Integration bridge testing - ✅ 100% automated test coverage for critical functionality - ✅ Clean, maintainable test codebase 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
305 lines
13 KiB
JavaScript
305 lines
13 KiB
JavaScript
#!/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; |