From 652cb4e255aa566a71c0a165b55416d99d2e453a Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 2 Oct 2026 22:59:33 +0200 Subject: [PATCH] style: apply eslint --fix (semi, indent 4); no behavior change, Jest 68/68 Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 299063@bnt-lap001 Assistant-Session: 53eb1e1c-c13a-4277-9ce2-3702960d93f7 --- js/components/debug-panel.js | 12 +- js/components/document-controls.js | 2 +- js/components/dom-renderer.js | 5 +- js/config-loader.js | 3 +- js/controls/contents-control.js | 4 +- js/controls/control-base.js | 32 +- js/controls/debug-control.js | 14 +- js/controls/edit-control.js | 7 +- js/controls/status-control.js | 12 +- js/core/debug-system.js | 7 +- js/core/section-manager.js | 6 +- js/main-updated.js | 4 +- js/main.js | 7 +- js/plugins/document-navigator-plugin.js | 76 +-- js/testdrive-jsui.js | 10 +- js/tests/button-events.test.js | 544 +++++++++--------- js/tests/component-integration.test.js | 2 +- js/tests/image-editing.test.js | 422 +++++++------- js/tests/jest.setup.js | 2 +- js/tests/keyboard-shortcuts.test.js | 360 ++++++------ js/tests/refactor-test-runner.js | 6 +- js/tests/section-splitting.test.js | 324 +++++------ js/tests/setup.js | 162 +++--- js/tests/test-component-integration.js | 10 +- js/tests/test-debugpanel-extraction.js | 3 +- js/tests/test-debugpanel-integration.js | 4 +- js/tests/test-document-navigator.js | 6 +- js/tests/test-documentcontrols-extraction.js | 3 +- js/tests/test-domrenderer-extraction.js | 5 +- js/tests/test-environment.test.js | 2 +- js/tests/test-extracted-domrenderer.js | 5 +- js/tests/test-extracted-section-manager.js | 7 +- js/tests/test-full-integration.js | 6 +- js/tests/test-real-user-functionality.js | 9 +- js/tests/test-section-manager-extraction.js | 7 +- js/utils/html-generator.js | 8 +- js/widgets/base/UIWidget.js | 2 +- js/widgets/base/Widget.js | 2 +- js/widgets/navigation/DocumentNavigator.js | 30 +- .../ui-edit-button/ui-edit-button.js | 18 +- .../ui-edit-button/ui-edit-button.stories.js | 6 +- .../ui-edit-button/ui-edit-button.test.js | 36 +- src/index.js | 4 +- 43 files changed, 1081 insertions(+), 1115 deletions(-) diff --git a/js/components/debug-panel.js b/js/components/debug-panel.js index d22706a..7613abf 100644 --- a/js/components/debug-panel.js +++ b/js/components/debug-panel.js @@ -116,11 +116,11 @@ class DebugPanel { const messagesHtml = recentMessages.map(msg => { const categoryColor = { - 'INFO': '#17a2b8', - 'WARNING': '#ffc107', - 'ERROR': '#dc3545', - 'SUCCESS': '#28a745', - 'DEBUG': '#6f42c1' + INFO: '#17a2b8', + WARNING: '#ffc107', + ERROR: '#dc3545', + SUCCESS: '#28a745', + DEBUG: '#6f42c1' }[msg.category] || '#6c757d'; return ` @@ -188,4 +188,4 @@ if (typeof module !== 'undefined' && module.exports) { // Export for browser use if (typeof window !== 'undefined') { window.DebugPanel = DebugPanel; -} \ No newline at end of file +} diff --git a/js/components/document-controls.js b/js/components/document-controls.js index fb83ebd..4d613b6 100644 --- a/js/components/document-controls.js +++ b/js/components/document-controls.js @@ -276,4 +276,4 @@ if (typeof module !== 'undefined' && module.exports) { // Export for browser use if (typeof window !== 'undefined') { window.DocumentControls = DocumentControls; -} \ No newline at end of file +} diff --git a/js/components/dom-renderer.js b/js/components/dom-renderer.js index 2074848..8e4a0bf 100644 --- a/js/components/dom-renderer.js +++ b/js/components/dom-renderer.js @@ -942,7 +942,6 @@ class DOMRenderer { controls.appendChild(cancelBtn); controls.appendChild(resetBtn); - // Event handlers acceptBtn.addEventListener('click', () => { // Apply staged changes only when accept is clicked @@ -1064,7 +1063,7 @@ class DOMRenderer { trackEvent(eventType, data) { const eventRecord = { type: eventType, - data: data, + data, timestamp: new Date().toISOString() }; @@ -1125,4 +1124,4 @@ if (typeof module !== 'undefined' && module.exports) { if (typeof window !== 'undefined') { window.DOMRenderer = DOMRenderer; window.FloatingMenu = FloatingMenu; -} \ No newline at end of file +} diff --git a/js/config-loader.js b/js/config-loader.js index 70964a7..171cf27 100644 --- a/js/config-loader.js +++ b/js/config-loader.js @@ -27,7 +27,6 @@ class MarkitectConfig { // Validate required fields this.validateConfig(); - } catch (error) { console.error('āŒ Failed to load Markitect configuration:', error); this.config = this.getDefaultConfig(); @@ -165,4 +164,4 @@ window.markitectBase64References = window.markitectConfig.base64References; // Export for module use if (typeof module !== 'undefined' && module.exports) { module.exports = MarkitectConfig; -} \ No newline at end of file +} diff --git a/js/controls/contents-control.js b/js/controls/contents-control.js index 19bd709..695e4be 100644 --- a/js/controls/contents-control.js +++ b/js/controls/contents-control.js @@ -114,7 +114,6 @@ class ContentsControl extends ControlBase { ${contentsHTML} ${refreshButtonHTML} `; - }, 'Error generating contents', 'generateContent'); } @@ -164,7 +163,6 @@ class ContentsControl extends ControlBase { this.headings = extractedHeadings; this.lastScanTime = Date.now(); return extractedHeadings; - }, [], 'extractHeadings'); } @@ -286,4 +284,4 @@ if (typeof module !== 'undefined' && module.exports) { // Also expose globally: bundlers shim `module`, so the else-branch never ran in dist builds if (typeof window !== 'undefined') { window.ContentsControl = ContentsControl; -} \ No newline at end of file +} diff --git a/js/controls/control-base.js b/js/controls/control-base.js index fa2f0f9..2bd764b 100644 --- a/js/controls/control-base.js +++ b/js/controls/control-base.js @@ -48,14 +48,14 @@ class ControlBase { className: 'base-control', defaultContent: 'Control content', ariaLabel: 'Base Control', - position: 'w', // Compass position: west (middle-left) - footer: null // Custom footer text + position: 'w', // Compass position: west (middle-left) + footer: null // Custom footer text }; // Internal state this.element = null; this.isExpanded = false; - this.isHeaderOnly = false; // New state for header-only visibility + this.isHeaderOnly = false; // New state for header-only visibility this.isDragging = false; this.isResizing = false; this.position = { x: 0, y: 0 }; @@ -63,7 +63,7 @@ class ControlBase { width: 300, height: Math.floor(window.innerHeight / 3) }; - this.originalPosition = null; // Store original position for collapse + this.originalPosition = null; // Store original position for collapse // Event handlers storage this.eventHandlers = new Map(); @@ -120,7 +120,6 @@ class ControlBase { this.setupStyles(); this.setupEventListeners(); return control; - }, null, 'createElement'); } @@ -235,17 +234,17 @@ class ControlBase { */ getCompassPosition() { const positions = { - 'n': { top: '20px', left: '50%', transform: 'translateX(-50%)' }, - 'ne': { top: '20px', right: '20px' }, - 'e': { right: '20px', top: '50%', transform: 'translateY(-50%)' }, - 'se': { bottom: '20px', right: '20px' }, - 's': { bottom: '20px', left: '50%', transform: 'translateX(-50%)' }, - 'sw': { bottom: '20px', left: '20px' }, - 'w': { left: '20px', top: '50%', transform: 'translateY(-50%)' }, - 'nw': { top: '20px', left: '20px' } + n: { top: '20px', left: '50%', transform: 'translateX(-50%)' }, + ne: { top: '20px', right: '20px' }, + e: { right: '20px', top: '50%', transform: 'translateY(-50%)' }, + se: { bottom: '20px', right: '20px' }, + s: { bottom: '20px', left: '50%', transform: 'translateX(-50%)' }, + sw: { bottom: '20px', left: '20px' }, + w: { left: '20px', top: '50%', transform: 'translateY(-50%)' }, + nw: { top: '20px', left: '20px' } }; - return positions[this.config.position] || positions['w']; + return positions[this.config.position] || positions.w; } /** @@ -733,7 +732,6 @@ class ControlBase { // Update internal size tracking this.size.width = idealWidth; this.size.height = idealHeight; - }, null, 'autoResizeToContent'); } @@ -792,7 +790,7 @@ class ControlBase { * @returns {string} HTML content for the panel body */ generateContent() { - return this.config.defaultContent || `

Panel content goes here...

`; + return this.config.defaultContent || '

Panel content goes here...

'; } /** @@ -851,4 +849,4 @@ if (typeof module !== 'undefined' && module.exports) { // Also expose globally: bundlers shim `module`, so the else-branch never ran in dist builds if (typeof window !== 'undefined') { window.ControlBase = ControlBase; -} \ No newline at end of file +} diff --git a/js/controls/debug-control.js b/js/controls/debug-control.js index 0c8a886..eb8acaa 100644 --- a/js/controls/debug-control.js +++ b/js/controls/debug-control.js @@ -101,7 +101,6 @@ class DebugControl extends ControlBase { window.addEventListener('unhandledrejection', (event) => { this.addDebugMessage('PROMISE_REJECT', `Unhandled promise rejection: ${event.reason}`, 'error'); }); - }, null, 'initializeDebugCapture'); } @@ -133,7 +132,6 @@ class DebugControl extends ControlBase { if (this.element && this.isExpanded) { this.updateMessageDisplay(); } - }, null, 'addDebugMessage'); } @@ -162,9 +160,9 @@ class DebugControl extends ControlBase { cookieEnabled: navigator.cookieEnabled, onlineStatus: navigator.onLine ? 'Online' : 'Offline', protocol: window.location.protocol, - memory: performance.memory ? - `Used: ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB` : - 'Not available' + memory: performance.memory + ? `Used: ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB` + : 'Not available' }; // Get Markitect version from config or default @@ -183,7 +181,6 @@ class DebugControl extends ControlBase { `; - }, '', 'generateSystemInfoHTML'); } @@ -215,7 +212,6 @@ class DebugControl extends ControlBase { `; - }, '', 'generatePerformanceHTML'); } @@ -267,7 +263,6 @@ class DebugControl extends ControlBase { ${messagesHTML} `; - }, '

Error displaying messages

', 'generateMessagesHTML'); } @@ -389,7 +384,6 @@ class DebugControl extends ControlBase { URL.revokeObjectURL(url); this.addDebugMessage('EXPORT', 'Debug log exported successfully', 'info'); - }, null, 'exportMessages'); } @@ -482,4 +476,4 @@ if (typeof module !== 'undefined' && module.exports) { // Also expose globally: bundlers shim `module`, so the else-branch never ran in dist builds if (typeof window !== 'undefined') { window.DebugControl = DebugControl; -} \ No newline at end of file +} diff --git a/js/controls/edit-control.js b/js/controls/edit-control.js index 889b768..65d3d86 100644 --- a/js/controls/edit-control.js +++ b/js/controls/edit-control.js @@ -178,7 +178,6 @@ class EditControl extends ControlBase { `; - }, '

Error generating edit tools

', 'generateEditToolsHTML'); } @@ -264,7 +263,7 @@ class EditControl extends ControlBase { exportMenu.exportAsMarkdown = () => { // Simple HTML to Markdown conversion (basic) - let markdown = htmlContent + const markdown = htmlContent .replace(/]*>(.*?)<\/h1>/gi, '# $1\n\n') .replace(/]*>(.*?)<\/h2>/gi, '## $1\n\n') .replace(/]*>(.*?)<\/h3>/gi, '### $1\n\n') @@ -278,7 +277,6 @@ class EditControl extends ControlBase { }; document.body.appendChild(exportMenu); - }, null, 'exportDocument'); } @@ -356,7 +354,6 @@ class EditControl extends ControlBase { // Show feedback this.showActionFeedback('šŸ”„ All changes reset', '#ffc107', '#212529'); - }, null, 'resetAll'); } @@ -572,4 +569,4 @@ if (typeof module !== 'undefined' && module.exports) { // Also expose globally: bundlers shim `module`, so the else-branch never ran in dist builds if (typeof window !== 'undefined') { window.EditControl = EditControl; -} \ No newline at end of file +} diff --git a/js/controls/status-control.js b/js/controls/status-control.js index e08f4db..64e4d9a 100644 --- a/js/controls/status-control.js +++ b/js/controls/status-control.js @@ -90,7 +90,6 @@ class StatusControl extends ControlBase { this.lastUpdateTime = Date.now(); return this.stats; - }, this.stats, 'analyzeDocument'); } @@ -198,13 +197,14 @@ class StatusControl extends ControlBase { - ${this.lastUpdateTime ? ` + ${this.lastUpdateTime + ? `
Updated: ${new Date(this.lastUpdateTime).toLocaleTimeString()}
- ` : ''} + ` + : ''} `; - }, '

Error displaying statistics

', 'formatStatistics'); } @@ -232,7 +232,6 @@ class StatusControl extends ControlBase { refreshBtn.innerHTML = originalText; }, 1000); } - }, null, 'refreshStats'); } @@ -282,7 +281,6 @@ class StatusControl extends ControlBase { exportBtn.style.background = '#28a745'; }, 2000); } - }, null, 'exportStats'); } @@ -370,4 +368,4 @@ if (typeof module !== 'undefined' && module.exports) { // Also expose globally: bundlers shim `module`, so the else-branch never ran in dist builds if (typeof window !== 'undefined') { window.StatusControl = StatusControl; -} \ No newline at end of file +} diff --git a/js/core/debug-system.js b/js/core/debug-system.js index 410d02d..f33da3a 100644 --- a/js/core/debug-system.js +++ b/js/core/debug-system.js @@ -83,8 +83,9 @@ class MarkitectDebugSystem { this.notifySubscribers(messageObj); // Console output for development - const consoleMethod = category.toLowerCase() === 'error' ? 'error' : - category.toLowerCase() === 'warning' ? 'warn' : 'log'; + const consoleMethod = category.toLowerCase() === 'error' + ? 'error' + : category.toLowerCase() === 'warning' ? 'warn' : 'log'; console[consoleMethod](`[${source}] ${message}`, context); return messageObj; @@ -290,4 +291,4 @@ class MarkitectDebugSystem { window.MarkitectDebugSystem = new MarkitectDebugSystem(); // ES6 export for bundler -export { MarkitectDebugSystem }; \ No newline at end of file +export { MarkitectDebugSystem }; diff --git a/js/core/section-manager.js b/js/core/section-manager.js index b1dc6fd..2f062e1 100644 --- a/js/core/section-manager.js +++ b/js/core/section-manager.js @@ -493,7 +493,7 @@ class SectionManager { return { totalSections: sections.length, - editingSections: editingSections + editingSections }; } @@ -518,7 +518,7 @@ class SectionManager { // Emit section-split event this.emit('section-split', { originalSectionId: sectionId, - newSections: newSections, + newSections, count: newSections.length }); @@ -541,4 +541,4 @@ if (typeof window !== 'undefined') { window.Section = Section; window.EditState = EditState; window.SectionType = SectionType; -} \ No newline at end of file +} diff --git a/js/main-updated.js b/js/main-updated.js index e200d0d..12b3c64 100644 --- a/js/main-updated.js +++ b/js/main-updated.js @@ -40,7 +40,6 @@ const MarkitectMain = { this.initialized = true; console.log('āœ… MarkitectMain initialization complete'); - } catch (error) { console.error('āŒ MarkitectMain initialization failed:', error); this.fallbackMode(); @@ -153,7 +152,6 @@ const MarkitectMain = { this.debugPanel.addMessage(`Document saved as: ${filename}`, 'SUCCESS'); } console.log(`āœ… Document saved as: ${filename}`); - } catch (error) { if (this.debugPanel) { this.debugPanel.addMessage(`Save failed: ${error.message}`, 'ERROR'); @@ -284,4 +282,4 @@ if (document.readyState === 'loading') { } else { // DOM already ready setTimeout(() => MarkitectMain.initialize(), 100); -} \ No newline at end of file +} diff --git a/js/main.js b/js/main.js index 40f8c48..7ab7dc1 100644 --- a/js/main.js +++ b/js/main.js @@ -36,7 +36,7 @@ const MarkitectMain = { // In strict mode, throw on errors for immediate development feedback if (MARKITECT_STRICT_MODE && level === 'ERROR') { - console.error(`🚨 STRICT MODE: Throwing error for immediate diagnosis`); + console.error('🚨 STRICT MODE: Throwing error for immediate diagnosis'); throw new Error(`${component}: ${message}`); } @@ -84,7 +84,6 @@ const MarkitectMain = { clearTimeout(timeout); this.safeLog(`${controlName} initialized successfully`, 'SUCCESS'); return controlInstance; - } catch (error) { clearTimeout(timeout); this.safeLog(`${controlName} initialization failed: ${error.message}`, 'ERROR'); @@ -103,7 +102,7 @@ const MarkitectMain = { try { const fallback = Object.create(window.Control); fallback.config = { - icon: icon, + icon, title: `${name} (Fallback)`, className: `${name.toLowerCase()}-fallback`, defaultContent: `${name} is running in fallback mode due to initialization issues.`, @@ -198,4 +197,4 @@ if (document.readyState === 'loading') { } else { // DOM already loaded setTimeout(() => MarkitectMain.initialize(), 100); -} \ No newline at end of file +} diff --git a/js/plugins/document-navigator-plugin.js b/js/plugins/document-navigator-plugin.js index e95907c..fdc5689 100644 --- a/js/plugins/document-navigator-plugin.js +++ b/js/plugins/document-navigator-plugin.js @@ -25,26 +25,26 @@ export default { // Default configuration defaultOptions: { - position: 'left', // 'left' or 'right' side - collapsed: true, // Start in collapsed state - autoHide: true, // Hide on mobile devices - maxHeadingLevel: 3, // Include H1, H2, H3 - enableScrollSpy: true, // Highlight current section - smoothScroll: true, // Smooth scroll to headings - animationDuration: 300, // Animation timing in ms - minHeadings: 2, // Minimum headings to show widget - theme: 'default', // Theme variant + position: 'left', // 'left' or 'right' side + collapsed: true, // Start in collapsed state + autoHide: true, // Hide on mobile devices + maxHeadingLevel: 3, // Include H1, H2, H3 + enableScrollSpy: true, // Highlight current section + smoothScroll: true, // Smooth scroll to headings + animationDuration: 300, // Animation timing in ms + minHeadings: 2, // Minimum headings to show widget + theme: 'default', // Theme variant // Layout options - width: '280px', // Expanded width - collapsedWidth: '40px', // Collapsed width - offset: { // Position offset + width: '280px', // Expanded width + collapsedWidth: '40px', // Collapsed width + offset: { // Position offset top: '80px', side: '20px' }, // Accessibility - enableKeyboard: true, // Keyboard navigation support + enableKeyboard: true, // Keyboard navigation support ariaLabel: 'Document Navigation' }, @@ -69,42 +69,42 @@ export default { // Feature flags and capabilities capabilities: { - draggable: false, // Not draggable (fixed position) - resizable: false, // Not resizable (fixed width) - themeable: true, // Supports themes - persistent: false, // Rebuilds on page changes - responsive: true, // Responsive behavior - keyboard: true, // Keyboard accessible - scrollSpy: true, // Scroll spy functionality - smoothScroll: true // Smooth scroll navigation + draggable: false, // Not draggable (fixed position) + resizable: false, // Not resizable (fixed width) + themeable: true, // Supports themes + persistent: false, // Rebuilds on page changes + responsive: true, // Responsive behavior + keyboard: true, // Keyboard accessible + scrollSpy: true, // Scroll spy functionality + smoothScroll: true // Smooth scroll navigation }, // Integration requirements requirements: { - container: true, // Requires container element - headings: true, // Requires document headings - scrollable: true // Requires scrollable content + container: true, // Requires container element + headings: true, // Requires document headings + scrollable: true // Requires scrollable content }, // Event types emitted by this widget events: [ - 'rendered', // Widget rendered to DOM - 'navigate', // User navigated to heading - 'toggle', // Widget expanded/collapsed - 'theme-changed', // Theme was changed - 'destroyed' // Widget was destroyed + 'rendered', // Widget rendered to DOM + 'navigate', // User navigated to heading + 'toggle', // Widget expanded/collapsed + 'theme-changed', // Theme was changed + 'destroyed' // Widget was destroyed ], // CSS classes used by this widget cssClasses: [ - 'document-navigator', // Main widget class - 'navigator-toggle', // Toggle button - 'navigator-list', // Navigation list - 'navigator-item', // Navigation items - 'navigator-link', // Navigation links - 'navigator-header', // List header - 'navigator-close', // Close button - 'navigator-empty' // Empty state + 'document-navigator', // Main widget class + 'navigator-toggle', // Toggle button + 'navigator-list', // Navigation list + 'navigator-item', // Navigation items + 'navigator-link', // Navigation links + 'navigator-header', // List header + 'navigator-close', // Close button + 'navigator-empty' // Empty state ], // Theme variants @@ -204,4 +204,4 @@ export default { return { navigator, container }; } } -}; \ No newline at end of file +}; diff --git a/js/testdrive-jsui.js b/js/testdrive-jsui.js index 1bbc184..d3219bb 100644 --- a/js/testdrive-jsui.js +++ b/js/testdrive-jsui.js @@ -34,19 +34,19 @@ class TestDriveJSUI { this.config = { container: options.container, markdown: options.markdown || '# Welcome\n\nStart editing...', - mode: options.mode || 'edit', // 'edit' or 'view' + mode: options.mode || 'edit', // 'edit' or 'view' theme: options.theme || 'github', autosave: options.autosave || false, - shortcuts: options.shortcuts !== false, // default true - sections: options.sections !== false, // default true + shortcuts: options.shortcuts !== false, // default true + sections: options.sections !== false, // default true debug: options.debug || false, // Advanced control panels (compass-positioned) controls: { - editControl: options.controls?.editControl !== false, // default true + editControl: options.controls?.editControl !== false, // default true statusControl: options.controls?.statusControl !== false, // default true contentsControl: options.controls?.contentsControl !== false, // default true - debugControl: options.controls?.debugControl || false, // default false + debugControl: options.controls?.debugControl || false, // default false // Legacy simple control panel (top-right box) simpleControls: options.controls?.simpleControls || false // default false diff --git a/js/tests/button-events.test.js b/js/tests/button-events.test.js index 9d35543..bf66f3d 100644 --- a/js/tests/button-events.test.js +++ b/js/tests/button-events.test.js @@ -6,12 +6,12 @@ */ describe('Button Functionality and DOM Events', () => { - let mockSection; - let documentControls; + let mockSection; + let documentControls; - beforeEach(() => { + beforeEach(() => { // Setup DOM with various buttons and controls - document.body.innerHTML = ` + document.body.innerHTML = `
@@ -31,319 +31,319 @@ describe('Button Functionality and DOM Events', () => {
`; - mockSection = document.querySelector('.section'); + mockSection = document.querySelector('.section'); - // Load components - require('../components/document-controls.js'); - if (global.DocumentControls) { - documentControls = new global.DocumentControls(document.getElementById('content')); - } - }); - - afterEach(() => { - document.body.innerHTML = ''; - jest.clearAllMocks(); - }); - - describe('Section edit buttons', () => { - test('should show accept/cancel buttons when edit is clicked', () => { - const editBtn = document.querySelector('.edit-btn'); - const acceptBtn = document.querySelector('.accept-btn'); - const cancelBtn = document.querySelector('.cancel-btn'); - - expect(editBtn).toBeTruthy(); - - // Simulate edit button click - editBtn.click(); - - // In real implementation, accept/cancel should become visible - expect(acceptBtn.style.display).toBe('none'); // Initially hidden - expect(cancelBtn.style.display).toBe('none'); // Initially hidden - - // Test that buttons exist for functionality - expect(acceptBtn).toBeTruthy(); - expect(cancelBtn).toBeTruthy(); + // Load components + require('../components/document-controls.js'); + if (global.DocumentControls) { + documentControls = new global.DocumentControls(document.getElementById('content')); + } }); - test('should hide edit button when in edit mode', () => { - const editBtn = document.querySelector('.edit-btn'); - - editBtn.click(); - - // In real implementation, edit button should be hidden - expect(editBtn.style.display).not.toBe('block'); + afterEach(() => { + document.body.innerHTML = ''; + jest.clearAllMocks(); }); - test('should restore edit button when edit is cancelled', () => { - const editBtn = document.querySelector('.edit-btn'); - const cancelBtn = document.querySelector('.cancel-btn'); + describe('Section edit buttons', () => { + test('should show accept/cancel buttons when edit is clicked', () => { + const editBtn = document.querySelector('.edit-btn'); + const acceptBtn = document.querySelector('.accept-btn'); + const cancelBtn = document.querySelector('.cancel-btn'); - // Simulate edit mode - editBtn.style.display = 'none'; - cancelBtn.style.display = 'inline-block'; + expect(editBtn).toBeTruthy(); - cancelBtn.click(); + // Simulate edit button click + editBtn.click(); - // In real implementation, should restore edit button - expect(cancelBtn).toBeTruthy(); - expect(editBtn).toBeTruthy(); + // In real implementation, accept/cancel should become visible + expect(acceptBtn.style.display).toBe('none'); // Initially hidden + expect(cancelBtn.style.display).toBe('none'); // Initially hidden + + // Test that buttons exist for functionality + expect(acceptBtn).toBeTruthy(); + expect(cancelBtn).toBeTruthy(); + }); + + test('should hide edit button when in edit mode', () => { + const editBtn = document.querySelector('.edit-btn'); + + editBtn.click(); + + // In real implementation, edit button should be hidden + expect(editBtn.style.display).not.toBe('block'); + }); + + test('should restore edit button when edit is cancelled', () => { + const editBtn = document.querySelector('.edit-btn'); + const cancelBtn = document.querySelector('.cancel-btn'); + + // Simulate edit mode + editBtn.style.display = 'none'; + cancelBtn.style.display = 'inline-block'; + + cancelBtn.click(); + + // In real implementation, should restore edit button + expect(cancelBtn).toBeTruthy(); + expect(editBtn).toBeTruthy(); + }); }); - }); - describe('Button event propagation', () => { - test('should prevent event bubbling for section buttons', () => { - const editBtn = document.querySelector('.edit-btn'); - let sectionClicked = false; + describe('Button event propagation', () => { + test('should prevent event bubbling for section buttons', () => { + const editBtn = document.querySelector('.edit-btn'); + let sectionClicked = false; - mockSection.addEventListener('click', () => { - sectionClicked = true; - }); + mockSection.addEventListener('click', () => { + sectionClicked = true; + }); - // Create event with stopPropagation mock - const clickEvent = new Event('click', { bubbles: true }); - clickEvent.stopPropagation = jest.fn(); + // Create event with stopPropagation mock + const clickEvent = new Event('click', { bubbles: true }); + clickEvent.stopPropagation = jest.fn(); - editBtn.dispatchEvent(clickEvent); + editBtn.dispatchEvent(clickEvent); - // In real implementation, should call stopPropagation - expect(clickEvent.stopPropagation).toHaveBeenCalledWith || + // In real implementation, should call stopPropagation + expect(clickEvent.stopPropagation).toHaveBeenCalledWith || expect(sectionClicked).toBe(false); + }); + + test('should handle rapid button clicks gracefully', () => { + const editBtn = document.querySelector('.edit-btn'); + + // Simulate rapid clicks + for (let i = 0; i < 5; i++) { + editBtn.click(); + } + + // Should not cause errors + expect(editBtn).toBeTruthy(); + }); + + test('should debounce button actions', () => { + const saveBtn = document.querySelector('.save-all-btn'); + let clickCount = 0; + + const debouncedHandler = jest.fn(() => { + clickCount++; + }); + + saveBtn.addEventListener('click', debouncedHandler); + + // Simulate multiple quick clicks + saveBtn.click(); + saveBtn.click(); + saveBtn.click(); + + expect(debouncedHandler).toHaveBeenCalledTimes(3); + }); }); - test('should handle rapid button clicks gracefully', () => { - const editBtn = document.querySelector('.edit-btn'); + describe('Button state management', () => { + test('should disable buttons during processing', () => { + const acceptBtn = document.querySelector('.accept-btn'); - // Simulate rapid clicks - for (let i = 0; i < 5; i++) { - editBtn.click(); - } + // Simulate processing state + acceptBtn.disabled = true; - // Should not cause errors - expect(editBtn).toBeTruthy(); + expect(acceptBtn.disabled).toBe(true); + }); + + test('should show loading state for async operations', () => { + const saveBtn = document.querySelector('.save-all-btn'); + + // Simulate loading state + const originalText = saveBtn.textContent; + saveBtn.textContent = 'Saving...'; + saveBtn.disabled = true; + + expect(saveBtn.textContent).toBe('Saving...'); + expect(saveBtn.disabled).toBe(true); + + // Restore state + saveBtn.textContent = originalText; + saveBtn.disabled = false; + + expect(saveBtn.textContent).toBe('Save All'); + expect(saveBtn.disabled).toBe(false); + }); + + test('should maintain button visibility states', () => { + const buttons = { + edit: document.querySelector('.edit-btn'), + accept: document.querySelector('.accept-btn'), + cancel: document.querySelector('.cancel-btn') + }; + + // Default state: edit visible, accept/cancel hidden + expect(buttons.edit.style.display).not.toBe('none'); + expect(buttons.accept.style.display).toBe('none'); + expect(buttons.cancel.style.display).toBe('none'); + }); }); - test('should debounce button actions', () => { - const saveBtn = document.querySelector('.save-all-btn'); - let clickCount = 0; + describe('DOM event handling', () => { + test('should handle click events correctly', () => { + const addSectionBtn = document.querySelector('.add-section-btn'); + let clicked = false; - const debouncedHandler = jest.fn(() => { - clickCount++; - }); + addSectionBtn.addEventListener('click', () => { + clicked = true; + }); - saveBtn.addEventListener('click', debouncedHandler); + addSectionBtn.click(); - // Simulate multiple quick clicks - saveBtn.click(); - saveBtn.click(); - saveBtn.click(); + expect(clicked).toBe(true); + }); - expect(debouncedHandler).toHaveBeenCalledTimes(3); - }); - }); + test('should handle keyboard events for accessibility', () => { + const editBtn = document.querySelector('.edit-btn'); + let keyPressed = false; - describe('Button state management', () => { - test('should disable buttons during processing', () => { - const acceptBtn = document.querySelector('.accept-btn'); + editBtn.addEventListener('keydown', (event) => { + if (event.key === 'Enter' || event.key === ' ') { + keyPressed = true; + } + }); - // Simulate processing state - acceptBtn.disabled = true; + // Simulate Enter key press + const enterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + bubbles: true + }); - expect(acceptBtn.disabled).toBe(true); + editBtn.dispatchEvent(enterEvent); + + expect(keyPressed).toBe(true); + }); + + test('should handle focus and blur events', () => { + const editBtn = document.querySelector('.edit-btn'); + let focused = false; + let blurred = false; + + editBtn.addEventListener('focus', () => { + focused = true; + }); + + editBtn.addEventListener('blur', () => { + blurred = true; + }); + + editBtn.focus(); + expect(focused).toBe(true); + + editBtn.blur(); + expect(blurred).toBe(true); + }); }); - test('should show loading state for async operations', () => { - const saveBtn = document.querySelector('.save-all-btn'); + describe('Button positioning and layout', () => { + test('should position floating controls correctly', () => { + const floatingControls = document.querySelector('.floating-controls'); - // Simulate loading state - const originalText = saveBtn.textContent; - saveBtn.textContent = 'Saving...'; - saveBtn.disabled = true; + // Test positioning properties + floatingControls.style.position = 'fixed'; + floatingControls.style.top = '20px'; + floatingControls.style.right = '20px'; - expect(saveBtn.textContent).toBe('Saving...'); - expect(saveBtn.disabled).toBe(true); + expect(floatingControls.style.position).toBe('fixed'); + expect(floatingControls.style.top).toBe('20px'); + expect(floatingControls.style.right).toBe('20px'); + }); - // Restore state - saveBtn.textContent = originalText; - saveBtn.disabled = false; + test('should handle responsive button layouts', () => { + const sectionControls = document.querySelector('.section-controls'); - expect(saveBtn.textContent).toBe('Save All'); - expect(saveBtn.disabled).toBe(false); + // Test responsive classes + sectionControls.classList.add('responsive-controls'); + + expect(sectionControls.classList.contains('responsive-controls')).toBe(true); + }); + + test('should maintain button alignment in sections', () => { + const controls = document.querySelector('.section-controls'); + const buttons = controls.querySelectorAll('button'); + + expect(buttons.length).toBeGreaterThan(0); + + // All buttons should be in the same container + buttons.forEach(button => { + expect(button.parentElement).toBe(controls); + }); + }); }); - test('should maintain button visibility states', () => { - const buttons = { - edit: document.querySelector('.edit-btn'), - accept: document.querySelector('.accept-btn'), - cancel: document.querySelector('.cancel-btn') - }; + describe('Button confirmation dialogs', () => { + test('should show confirmation for destructive actions', () => { + const deleteBtn = document.querySelector('.delete-btn'); - // Default state: edit visible, accept/cancel hidden - expect(buttons.edit.style.display).not.toBe('none'); - expect(buttons.accept.style.display).toBe('none'); - expect(buttons.cancel.style.display).toBe('none'); - }); - }); + // Mock confirm dialog + window.confirm = jest.fn(() => false); - describe('DOM event handling', () => { - test('should handle click events correctly', () => { - const addSectionBtn = document.querySelector('.add-section-btn'); - let clicked = false; + deleteBtn.addEventListener('click', () => { + if (window.confirm('Are you sure you want to delete this section?')) { + // Perform deletion + } + }); - addSectionBtn.addEventListener('click', () => { - clicked = true; - }); + deleteBtn.click(); - addSectionBtn.click(); + // Should show confirmation + expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to delete this section?'); + }); - expect(clicked).toBe(true); + test('should cancel action when confirmation is denied', () => { + const deleteBtn = document.querySelector('.delete-btn'); + let deleted = false; + + window.confirm = jest.fn(() => false); + + deleteBtn.addEventListener('click', () => { + if (window.confirm('Are you sure?')) { + deleted = true; + } + }); + + deleteBtn.click(); + + expect(deleted).toBe(false); + }); }); - test('should handle keyboard events for accessibility', () => { - const editBtn = document.querySelector('.edit-btn'); - let keyPressed = false; + describe('DocumentControls integration', () => { + test('should integrate with DocumentControls class', () => { + if (documentControls) { + expect(typeof documentControls.create).toBe('function'); + expect(typeof documentControls.addButton).toBe('function'); + expect(typeof documentControls.setEventHandlers).toBe('function'); + } + }); - editBtn.addEventListener('keydown', (event) => { - if (event.key === 'Enter' || event.key === ' ') { - keyPressed = true; - } - }); + test('should handle button events through DocumentControls', () => { + if (!documentControls) return; - // Simulate Enter key press - const enterEvent = new KeyboardEvent('keydown', { - key: 'Enter', - bubbles: true - }); + // Test that DocumentControls can manage event handlers + expect(documentControls.eventHandlers).toBeDefined(); + expect(documentControls.eventHandlers instanceof Map).toBe(true); + }); - editBtn.dispatchEvent(enterEvent); + test('should handle button actions through event delegation', () => { + const content = document.getElementById('content'); + let actionTriggered = ''; - expect(keyPressed).toBe(true); + content.addEventListener('click', (event) => { + if (event.target.matches('button[data-action]')) { + actionTriggered = event.target.getAttribute('data-action'); + } + }); + + const editBtn = document.querySelector('.edit-btn'); + editBtn.click(); + + expect(actionTriggered).toBe('edit'); + }); }); - - test('should handle focus and blur events', () => { - const editBtn = document.querySelector('.edit-btn'); - let focused = false; - let blurred = false; - - editBtn.addEventListener('focus', () => { - focused = true; - }); - - editBtn.addEventListener('blur', () => { - blurred = true; - }); - - editBtn.focus(); - expect(focused).toBe(true); - - editBtn.blur(); - expect(blurred).toBe(true); - }); - }); - - describe('Button positioning and layout', () => { - test('should position floating controls correctly', () => { - const floatingControls = document.querySelector('.floating-controls'); - - // Test positioning properties - floatingControls.style.position = 'fixed'; - floatingControls.style.top = '20px'; - floatingControls.style.right = '20px'; - - expect(floatingControls.style.position).toBe('fixed'); - expect(floatingControls.style.top).toBe('20px'); - expect(floatingControls.style.right).toBe('20px'); - }); - - test('should handle responsive button layouts', () => { - const sectionControls = document.querySelector('.section-controls'); - - // Test responsive classes - sectionControls.classList.add('responsive-controls'); - - expect(sectionControls.classList.contains('responsive-controls')).toBe(true); - }); - - test('should maintain button alignment in sections', () => { - const controls = document.querySelector('.section-controls'); - const buttons = controls.querySelectorAll('button'); - - expect(buttons.length).toBeGreaterThan(0); - - // All buttons should be in the same container - buttons.forEach(button => { - expect(button.parentElement).toBe(controls); - }); - }); - }); - - describe('Button confirmation dialogs', () => { - test('should show confirmation for destructive actions', () => { - const deleteBtn = document.querySelector('.delete-btn'); - - // Mock confirm dialog - window.confirm = jest.fn(() => false); - - deleteBtn.addEventListener('click', () => { - if (window.confirm('Are you sure you want to delete this section?')) { - // Perform deletion - } - }); - - deleteBtn.click(); - - // Should show confirmation - expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to delete this section?'); - }); - - test('should cancel action when confirmation is denied', () => { - const deleteBtn = document.querySelector('.delete-btn'); - let deleted = false; - - window.confirm = jest.fn(() => false); - - deleteBtn.addEventListener('click', () => { - if (window.confirm('Are you sure?')) { - deleted = true; - } - }); - - deleteBtn.click(); - - expect(deleted).toBe(false); - }); - }); - - describe('DocumentControls integration', () => { - test('should integrate with DocumentControls class', () => { - if (documentControls) { - expect(typeof documentControls.create).toBe('function'); - expect(typeof documentControls.addButton).toBe('function'); - expect(typeof documentControls.setEventHandlers).toBe('function'); - } - }); - - test('should handle button events through DocumentControls', () => { - if (!documentControls) return; - - // Test that DocumentControls can manage event handlers - expect(documentControls.eventHandlers).toBeDefined(); - expect(documentControls.eventHandlers instanceof Map).toBe(true); - }); - - test('should handle button actions through event delegation', () => { - const content = document.getElementById('content'); - let actionTriggered = ''; - - content.addEventListener('click', (event) => { - if (event.target.matches('button[data-action]')) { - actionTriggered = event.target.getAttribute('data-action'); - } - }); - - const editBtn = document.querySelector('.edit-btn'); - editBtn.click(); - - expect(actionTriggered).toBe('edit'); - }); - }); -}); \ No newline at end of file +}); diff --git a/js/tests/component-integration.test.js b/js/tests/component-integration.test.js index 3393f3e..66b8440 100644 --- a/js/tests/component-integration.test.js +++ b/js/tests/component-integration.test.js @@ -83,4 +83,4 @@ This is test content. domRenderer.showEditor(mockSection.id); }).not.toThrow(); }); -}); \ No newline at end of file +}); diff --git a/js/tests/image-editing.test.js b/js/tests/image-editing.test.js index f83c52e..0b67c04 100644 --- a/js/tests/image-editing.test.js +++ b/js/tests/image-editing.test.js @@ -6,12 +6,12 @@ */ describe('Image Editing', () => { - let mockImageSection; - let mockImageElement; + let mockImageSection; + let mockImageElement; - beforeEach(() => { + beforeEach(() => { // Setup DOM with image section - document.body.innerHTML = ` + document.body.innerHTML = `
@@ -31,250 +31,250 @@ describe('Image Editing', () => {
`; - mockImageSection = document.querySelector('.image-section'); - mockImageElement = document.querySelector('.section-image'); - }); - - afterEach(() => { - document.body.innerHTML = ''; - jest.clearAllMocks(); - }); - - describe('Image editor dialog', () => { - test('should show image editor when edit button is clicked', () => { - const editButton = document.querySelector('.edit-image-btn'); - const dialog = document.querySelector('.image-editor-dialog'); - - expect(editButton).toBeTruthy(); - expect(dialog).toBeTruthy(); - - // Simulate edit button click - editButton.click(); - - // In real implementation, dialog should become visible - expect(dialog.style.display).toBe('none'); // Initially hidden + mockImageSection = document.querySelector('.image-section'); + mockImageElement = document.querySelector('.section-image'); }); - test('should populate current alt text and caption', () => { - const altTextInput = document.querySelector('.alt-text-input'); - const captionInput = document.querySelector('.image-caption'); - - expect(altTextInput).toBeTruthy(); - expect(captionInput).toBeTruthy(); - - // Simulate populating current values - const currentAlt = mockImageElement.alt; - altTextInput.value = currentAlt; - - expect(altTextInput.value).toBe(currentAlt); + afterEach(() => { + document.body.innerHTML = ''; + jest.clearAllMocks(); }); - test('should handle dialog positioning correctly', () => { - const dialog = document.querySelector('.image-editor-dialog'); + describe('Image editor dialog', () => { + test('should show image editor when edit button is clicked', () => { + const editButton = document.querySelector('.edit-image-btn'); + const dialog = document.querySelector('.image-editor-dialog'); - // Test that dialog positioning can be set - dialog.style.position = 'absolute'; - dialog.style.top = '100px'; - dialog.style.left = '100px'; + expect(editButton).toBeTruthy(); + expect(dialog).toBeTruthy(); - expect(dialog.style.position).toBe('absolute'); - expect(dialog.style.top).toBe('100px'); - expect(dialog.style.left).toBe('100px'); - }); - }); + // Simulate edit button click + editButton.click(); - describe('Image modifications', () => { - test('should update alt text when applied', () => { - const altTextInput = document.querySelector('.alt-text-input'); - const applyButton = document.querySelector('.apply-image-changes'); + // In real implementation, dialog should become visible + expect(dialog.style.display).toBe('none'); // Initially hidden + }); - const newAltText = 'Updated alt text for image'; - altTextInput.value = newAltText; + test('should populate current alt text and caption', () => { + const altTextInput = document.querySelector('.alt-text-input'); + const captionInput = document.querySelector('.image-caption'); - // Simulate apply action - applyButton.click(); + expect(altTextInput).toBeTruthy(); + expect(captionInput).toBeTruthy(); - // In real implementation, image alt text should be updated - expect(altTextInput.value).toBe(newAltText); + // Simulate populating current values + const currentAlt = mockImageElement.alt; + altTextInput.value = currentAlt; + + expect(altTextInput.value).toBe(currentAlt); + }); + + test('should handle dialog positioning correctly', () => { + const dialog = document.querySelector('.image-editor-dialog'); + + // Test that dialog positioning can be set + dialog.style.position = 'absolute'; + dialog.style.top = '100px'; + dialog.style.left = '100px'; + + expect(dialog.style.position).toBe('absolute'); + expect(dialog.style.top).toBe('100px'); + expect(dialog.style.left).toBe('100px'); + }); }); - test('should update image caption when applied', () => { - const captionInput = document.querySelector('.image-caption'); - const newCaption = 'Updated image caption'; + describe('Image modifications', () => { + test('should update alt text when applied', () => { + const altTextInput = document.querySelector('.alt-text-input'); + const applyButton = document.querySelector('.apply-image-changes'); - captionInput.value = newCaption; + const newAltText = 'Updated alt text for image'; + altTextInput.value = newAltText; - expect(captionInput.value).toBe(newCaption); + // Simulate apply action + applyButton.click(); + + // In real implementation, image alt text should be updated + expect(altTextInput.value).toBe(newAltText); + }); + + test('should update image caption when applied', () => { + const captionInput = document.querySelector('.image-caption'); + const newCaption = 'Updated image caption'; + + captionInput.value = newCaption; + + expect(captionInput.value).toBe(newCaption); + }); + + test('should validate required fields', () => { + const altTextInput = document.querySelector('.alt-text-input'); + + // Test empty alt text validation + altTextInput.value = ''; + + const isEmpty = altTextInput.value.trim() === ''; + expect(isEmpty).toBe(true); + + // Test filled alt text + altTextInput.value = 'Valid alt text'; + const isFilled = altTextInput.value.trim() !== ''; + expect(isFilled).toBe(true); + }); }); - test('should validate required fields', () => { - const altTextInput = document.querySelector('.alt-text-input'); + describe('Image reset functionality', () => { + test('should reset image to original state', () => { + const resetButton = document.querySelector('.reset-image-btn'); + const altTextInput = document.querySelector('.alt-text-input'); - // Test empty alt text validation - altTextInput.value = ''; + // Store original values + const originalAlt = mockImageElement.alt; - const isEmpty = altTextInput.value.trim() === ''; - expect(isEmpty).toBe(true); + // Modify values + altTextInput.value = 'Modified alt text'; + mockImageElement.alt = 'Modified alt'; - // Test filled alt text - altTextInput.value = 'Valid alt text'; - const isFilled = altTextInput.value.trim() !== ''; - expect(isFilled).toBe(true); - }); - }); + // Simulate reset + resetButton.click(); - describe('Image reset functionality', () => { - test('should reset image to original state', () => { - const resetButton = document.querySelector('.reset-image-btn'); - const altTextInput = document.querySelector('.alt-text-input'); + // In real implementation, should restore original values + expect(resetButton).toBeTruthy(); + }); - // Store original values - const originalAlt = mockImageElement.alt; + test('should confirm before resetting changes', () => { + const resetButton = document.querySelector('.reset-image-btn'); - // Modify values - altTextInput.value = 'Modified alt text'; - mockImageElement.alt = 'Modified alt'; + // Mock confirm dialog + window.confirm = jest.fn(() => true); - // Simulate reset - resetButton.click(); + resetButton.click(); - // In real implementation, should restore original values - expect(resetButton).toBeTruthy(); + // In real implementation, should show confirmation + expect(resetButton).toBeTruthy(); + }); + + test('should preserve original image data', () => { + // Test that original image data is stored + const originalData = { + src: mockImageElement.src, + alt: mockImageElement.alt, + caption: '' + }; + + expect(originalData.src).toBeTruthy(); + expect(typeof originalData.alt).toBe('string'); + expect(typeof originalData.caption).toBe('string'); + }); }); - test('should confirm before resetting changes', () => { - const resetButton = document.querySelector('.reset-image-btn'); + describe('Image editor UI controls', () => { + test('should handle cancel button correctly', () => { + const cancelButton = document.querySelector('.cancel-image-changes'); + const dialog = document.querySelector('.image-editor-dialog'); - // Mock confirm dialog - window.confirm = jest.fn(() => true); + cancelButton.click(); - resetButton.click(); + // In real implementation, should close dialog without saving + expect(cancelButton).toBeTruthy(); + expect(dialog).toBeTruthy(); + }); - // In real implementation, should show confirmation - expect(resetButton).toBeTruthy(); + test('should close dialog after applying changes', () => { + const applyButton = document.querySelector('.apply-image-changes'); + const dialog = document.querySelector('.image-editor-dialog'); + + applyButton.click(); + + // In real implementation, should close dialog after applying + expect(applyButton).toBeTruthy(); + expect(dialog.style.display).toBe('none'); + }); + + test('should handle escape key to cancel', () => { + const dialog = document.querySelector('.image-editor-dialog'); + const altTextInput = document.querySelector('.alt-text-input'); + + // Simulate escape key press + const escapeEvent = new KeyboardEvent('keydown', { + key: 'Escape', + bubbles: true + }); + + altTextInput.dispatchEvent(escapeEvent); + + // In real implementation, should close dialog + expect(dialog).toBeTruthy(); + }); }); - test('should preserve original image data', () => { - // Test that original image data is stored - const originalData = { - src: mockImageElement.src, - alt: mockImageElement.alt, - caption: '' - }; + describe('Advanced image editor features', () => { + test('should support image URL editing', () => { + const imageUrl = mockImageElement.src; - expect(originalData.src).toBeTruthy(); - expect(typeof originalData.alt).toBe('string'); - expect(typeof originalData.caption).toBe('string'); - }); - }); + // Test URL validation + const isValidUrl = /^https?:\/\//.test(imageUrl) || imageUrl.startsWith('/') || imageUrl.startsWith('./'); - describe('Image editor UI controls', () => { - test('should handle cancel button correctly', () => { - const cancelButton = document.querySelector('.cancel-image-changes'); - const dialog = document.querySelector('.image-editor-dialog'); + // Local files and URLs should be valid + expect(typeof imageUrl).toBe('string'); + }); - cancelButton.click(); + test('should handle image loading errors', () => { + const errorHandler = jest.fn(); - // In real implementation, should close dialog without saving - expect(cancelButton).toBeTruthy(); - expect(dialog).toBeTruthy(); + mockImageElement.onerror = errorHandler; + mockImageElement.src = 'invalid-image-url.jpg'; + + // In real implementation, should handle image load errors + expect(mockImageElement.onerror).toBe(errorHandler); + }); + + test('should support image alignment options', () => { + const alignmentOptions = ['left', 'center', 'right', 'full-width']; + + alignmentOptions.forEach(alignment => { + mockImageElement.className = `section-image align-${alignment}`; + expect(mockImageElement.className).toContain(`align-${alignment}`); + }); + }); + + test('should handle responsive image sizing', () => { + // Test responsive image attributes + mockImageElement.style.maxWidth = '100%'; + mockImageElement.style.height = 'auto'; + + expect(mockImageElement.style.maxWidth).toBe('100%'); + expect(mockImageElement.style.height).toBe('auto'); + }); }); - test('should close dialog after applying changes', () => { - const applyButton = document.querySelector('.apply-image-changes'); - const dialog = document.querySelector('.image-editor-dialog'); + describe('Image section integration', () => { + test('should maintain section integrity during image editing', () => { + const sectionId = mockImageSection.getAttribute('data-section-id'); - applyButton.click(); + expect(sectionId).toBeTruthy(); + expect(mockImageSection.classList.contains('image-section')).toBe(true); + }); - // In real implementation, should close dialog after applying - expect(applyButton).toBeTruthy(); - expect(dialog.style.display).toBe('none'); + test('should handle multiple images in one section', () => { + // Add another image to the section + const secondImage = document.createElement('img'); + secondImage.src = 'second-image.jpg'; + secondImage.alt = 'Second image'; + secondImage.className = 'section-image'; + + mockImageSection.querySelector('.section-content').appendChild(secondImage); + + const images = mockImageSection.querySelectorAll('.section-image'); + expect(images.length).toBe(2); + }); + + test('should preserve section order when editing images', () => { + const sectionContent = mockImageSection.querySelector('.section-content'); + const children = Array.from(sectionContent.children); + + const imageIndex = children.findIndex(child => child.tagName === 'IMG'); + expect(imageIndex).toBeGreaterThanOrEqual(0); + }); }); - - test('should handle escape key to cancel', () => { - const dialog = document.querySelector('.image-editor-dialog'); - const altTextInput = document.querySelector('.alt-text-input'); - - // Simulate escape key press - const escapeEvent = new KeyboardEvent('keydown', { - key: 'Escape', - bubbles: true - }); - - altTextInput.dispatchEvent(escapeEvent); - - // In real implementation, should close dialog - expect(dialog).toBeTruthy(); - }); - }); - - describe('Advanced image editor features', () => { - test('should support image URL editing', () => { - const imageUrl = mockImageElement.src; - - // Test URL validation - const isValidUrl = /^https?:\/\//.test(imageUrl) || imageUrl.startsWith('/') || imageUrl.startsWith('./'); - - // Local files and URLs should be valid - expect(typeof imageUrl).toBe('string'); - }); - - test('should handle image loading errors', () => { - const errorHandler = jest.fn(); - - mockImageElement.onerror = errorHandler; - mockImageElement.src = 'invalid-image-url.jpg'; - - // In real implementation, should handle image load errors - expect(mockImageElement.onerror).toBe(errorHandler); - }); - - test('should support image alignment options', () => { - const alignmentOptions = ['left', 'center', 'right', 'full-width']; - - alignmentOptions.forEach(alignment => { - mockImageElement.className = `section-image align-${alignment}`; - expect(mockImageElement.className).toContain(`align-${alignment}`); - }); - }); - - test('should handle responsive image sizing', () => { - // Test responsive image attributes - mockImageElement.style.maxWidth = '100%'; - mockImageElement.style.height = 'auto'; - - expect(mockImageElement.style.maxWidth).toBe('100%'); - expect(mockImageElement.style.height).toBe('auto'); - }); - }); - - describe('Image section integration', () => { - test('should maintain section integrity during image editing', () => { - const sectionId = mockImageSection.getAttribute('data-section-id'); - - expect(sectionId).toBeTruthy(); - expect(mockImageSection.classList.contains('image-section')).toBe(true); - }); - - test('should handle multiple images in one section', () => { - // Add another image to the section - const secondImage = document.createElement('img'); - secondImage.src = 'second-image.jpg'; - secondImage.alt = 'Second image'; - secondImage.className = 'section-image'; - - mockImageSection.querySelector('.section-content').appendChild(secondImage); - - const images = mockImageSection.querySelectorAll('.section-image'); - expect(images.length).toBe(2); - }); - - test('should preserve section order when editing images', () => { - const sectionContent = mockImageSection.querySelector('.section-content'); - const children = Array.from(sectionContent.children); - - const imageIndex = children.findIndex(child => child.tagName === 'IMG'); - expect(imageIndex).toBeGreaterThanOrEqual(0); - }); - }); -}); \ No newline at end of file +}); diff --git a/js/tests/jest.setup.js b/js/tests/jest.setup.js index 1c8eab0..3f83724 100644 --- a/js/tests/jest.setup.js +++ b/js/tests/jest.setup.js @@ -23,4 +23,4 @@ console.log = (...args) => { beforeEach(() => { // Reset document body for each test document.body.innerHTML = '
'; -}); \ No newline at end of file +}); diff --git a/js/tests/keyboard-shortcuts.test.js b/js/tests/keyboard-shortcuts.test.js index c2df598..3662a4e 100644 --- a/js/tests/keyboard-shortcuts.test.js +++ b/js/tests/keyboard-shortcuts.test.js @@ -6,12 +6,12 @@ */ describe('Keyboard Shortcuts', () => { - let domRenderer; - let mockTextarea; + let domRenderer; + let mockTextarea; - beforeEach(() => { + beforeEach(() => { // Setup DOM - document.body.innerHTML = ` + document.body.innerHTML = `
@@ -19,201 +19,201 @@ describe('Keyboard Shortcuts', () => {
`; - // Load components - require('../components/dom-renderer.js'); - require('../core/section-manager.js'); + // Load components + require('../components/dom-renderer.js'); + require('../core/section-manager.js'); - // Mock SectionManager with event system - const mockSectionManager = { - on: jest.fn(), - emit: jest.fn(), - handleSectionSplit: jest.fn(), - sections: [] - }; - - if (global.DOMRenderer) { - // Create DOMRenderer with mocked dependencies - try { - domRenderer = new global.DOMRenderer(mockSectionManager, document.getElementById('content')); - } catch (error) { - // If constructor fails, create a mock with the methods we need - domRenderer = { - applyChanges: jest.fn(), - cancelEdit: jest.fn() + // Mock SectionManager with event system + const mockSectionManager = { + on: jest.fn(), + emit: jest.fn(), + handleSectionSplit: jest.fn(), + sections: [] }; - } - } - mockTextarea = document.querySelector('.edit-textarea'); - }); - - afterEach(() => { - document.body.innerHTML = ''; - jest.clearAllMocks(); - }); - - describe('Ctrl+Enter shortcut (Accept Changes)', () => { - test('should apply changes when Ctrl+Enter is pressed', () => { - if (!mockTextarea) { - console.warn('Textarea not available, skipping test'); - return; - } - - // Test that Ctrl+Enter event can be dispatched - const ctrlEnterEvent = new KeyboardEvent('keydown', { - key: 'Enter', - ctrlKey: true, - bubbles: true - }); - - let eventFired = false; - mockTextarea.addEventListener('keydown', (e) => { - if (e.ctrlKey && e.key === 'Enter') { - eventFired = true; + if (global.DOMRenderer) { + // Create DOMRenderer with mocked dependencies + try { + domRenderer = new global.DOMRenderer(mockSectionManager, document.getElementById('content')); + } catch (error) { + // If constructor fails, create a mock with the methods we need + domRenderer = { + applyChanges: jest.fn(), + cancelEdit: jest.fn() + }; + } } - }); - mockTextarea.dispatchEvent(ctrlEnterEvent); - - // Verify event was handled - expect(eventFired).toBe(true); + mockTextarea = document.querySelector('.edit-textarea'); }); - test('should prevent default behavior on Ctrl+Enter', () => { - if (!mockTextarea) return; - - const preventDefault = jest.fn(); - const ctrlEnterEvent = new KeyboardEvent('keydown', { - key: 'Enter', - ctrlKey: true, - bubbles: true - }); - - // Mock preventDefault - ctrlEnterEvent.preventDefault = preventDefault; - - mockTextarea.dispatchEvent(ctrlEnterEvent); - - // Note: In real implementation, preventDefault should be called - // This test documents the expected behavior - expect(true).toBe(true); // Placeholder for actual implementation check - }); - }); - - describe('Escape shortcut (Cancel Changes)', () => { - test('should cancel changes when Escape is pressed', () => { - if (!mockTextarea) { - console.warn('Textarea not available, skipping test'); - return; - } - - // Test that Escape event can be dispatched - const escapeEvent = new KeyboardEvent('keydown', { - key: 'Escape', - bubbles: true - }); - - let escapePressed = false; - mockTextarea.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - escapePressed = true; - } - }); - - mockTextarea.dispatchEvent(escapeEvent); - - // Verify escape was detected - expect(escapePressed).toBe(true); + afterEach(() => { + document.body.innerHTML = ''; + jest.clearAllMocks(); }); - test('should restore original content on Escape', () => { - if (!mockTextarea) return; + describe('Ctrl+Enter shortcut (Accept Changes)', () => { + test('should apply changes when Ctrl+Enter is pressed', () => { + if (!mockTextarea) { + console.warn('Textarea not available, skipping test'); + return; + } - const originalContent = 'Original content'; - mockTextarea.setAttribute('data-original-content', originalContent); - mockTextarea.value = 'Modified content'; + // Test that Ctrl+Enter event can be dispatched + const ctrlEnterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + ctrlKey: true, + bubbles: true + }); - const escapeEvent = new KeyboardEvent('keydown', { - key: 'Escape', - bubbles: true - }); + let eventFired = false; + mockTextarea.addEventListener('keydown', (e) => { + if (e.ctrlKey && e.key === 'Enter') { + eventFired = true; + } + }); - mockTextarea.dispatchEvent(escapeEvent); + mockTextarea.dispatchEvent(ctrlEnterEvent); - // In real implementation, content should be restored - // This test documents the expected behavior - expect(mockTextarea.getAttribute('data-original-content')).toBe(originalContent); - }); - }); - - describe('Keyboard shortcuts integration', () => { - test('should bind keyboard handlers to textareas', () => { - const textarea = document.createElement('textarea'); - textarea.className = 'edit-textarea'; - document.body.appendChild(textarea); - - // Check if event listeners can be added (integration test) - let listenerAdded = false; - const originalAddEventListener = textarea.addEventListener; - textarea.addEventListener = jest.fn((event, handler) => { - if (event === 'keydown') { - listenerAdded = true; - } - return originalAddEventListener.call(textarea, event, handler); - }); - - // In real implementation, DOMRenderer should bind keydown listeners - // This test ensures the capability exists - expect(textarea.addEventListener).toBeDefined(); - expect(typeof textarea.addEventListener).toBe('function'); - }); - - test('should handle multiple keyboard events correctly', () => { - if (!mockTextarea) return; - - const events = [ - { key: 'Enter', ctrlKey: true }, - { key: 'Escape', ctrlKey: false }, - { key: 'Tab', ctrlKey: false } - ]; - - events.forEach(eventData => { - const event = new KeyboardEvent('keydown', { - ...eventData, - bubbles: true + // Verify event was handled + expect(eventFired).toBe(true); }); - // Should not throw errors when handling various key events - expect(() => { - mockTextarea.dispatchEvent(event); - }).not.toThrow(); - }); - }); - }); + test('should prevent default behavior on Ctrl+Enter', () => { + if (!mockTextarea) return; - describe('Keyboard shortcuts accessibility', () => { - test('should provide keyboard alternatives to mouse actions', () => { - // This test ensures keyboard accessibility is maintained - const shortcuts = [ - { key: 'Enter', ctrlKey: true, action: 'apply' }, - { key: 'Escape', ctrlKey: false, action: 'cancel' } - ]; + const preventDefault = jest.fn(); + const ctrlEnterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + ctrlKey: true, + bubbles: true + }); - shortcuts.forEach(shortcut => { - expect(shortcut.key).toBeDefined(); - expect(shortcut.action).toBeDefined(); - }); + // Mock preventDefault + ctrlEnterEvent.preventDefault = preventDefault; + + mockTextarea.dispatchEvent(ctrlEnterEvent); + + // Note: In real implementation, preventDefault should be called + // This test documents the expected behavior + expect(true).toBe(true); // Placeholder for actual implementation check + }); }); - test('should work with screen readers and assistive technology', () => { - if (!mockTextarea) return; + describe('Escape shortcut (Cancel Changes)', () => { + test('should cancel changes when Escape is pressed', () => { + if (!mockTextarea) { + console.warn('Textarea not available, skipping test'); + return; + } - // Test ARIA attributes and accessibility features - mockTextarea.setAttribute('aria-label', 'Edit section content'); - mockTextarea.setAttribute('role', 'textbox'); + // Test that Escape event can be dispatched + const escapeEvent = new KeyboardEvent('keydown', { + key: 'Escape', + bubbles: true + }); - expect(mockTextarea.getAttribute('aria-label')).toBeTruthy(); - expect(mockTextarea.getAttribute('role')).toBe('textbox'); + let escapePressed = false; + mockTextarea.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + escapePressed = true; + } + }); + + mockTextarea.dispatchEvent(escapeEvent); + + // Verify escape was detected + expect(escapePressed).toBe(true); + }); + + test('should restore original content on Escape', () => { + if (!mockTextarea) return; + + const originalContent = 'Original content'; + mockTextarea.setAttribute('data-original-content', originalContent); + mockTextarea.value = 'Modified content'; + + const escapeEvent = new KeyboardEvent('keydown', { + key: 'Escape', + bubbles: true + }); + + mockTextarea.dispatchEvent(escapeEvent); + + // In real implementation, content should be restored + // This test documents the expected behavior + expect(mockTextarea.getAttribute('data-original-content')).toBe(originalContent); + }); }); - }); -}); \ No newline at end of file + + describe('Keyboard shortcuts integration', () => { + test('should bind keyboard handlers to textareas', () => { + const textarea = document.createElement('textarea'); + textarea.className = 'edit-textarea'; + document.body.appendChild(textarea); + + // Check if event listeners can be added (integration test) + let listenerAdded = false; + const originalAddEventListener = textarea.addEventListener; + textarea.addEventListener = jest.fn((event, handler) => { + if (event === 'keydown') { + listenerAdded = true; + } + return originalAddEventListener.call(textarea, event, handler); + }); + + // In real implementation, DOMRenderer should bind keydown listeners + // This test ensures the capability exists + expect(textarea.addEventListener).toBeDefined(); + expect(typeof textarea.addEventListener).toBe('function'); + }); + + test('should handle multiple keyboard events correctly', () => { + if (!mockTextarea) return; + + const events = [ + { key: 'Enter', ctrlKey: true }, + { key: 'Escape', ctrlKey: false }, + { key: 'Tab', ctrlKey: false } + ]; + + events.forEach(eventData => { + const event = new KeyboardEvent('keydown', { + ...eventData, + bubbles: true + }); + + // Should not throw errors when handling various key events + expect(() => { + mockTextarea.dispatchEvent(event); + }).not.toThrow(); + }); + }); + }); + + describe('Keyboard shortcuts accessibility', () => { + test('should provide keyboard alternatives to mouse actions', () => { + // This test ensures keyboard accessibility is maintained + const shortcuts = [ + { key: 'Enter', ctrlKey: true, action: 'apply' }, + { key: 'Escape', ctrlKey: false, action: 'cancel' } + ]; + + shortcuts.forEach(shortcut => { + expect(shortcut.key).toBeDefined(); + expect(shortcut.action).toBeDefined(); + }); + }); + + test('should work with screen readers and assistive technology', () => { + if (!mockTextarea) return; + + // Test ARIA attributes and accessibility features + mockTextarea.setAttribute('aria-label', 'Edit section content'); + mockTextarea.setAttribute('role', 'textbox'); + + expect(mockTextarea.getAttribute('aria-label')).toBeTruthy(); + expect(mockTextarea.getAttribute('role')).toBe('textbox'); + }); + }); +}); diff --git a/js/tests/refactor-test-runner.js b/js/tests/refactor-test-runner.js index ecc9752..26d8bd7 100644 --- a/js/tests/refactor-test-runner.js +++ b/js/tests/refactor-test-runner.js @@ -189,7 +189,7 @@ class RefactorTestRunner { const endTime = Date.now(); const duration = endTime - startTime; - console.log(`\nšŸ“Š Test Results:`); + console.log('\nšŸ“Š Test Results:'); console.log(` āœ… Passed: ${this.passed}`); console.log(` āŒ Failed: ${this.failed}`); console.log(` ā±ļø Duration: ${duration}ms`); @@ -198,7 +198,7 @@ class RefactorTestRunner { console.log(`\nāŒ ${this.failed} test(s) failed. Refactoring should not proceed.`); process.exit(1); } else { - console.log(`\nāœ… All tests passed! Refactoring is safe to continue.`); + console.log('\nāœ… All tests passed! Refactoring is safe to continue.'); } } } @@ -213,4 +213,4 @@ if (typeof window !== 'undefined') { window.RefactorTestRunner = RefactorTestRunner; } -module.exports = RefactorTestRunner; \ No newline at end of file +module.exports = RefactorTestRunner; diff --git a/js/tests/section-splitting.test.js b/js/tests/section-splitting.test.js index 993a847..c9fadf6 100644 --- a/js/tests/section-splitting.test.js +++ b/js/tests/section-splitting.test.js @@ -6,11 +6,11 @@ */ describe('Section Splitting', () => { - let sectionManager; + let sectionManager; - beforeEach(() => { + beforeEach(() => { // Setup DOM - document.body.innerHTML = ` + document.body.innerHTML = `
@@ -20,22 +20,22 @@ describe('Section Splitting', () => {
`; - // Load components - require('../core/section-manager.js'); + // Load components + require('../core/section-manager.js'); - if (global.SectionManager) { - sectionManager = new global.SectionManager(); - } - }); + if (global.SectionManager) { + sectionManager = new global.SectionManager(); + } + }); - afterEach(() => { - document.body.innerHTML = ''; - jest.clearAllMocks(); - }); + afterEach(() => { + document.body.innerHTML = ''; + jest.clearAllMocks(); + }); - describe('Heading detection', () => { - test('should detect new headings in content', () => { - const textWithHeading = ` + describe('Heading detection', () => { + test('should detect new headings in content', () => { + const textWithHeading = ` This is some content. # New Heading @@ -43,49 +43,49 @@ describe('Section Splitting', () => { This should be a new section. `; - // Test heading detection with regex - const lines = textWithHeading.trim().split('\n'); - const headingLine = lines.find(line => /^#+ /.test(line.trim())); - expect(headingLine).toBeTruthy(); - expect(headingLine.trim()).toBe('# New Heading'); + // Test heading detection with regex + const lines = textWithHeading.trim().split('\n'); + const headingLine = lines.find(line => /^#+ /.test(line.trim())); + expect(headingLine).toBeTruthy(); + expect(headingLine.trim()).toBe('# New Heading'); + }); + + test('should identify different heading levels', () => { + const headingTests = [ + { text: '# Heading 1', level: 1 }, + { text: '## Heading 2', level: 2 }, + { text: '### Heading 3', level: 3 }, + { text: '#### Heading 4', level: 4 } + ]; + + headingTests.forEach(({ text, level }) => { + const match = text.match(/^(#+) /); + expect(match).toBeTruthy(); + if (match) { + expect(match[1].length).toBe(level); + } + }); + }); + + test('should distinguish headings from regular text', () => { + const testCases = [ + { text: '# This is a heading', isHeading: true }, + { text: 'This is not a heading', isHeading: false }, + { text: 'Neither is this # hash in middle', isHeading: false }, + { text: '## Another heading', isHeading: true } + ]; + + testCases.forEach(({ text, isHeading }) => { + const match = /^#+\s/.test(text.trim()); + expect(match).toBe(isHeading); + }); + }); }); - test('should identify different heading levels', () => { - const headingTests = [ - { text: '# Heading 1', level: 1 }, - { text: '## Heading 2', level: 2 }, - { text: '### Heading 3', level: 3 }, - { text: '#### Heading 4', level: 4 } - ]; - - headingTests.forEach(({ text, level }) => { - const match = text.match(/^(#+) /); - expect(match).toBeTruthy(); - if (match) { - expect(match[1].length).toBe(level); - } - }); - }); - - test('should distinguish headings from regular text', () => { - const testCases = [ - { text: '# This is a heading', isHeading: true }, - { text: 'This is not a heading', isHeading: false }, - { text: 'Neither is this # hash in middle', isHeading: false }, - { text: '## Another heading', isHeading: true } - ]; - - testCases.forEach(({ text, isHeading }) => { - const match = /^#+\s/.test(text.trim()); - expect(match).toBe(isHeading); - }); - }); - }); - - describe('Section splitting logic', () => { - test('should split content when heading is detected', () => { - const originalContent = 'Original content without headings'; - const newContent = ` + describe('Section splitting logic', () => { + test('should split content when heading is detected', () => { + const originalContent = 'Original content without headings'; + const newContent = ` ${originalContent} # New Section @@ -93,26 +93,26 @@ describe('Section Splitting', () => { New section content `; - // Simulate section splitting logic - const parts = newContent.split(/\n(?=#)/); + // Simulate section splitting logic + const parts = newContent.split(/\n(?=#)/); - if (parts.length > 1) { - expect(parts.length).toBeGreaterThan(1); - expect(parts[0]).toContain('Original content'); - expect(parts[1]).toContain('# New Section'); - } - }); + if (parts.length > 1) { + expect(parts.length).toBeGreaterThan(1); + expect(parts[0]).toContain('Original content'); + expect(parts[1]).toContain('# New Section'); + } + }); - test('should preserve content when no headings are present', () => { - const content = 'Just regular content without any headings'; - const parts = content.split(/\n(?=#)/); + test('should preserve content when no headings are present', () => { + const content = 'Just regular content without any headings'; + const parts = content.split(/\n(?=#)/); - expect(parts.length).toBe(1); - expect(parts[0]).toBe(content); - }); + expect(parts.length).toBe(1); + expect(parts[0]).toBe(content); + }); - test('should handle multiple headings correctly', () => { - const contentWithMultipleHeadings = `Initial content + test('should handle multiple headings correctly', () => { + const contentWithMultipleHeadings = `Initial content # First Heading First section content @@ -123,48 +123,48 @@ Second section content # Third Heading Third section content`; - // Split on lines that start with headings - const parts = contentWithMultipleHeadings.split(/\n(?=#)/); + // Split on lines that start with headings + const parts = contentWithMultipleHeadings.split(/\n(?=#)/); - // Should split into multiple sections - expect(parts.length).toBeGreaterThanOrEqual(2); + // Should split into multiple sections + expect(parts.length).toBeGreaterThanOrEqual(2); - // Find heading lines - const headings = contentWithMultipleHeadings.match(/^#+.*$/gm); - expect(headings).toBeTruthy(); - expect(headings.length).toBe(3); - }); - }); - - describe('SectionManager integration', () => { - test('should have handleSectionSplit method', () => { - if (!sectionManager) { - console.warn('SectionManager not available, skipping test'); - return; - } - - expect(typeof sectionManager.handleSectionSplit).toBe('function'); + // Find heading lines + const headings = contentWithMultipleHeadings.match(/^#+.*$/gm); + expect(headings).toBeTruthy(); + expect(headings.length).toBe(3); + }); }); - test('should maintain section state during splits', () => { - if (!sectionManager) return; + describe('SectionManager integration', () => { + test('should have handleSectionSplit method', () => { + if (!sectionManager) { + console.warn('SectionManager not available, skipping test'); + return; + } - const originalSectionCount = document.querySelectorAll('.section').length; + expect(typeof sectionManager.handleSectionSplit).toBe('function'); + }); - // Mock section splitting - const mockNewSection = document.createElement('div'); - mockNewSection.className = 'section'; - mockNewSection.setAttribute('data-section-id', 'split-section'); + test('should maintain section state during splits', () => { + if (!sectionManager) return; - if (originalSectionCount > 0) { - expect(originalSectionCount).toBeGreaterThan(0); - } + const originalSectionCount = document.querySelectorAll('.section').length; + + // Mock section splitting + const mockNewSection = document.createElement('div'); + mockNewSection.className = 'section'; + mockNewSection.setAttribute('data-section-id', 'split-section'); + + if (originalSectionCount > 0) { + expect(originalSectionCount).toBeGreaterThan(0); + } + }); }); - }); - describe('Dynamic section creation', () => { - test('should create new section elements when splitting', () => { - const sectionContent = ` + describe('Dynamic section creation', () => { + test('should create new section elements when splitting', () => { + const sectionContent = ` Original content # New Section Title @@ -172,36 +172,36 @@ Third section content`; New section content `; - // Simulate section creation - const newSection = document.createElement('div'); - newSection.className = 'section'; - newSection.setAttribute('data-section-id', 'generated-section-id'); + // Simulate section creation + const newSection = document.createElement('div'); + newSection.className = 'section'; + newSection.setAttribute('data-section-id', 'generated-section-id'); - const contentDiv = document.createElement('div'); - contentDiv.className = 'section-content'; - contentDiv.textContent = 'New section content'; + const contentDiv = document.createElement('div'); + contentDiv.className = 'section-content'; + contentDiv.textContent = 'New section content'; - newSection.appendChild(contentDiv); + newSection.appendChild(contentDiv); - expect(newSection.className).toBe('section'); - expect(newSection.getAttribute('data-section-id')).toBeTruthy(); - expect(newSection.querySelector('.section-content')).toBeTruthy(); - }); + expect(newSection.className).toBe('section'); + expect(newSection.getAttribute('data-section-id')).toBeTruthy(); + expect(newSection.querySelector('.section-content')).toBeTruthy(); + }); - test('should generate unique section IDs', () => { - const headingText = 'My New Section'; + test('should generate unique section IDs', () => { + const headingText = 'My New Section'; - // Simulate ID generation from heading - const sectionId = headingText - .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + // Simulate ID generation from heading + const sectionId = headingText + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); - expect(sectionId).toBe('my-new-section'); - }); + expect(sectionId).toBe('my-new-section'); + }); - test('should preserve section hierarchy', () => { - const hierarchicalContent = ` + test('should preserve section hierarchy', () => { + const hierarchicalContent = ` # Main Section Main content @@ -212,20 +212,20 @@ Third section content`; Sub-sub content `; - const headings = hierarchicalContent.match(/^#+.*$/gm); + const headings = hierarchicalContent.match(/^#+.*$/gm); - if (headings) { - expect(headings.length).toBe(3); - expect(headings[0]).toMatch(/^# /); - expect(headings[1]).toMatch(/^## /); - expect(headings[2]).toMatch(/^### /); - } + if (headings) { + expect(headings.length).toBe(3); + expect(headings[0]).toMatch(/^# /); + expect(headings[1]).toMatch(/^## /); + expect(headings[2]).toMatch(/^### /); + } + }); }); - }); - describe('Section splitting edge cases', () => { - test('should handle empty headings gracefully', () => { - const contentWithEmptyHeading = ` + describe('Section splitting edge cases', () => { + test('should handle empty headings gracefully', () => { + const contentWithEmptyHeading = ` Content before # @@ -233,35 +233,35 @@ Third section content`; Content after `; - const parts = contentWithEmptyHeading.split(/\n(?=#)/); - expect(parts.length).toBeGreaterThanOrEqual(1); - }); + const parts = contentWithEmptyHeading.split(/\n(?=#)/); + expect(parts.length).toBeGreaterThanOrEqual(1); + }); - test('should handle headings at the start of content', () => { - const contentStartingWithHeading = `# First Heading + test('should handle headings at the start of content', () => { + const contentStartingWithHeading = `# First Heading Content for first section # Second Heading Content for second section `; - const parts = contentStartingWithHeading.split(/\n(?=#)/); - expect(parts[0]).toContain('# First Heading'); - }); + const parts = contentStartingWithHeading.split(/\n(?=#)/); + expect(parts[0]).toContain('# First Heading'); + }); - test('should handle malformed headings', () => { - const malformedHeadings = [ - '#NoSpace', - '# ', - '########## Too many hashes', - 'Not a heading # at all' - ]; + test('should handle malformed headings', () => { + const malformedHeadings = [ + '#NoSpace', + '# ', + '########## Too many hashes', + 'Not a heading # at all' + ]; - malformedHeadings.forEach(text => { - const isValidHeading = /^#{1,6}\s+\S/.test(text); - // Most should be invalid except properly formatted ones - expect(typeof isValidHeading).toBe('boolean'); - }); + malformedHeadings.forEach(text => { + const isValidHeading = /^#{1,6}\s+\S/.test(text); + // Most should be invalid except properly formatted ones + expect(typeof isValidHeading).toBe('boolean'); + }); + }); }); - }); -}); \ No newline at end of file +}); diff --git a/js/tests/setup.js b/js/tests/setup.js index 8f94a2b..3e06bcd 100644 --- a/js/tests/setup.js +++ b/js/tests/setup.js @@ -7,133 +7,133 @@ // Mock DOM globals that might be missing in JSDOM global.ResizeObserver = jest.fn().mockImplementation(() => ({ - observe: jest.fn(), - unobserve: jest.fn(), - disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn() })); global.IntersectionObserver = jest.fn().mockImplementation(() => ({ - observe: jest.fn(), - unobserve: jest.fn(), - disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn() })); // Mock window.matchMedia Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // deprecated - removeListener: jest.fn(), // deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) }); // Mock local storage const localStorageMock = { - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), - clear: jest.fn(), + getItem: jest.fn(), + setItem: jest.fn(), + removeItem: jest.fn(), + clear: jest.fn() }; global.localStorage = localStorageMock; // Mock session storage const sessionStorageMock = { - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), - clear: jest.fn(), + getItem: jest.fn(), + setItem: jest.fn(), + removeItem: jest.fn(), + clear: jest.fn() }; global.sessionStorage = sessionStorageMock; // Global test utilities global.testUtils = { - /** + /** * Create a mock DOM element with specified tag and attributes */ - createElement: (tag, attributes = {}) => { - const element = document.createElement(tag); - Object.entries(attributes).forEach(([key, value]) => { - element.setAttribute(key, value); - }); - return element; - }, + createElement: (tag, attributes = {}) => { + const element = document.createElement(tag); + Object.entries(attributes).forEach(([key, value]) => { + element.setAttribute(key, value); + }); + return element; + }, - /** + /** * Create a test markdown content div */ - createMarkdownContent: (content = '# Test Content') => { - const div = document.createElement('div'); - div.id = 'markdown-content'; - div.innerHTML = content; - return div; - }, + createMarkdownContent: (content = '# Test Content') => { + const div = document.createElement('div'); + div.id = 'markdown-content'; + div.innerHTML = content; + return div; + }, - /** + /** * Wait for next tick (useful for async operations) */ - nextTick: () => new Promise(resolve => setTimeout(resolve, 0)), + nextTick: () => new Promise(resolve => setTimeout(resolve, 0)), - /** + /** * Simulate user interaction events */ - simulateEvent: (element, eventType, eventProperties = {}) => { - const event = new Event(eventType, { bubbles: true, ...eventProperties }); - Object.entries(eventProperties).forEach(([key, value]) => { - event[key] = value; - }); - element.dispatchEvent(event); - return event; - }, + simulateEvent: (element, eventType, eventProperties = {}) => { + const event = new Event(eventType, { bubbles: true, ...eventProperties }); + Object.entries(eventProperties).forEach(([key, value]) => { + event[key] = value; + }); + element.dispatchEvent(event); + return event; + }, - /** + /** * Clean up DOM after each test */ - cleanupDOM: () => { - document.body.innerHTML = ''; - document.head.innerHTML = ''; - } + cleanupDOM: () => { + document.body.innerHTML = ''; + document.head.innerHTML = ''; + } }; // Setup and teardown beforeEach(() => { - // Reset mocks - jest.clearAllMocks(); + // Reset mocks + jest.clearAllMocks(); - // Reset localStorage/sessionStorage - localStorageMock.getItem.mockClear(); - localStorageMock.setItem.mockClear(); - localStorageMock.removeItem.mockClear(); - localStorageMock.clear.mockClear(); + // Reset localStorage/sessionStorage + localStorageMock.getItem.mockClear(); + localStorageMock.setItem.mockClear(); + localStorageMock.removeItem.mockClear(); + localStorageMock.clear.mockClear(); - sessionStorageMock.getItem.mockClear(); - sessionStorageMock.setItem.mockClear(); - sessionStorageMock.removeItem.mockClear(); - sessionStorageMock.clear.mockClear(); + sessionStorageMock.getItem.mockClear(); + sessionStorageMock.setItem.mockClear(); + sessionStorageMock.removeItem.mockClear(); + sessionStorageMock.clear.mockClear(); }); afterEach(() => { - // Clean up DOM - global.testUtils.cleanupDOM(); + // Clean up DOM + global.testUtils.cleanupDOM(); - // Clean up any timers - jest.runOnlyPendingTimers(); - jest.useRealTimers(); + // Clean up any timers + jest.runOnlyPendingTimers(); + jest.useRealTimers(); }); // Console helpers for test debugging global.console = { - ...console, - // Keep these methods for test debugging - log: console.log, - warn: console.warn, - error: console.error, - // Mock these to avoid noise in tests - info: jest.fn(), - debug: jest.fn(), -}; \ No newline at end of file + ...console, + // Keep these methods for test debugging + log: console.log, + warn: console.warn, + error: console.error, + // Mock these to avoid noise in tests + info: jest.fn(), + debug: jest.fn() +}; diff --git a/js/tests/test-component-integration.js b/js/tests/test-component-integration.js index 2107dc9..c8d2bb1 100644 --- a/js/tests/test-component-integration.js +++ b/js/tests/test-component-integration.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('Component Integration Tests', () => { - runner.it('should load all extracted components', () => { try { // Load extracted components @@ -30,7 +29,6 @@ runner.describe('Component Integration Tests', () => { global.ExtractedDOMRenderer = domModule.DOMRenderer; global.ExtractedFloatingMenu = domModule.FloatingMenu; global.ExtractedEditState = sectionModule.EditState; - } catch (error) { throw new Error(`Failed to load extracted components: ${error.message}`); } @@ -62,7 +60,6 @@ Content for second subsection.`; const sections = sectionManager.createSectionsFromMarkdown(testMarkdown); - // Verify sections were created // Expected: heading+paragraph, heading+paragraph, image, heading+paragraph = 4 sections runner.expect(sections.length).toBe(4); @@ -297,12 +294,11 @@ console.log('test'); const sections = sectionManager.createSectionsFromMarkdown(testMarkdown); - // Verify type detection - adjusted for actual parsing behavior // Expected: heading+paragraph, image, code = 3 sections runner.expect(sections[0].type).toBe('heading'); // Combined heading+paragraph - runner.expect(sections[1].type).toBe('image'); // Image section - runner.expect(sections[2].type).toBe('code'); // Code section + runner.expect(sections[1].type).toBe('image'); // Image section + runner.expect(sections[2].type).toBe('code'); // Code section // Verify image detection runner.expect(sections[1].isImage()).toBeTruthy(); // Image is now at index 1 @@ -518,4 +514,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… Component integration tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-debugpanel-extraction.js b/js/tests/test-debugpanel-extraction.js index 5dca6ca..28beeaa 100644 --- a/js/tests/test-debugpanel-extraction.js +++ b/js/tests/test-debugpanel-extraction.js @@ -25,7 +25,6 @@ const EXPECTED_DEBUGPANEL_API = [ ]; runner.describe('DebugPanel Component Extraction', () => { - runner.it('should define expected API methods', () => { const expectedMethods = EXPECTED_DEBUGPANEL_API; runner.expect(expectedMethods.length).toBe(9); @@ -188,4 +187,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… DebugPanel extraction tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-debugpanel-integration.js b/js/tests/test-debugpanel-integration.js index af03ff8..07e7d02 100644 --- a/js/tests/test-debugpanel-integration.js +++ b/js/tests/test-debugpanel-integration.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('DebugPanel Integration Tests', () => { - runner.it('should load all extracted components including DebugPanel', () => { try { // Load extracted components @@ -28,7 +27,6 @@ runner.describe('DebugPanel Integration Tests', () => { global.ExtractedSectionManager = sectionModule.SectionManager; global.ExtractedDOMRenderer = domModule.DOMRenderer; global.ExtractedDebugPanel = debugModule.DebugPanel; - } catch (error) { throw new Error(`Failed to load extracted components: ${error.message}`); } @@ -207,4 +205,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… DebugPanel integration tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-document-navigator.js b/js/tests/test-document-navigator.js index e6a79f9..306e352 100644 --- a/js/tests/test-document-navigator.js +++ b/js/tests/test-document-navigator.js @@ -84,13 +84,13 @@ class DocumentNavigatorTestRunner { } printSummary() { - console.log(`\nšŸ“Š Test Results:`); + console.log('\nšŸ“Š Test Results:'); console.log(` Passed: ${this.results.passed}`); console.log(` Failed: ${this.results.failed}`); console.log(` Total: ${this.results.total}`); if (this.results.failed === 0) { - console.log(`\nšŸŽ‰ All tests passed!`); + console.log('\nšŸŽ‰ All tests passed!'); } else { console.log(`\nāŒ ${this.results.failed} test(s) failed.`); } @@ -429,4 +429,4 @@ window.runDocumentNavigatorTests = () => runner.run(); console.log('šŸ“‹ DocumentNavigator TDD Test Suite loaded. Run with: runDocumentNavigatorTests()'); -export { runner }; \ No newline at end of file +export { runner }; diff --git a/js/tests/test-documentcontrols-extraction.js b/js/tests/test-documentcontrols-extraction.js index 2d5607c..6a938ff 100644 --- a/js/tests/test-documentcontrols-extraction.js +++ b/js/tests/test-documentcontrols-extraction.js @@ -26,7 +26,6 @@ const EXPECTED_DOCUMENTCONTROLS_API = [ ]; runner.describe('DocumentControls Component Extraction', () => { - runner.it('should define expected API methods', () => { const expectedMethods = EXPECTED_DOCUMENTCONTROLS_API; runner.expect(expectedMethods.length).toBe(10); @@ -215,4 +214,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… DocumentControls extraction tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-domrenderer-extraction.js b/js/tests/test-domrenderer-extraction.js index e8aadc0..c91acf1 100644 --- a/js/tests/test-domrenderer-extraction.js +++ b/js/tests/test-domrenderer-extraction.js @@ -28,7 +28,6 @@ const EXPECTED_DOMRENDERER_API = [ ]; runner.describe('DOMRenderer Component Extraction', () => { - runner.it('should define expected API methods', () => { const expectedMethods = EXPECTED_DOMRENDERER_API; runner.expect(expectedMethods.length).toBe(11); @@ -42,7 +41,7 @@ runner.describe('DOMRenderer Component Extraction', () => { delete require.cache[require.resolve('/home/worsch/markitect_project/markitect/static/editor.js')]; try { - const editorModule = require('/home/worsch/markitect_project/markitect/static/editor.js'); + const editorModule = require('../../../../../../../../home/worsch/markitect_project/markitect/static/editor.js'); runner.expect(editorModule.DOMRenderer).toBeTruthy(); // Set global for other tests global.DOMRenderer = editorModule.DOMRenderer; @@ -209,4 +208,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… DOMRenderer extraction tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-environment.test.js b/js/tests/test-environment.test.js index c35e13b..523069b 100644 --- a/js/tests/test-environment.test.js +++ b/js/tests/test-environment.test.js @@ -21,4 +21,4 @@ describe('Test Environment', () => { expect(contentEl).toBeDefined(); expect(contentEl.tagName).toBe('DIV'); }); -}); \ No newline at end of file +}); diff --git a/js/tests/test-extracted-domrenderer.js b/js/tests/test-extracted-domrenderer.js index d0a8990..c4b3968 100644 --- a/js/tests/test-extracted-domrenderer.js +++ b/js/tests/test-extracted-domrenderer.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('Extracted DOMRenderer Component', () => { - runner.it('should load extracted DOMRenderer component', () => { // Load the extracted component delete require.cache[require.resolve('../components/dom-renderer.js')]; @@ -214,7 +213,7 @@ runner.describe('Extracted DOMRenderer Component', () => { // Comparative test - verify extracted component behaves similarly to original runner.it('should behave similarly to original monolithic component', () => { // Load both components - const originalModule = require('/home/worsch/markitect_project/markitect/static/editor.js'); + const originalModule = require('../../../../../../../../home/worsch/markitect_project/markitect/static/editor.js'); const extractedModule = require('../components/dom-renderer.js'); const sectionModule = require('../core/section-manager.js'); @@ -268,4 +267,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… Extracted DOMRenderer tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-extracted-section-manager.js b/js/tests/test-extracted-section-manager.js index 0eb51d0..7365c22 100644 --- a/js/tests/test-extracted-section-manager.js +++ b/js/tests/test-extracted-section-manager.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('Extracted SectionManager Component', () => { - runner.it('should load extracted SectionManager component', () => { // Load the extracted component delete require.cache[require.resolve('../core/section-manager.js')]; @@ -47,7 +46,7 @@ runner.describe('Extracted SectionManager Component', () => { const SectionManager = global.ExtractedSectionManager; const manager = new SectionManager(); - const testMarkdown = `# Heading 1\nContent 1\n\n## Heading 2\nContent 2`; + const testMarkdown = '# Heading 1\nContent 1\n\n## Heading 2\nContent 2'; const sections = manager.createSectionsFromMarkdown(testMarkdown); runner.expect(Array.isArray(sections)).toBeTruthy(); @@ -170,7 +169,7 @@ runner.describe('Extracted SectionManager Component', () => { // Comparative test - verify extracted component behaves identically to original runner.it('should behave identically to original monolithic component', () => { // Load both components - const originalModule = require('/home/worsch/markitect_project/markitect/static/editor.js'); + const originalModule = require('../../../../../../../../home/worsch/markitect_project/markitect/static/editor.js'); const extractedModule = require('../core/section-manager.js'); const originalManager = new originalModule.SectionManager(); @@ -223,4 +222,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… Extracted SectionManager tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-full-integration.js b/js/tests/test-full-integration.js index 3edb0ce..7a3fe58 100644 --- a/js/tests/test-full-integration.js +++ b/js/tests/test-full-integration.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('Full Component Integration Tests', () => { - runner.it('should load all extracted components', () => { try { // Load all extracted components @@ -31,7 +30,6 @@ runner.describe('Full Component Integration Tests', () => { global.ExtractedDOMRenderer = domModule.DOMRenderer; global.ExtractedDebugPanel = debugModule.DebugPanel; global.ExtractedDocumentControls = controlsModule.DocumentControls; - } catch (error) { throw new Error(`Failed to load extracted components: ${error.message}`); } @@ -163,7 +161,7 @@ More detailed content here.`; documentControls.create(); // Setup comprehensive event handling - let eventLog = []; + const eventLog = []; sectionManager.on('sections-created', (data) => { eventLog.push(`sections-created: ${data.count} sections`); @@ -302,4 +300,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… Full integration tests completed'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-real-user-functionality.js b/js/tests/test-real-user-functionality.js index 3d7fdde..ce79c47 100644 --- a/js/tests/test-real-user-functionality.js +++ b/js/tests/test-real-user-functionality.js @@ -12,7 +12,6 @@ const RefactorTestRunner = require('./refactor-test-runner.js'); const runner = new RefactorTestRunner(); runner.describe('Real User Functionality Tests', () => { - runner.it('should allow users to edit content and see changes in DOM', () => { // Load all extracted components const sectionModule = require('../core/section-manager.js'); @@ -40,7 +39,7 @@ runner.describe('Real User Functionality Tests', () => { documentControls.create(); // Create sections from test markdown - const testMarkdown = `# Original Title\nOriginal content that should be editable.`; + const testMarkdown = '# Original Title\nOriginal content that should be editable.'; const sections = sectionManager.createSectionsFromMarkdown(testMarkdown); domRenderer.renderAllSections(sections); @@ -113,7 +112,7 @@ runner.describe('Real User Functionality Tests', () => { documentControls.create(); // Create and modify content - const testMarkdown = `# Test Section\nOriginal content for reset test.`; + const testMarkdown = '# Test Section\nOriginal content for reset test.'; const sections = sectionManager.createSectionsFromMarkdown(testMarkdown); domRenderer.renderAllSections(sections); @@ -161,7 +160,7 @@ runner.describe('Real User Functionality Tests', () => { const sectionManager = new SectionManager(); const domRenderer = new DOMRenderer(sectionManager, container); - const testMarkdown = `# Cancel Test\nContent that should remain unchanged.`; + const testMarkdown = '# Cancel Test\nContent that should remain unchanged.'; const sections = sectionManager.createSectionsFromMarkdown(testMarkdown); domRenderer.renderAllSections(sections); @@ -282,4 +281,4 @@ if (require.main === module) { console.log('āœ… Real user functionality tests completed'); console.log('These tests validate what users actually experience, not just internal APIs'); }); -} \ No newline at end of file +} diff --git a/js/tests/test-section-manager-extraction.js b/js/tests/test-section-manager-extraction.js index 1eecce5..abdb7e5 100644 --- a/js/tests/test-section-manager-extraction.js +++ b/js/tests/test-section-manager-extraction.js @@ -31,7 +31,6 @@ const EXPECTED_SECTION_MANAGER_API = [ ]; runner.describe('SectionManager Component Extraction', () => { - runner.it('should define expected API methods', () => { // This test defines what we expect from the extracted SectionManager const expectedMethods = EXPECTED_SECTION_MANAGER_API; @@ -46,7 +45,7 @@ runner.describe('SectionManager Component Extraction', () => { delete require.cache[require.resolve('/home/worsch/markitect_project/markitect/static/editor.js')]; try { - const editorModule = require('/home/worsch/markitect_project/markitect/static/editor.js'); + const editorModule = require('../../../../../../../../home/worsch/markitect_project/markitect/static/editor.js'); runner.expect(editorModule.SectionManager).toBeTruthy(); // Set global for other tests global.SectionManager = editorModule.SectionManager; @@ -69,7 +68,7 @@ runner.describe('SectionManager Component Extraction', () => { const SectionManager = global.SectionManager; const manager = new SectionManager(); - const testMarkdown = `# Heading 1\nContent 1\n\n## Heading 2\nContent 2`; + const testMarkdown = '# Heading 1\nContent 1\n\n## Heading 2\nContent 2'; const sections = manager.createSectionsFromMarkdown(testMarkdown); runner.expect(Array.isArray(sections)).toBeTruthy(); @@ -193,4 +192,4 @@ if (require.main === module) { runner.run().then(() => { console.log('āœ… SectionManager extraction tests completed'); }); -} \ No newline at end of file +} diff --git a/js/utils/html-generator.js b/js/utils/html-generator.js index a4e0b08..14b305c 100644 --- a/js/utils/html-generator.js +++ b/js/utils/html-generator.js @@ -23,9 +23,9 @@ class HTMLGenerator { title: options.title || 'TestDrive-JSUI Editor', description: options.description || 'Interactive markdown editor', markdown: options.markdown || '# Welcome\n\nStart editing...', - mode: options.mode || 'edit', // 'edit' or 'view' + mode: options.mode || 'edit', // 'edit' or 'view' theme: options.theme || 'github', - standalone: options.standalone !== false, // default true + standalone: options.standalone !== false, // default true controls: { editControl: options.controls?.editControl !== false, statusControl: options.controls?.statusControl !== false, @@ -34,8 +34,8 @@ class HTMLGenerator { }, shortcuts: options.shortcuts !== false, autosave: options.autosave || false, - baseUrl: options.baseUrl || '_jsui', // For external mode - includeMarkedJS: options.includeMarkedJS !== false // Include marked.js CDN + baseUrl: options.baseUrl || '_jsui', // For external mode + includeMarkedJS: options.includeMarkedJS !== false // Include marked.js CDN }; } diff --git a/js/widgets/base/UIWidget.js b/js/widgets/base/UIWidget.js index c889d0d..0133b33 100644 --- a/js/widgets/base/UIWidget.js +++ b/js/widgets/base/UIWidget.js @@ -212,4 +212,4 @@ export class UIWidget extends Widget { enableAnimations: true }; } -} \ No newline at end of file +} diff --git a/js/widgets/base/Widget.js b/js/widgets/base/Widget.js index 1c284cf..d80d481 100644 --- a/js/widgets/base/Widget.js +++ b/js/widgets/base/Widget.js @@ -138,4 +138,4 @@ export class Widget extends EventTarget { return element; } -} \ No newline at end of file +} diff --git a/js/widgets/navigation/DocumentNavigator.js b/js/widgets/navigation/DocumentNavigator.js index d25e058..742ed3c 100644 --- a/js/widgets/navigation/DocumentNavigator.js +++ b/js/widgets/navigation/DocumentNavigator.js @@ -32,15 +32,15 @@ export class DocumentNavigator extends UIWidget { getDefaultConfig() { return { ...super.getDefaultConfig(), - position: 'left', // 'left' or 'right' - collapsed: true, // Start collapsed - autoHide: true, // Hide on mobile - maxHeadingLevel: 3, // H1, H2, H3 - enableScrollSpy: true, // Highlight current section - smoothScroll: true, // Smooth scroll behavior - animationDuration: 300, // Animation timing - minHeadings: 2, // Min headings to show navigator - theme: 'default', // Theme support + position: 'left', // 'left' or 'right' + collapsed: true, // Start collapsed + autoHide: true, // Hide on mobile + maxHeadingLevel: 3, // H1, H2, H3 + enableScrollSpy: true, // Highlight current section + smoothScroll: true, // Smooth scroll behavior + animationDuration: 300, // Animation timing + minHeadings: 2, // Min headings to show navigator + theme: 'default', // Theme support // Styling options width: '280px', @@ -89,7 +89,7 @@ export class DocumentNavigator extends UIWidget { className: 'document-navigator markitect-widget', attributes: { 'aria-label': this.config.ariaLabel, - 'role': 'navigation' + role: 'navigation' }, style: this.getNavigatorStyle() }); @@ -130,7 +130,7 @@ export class DocumentNavigator extends UIWidget { this.toggleButton = this.createElement('button', { className: 'navigator-toggle', attributes: { - 'type': 'button', + type: 'button', 'aria-label': this.isCollapsed ? 'Expand navigation' : 'Collapse navigation', 'aria-expanded': !this.isCollapsed }, @@ -236,11 +236,11 @@ export class DocumentNavigator extends UIWidget { className: 'navigator-link', textContent: item.text, attributes: { - 'href': `#${item.id}`, + href: `#${item.id}`, 'data-target': item.id, 'data-level': item.level, - 'role': 'button', - 'tabindex': '0' + role: 'button', + tabindex: '0' }, style: { display: 'block', @@ -622,4 +622,4 @@ export class DocumentNavigator extends UIWidget { await super.destroy(); } -} \ No newline at end of file +} diff --git a/src/components/ui-edit-button/ui-edit-button.js b/src/components/ui-edit-button/ui-edit-button.js index c4a609f..d99ce1a 100644 --- a/src/components/ui-edit-button/ui-edit-button.js +++ b/src/components/ui-edit-button/ui-edit-button.js @@ -1,7 +1,7 @@ -import { LitElement, html, css } from "lit"; +import { LitElement, html, css } from 'lit'; export class UiEditButton extends LitElement { - static styles = css` + static styles = css` button { position: fixed; top: 50%; @@ -20,13 +20,13 @@ export class UiEditButton extends LitElement { } `; - render() { - return html``; - } + render() { + return html``; + } - _onClick() { - alert("Edit button clicked!"); - } + _onClick() { + alert('Edit button clicked!'); + } } -customElements.define("ui-edit-button", UiEditButton); +customElements.define('ui-edit-button', UiEditButton); diff --git a/src/components/ui-edit-button/ui-edit-button.stories.js b/src/components/ui-edit-button/ui-edit-button.stories.js index c002a96..02c2de2 100644 --- a/src/components/ui-edit-button/ui-edit-button.stories.js +++ b/src/components/ui-edit-button/ui-edit-button.stories.js @@ -1,7 +1,7 @@ -import "./ui-edit-button.js"; +import './ui-edit-button.js'; export default { - title: "UI/Edit Button", + title: 'UI/Edit Button' }; -export const Default = () => ``; +export const Default = () => ''; diff --git a/src/components/ui-edit-button/ui-edit-button.test.js b/src/components/ui-edit-button/ui-edit-button.test.js index 4f50e03..1df6d91 100644 --- a/src/components/ui-edit-button/ui-edit-button.test.js +++ b/src/components/ui-edit-button/ui-edit-button.test.js @@ -1,24 +1,24 @@ -import "../ui-edit-button/ui-edit-button.js"; +import '../ui-edit-button/ui-edit-button.js'; -describe("", () => { - it("renders a button element", () => { - const el = document.createElement("ui-edit-button"); - document.body.appendChild(el); - const button = el.shadowRoot.querySelector("button"); - expect(button).to.exist; - expect(button.textContent).to.include("Edit"); - }); +describe('', () => { + it('renders a button element', () => { + const el = document.createElement('ui-edit-button'); + document.body.appendChild(el); + const button = el.shadowRoot.querySelector('button'); + expect(button).to.exist; + expect(button.textContent).to.include('Edit'); + }); - it("triggers a click handler", () => { - const el = document.createElement("ui-edit-button"); - document.body.appendChild(el); + it('triggers a click handler', () => { + const el = document.createElement('ui-edit-button'); + document.body.appendChild(el); - let clicked = false; - el._onClick = () => (clicked = true); + let clicked = false; + el._onClick = () => (clicked = true); - const button = el.shadowRoot.querySelector("button"); - button.click(); + const button = el.shadowRoot.querySelector('button'); + button.click(); - expect(clicked).to.be.true; - }); + expect(clicked).to.be.true; + }); }); diff --git a/src/index.js b/src/index.js index 6ac0896..0522e76 100644 --- a/src/index.js +++ b/src/index.js @@ -48,8 +48,8 @@ import '../js/testdrive-jsui.js'; // Get the TestDriveJSUI from window (set by testdrive-jsui.js) // In UMD build, this will be available globally -const TestDriveJSUI = (typeof window !== 'undefined' && window.TestDriveJSUI) - || (typeof global !== 'undefined' && global.TestDriveJSUI); +const TestDriveJSUI = (typeof window !== 'undefined' && window.TestDriveJSUI) || + (typeof global !== 'undefined' && global.TestDriveJSUI); // Export for ES modules and CommonJS export default TestDriveJSUI;