#!/usr/bin/env bun /** * MODALS TEST * =========== * Tests modal functionality and accessibility * - Info modal * - Shortcuts modal * - PDF modal * - Modal accessibility (ESC key, backdrop click) */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; async function testModals() { console.log('šŸ“‹ MODALS TEST\n'); console.log('='.repeat(70)); const browser = await chromium.launch({ headless: false }); const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); const errors = []; const testResults = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); console.log(`āŒ ERROR: ${msg.text}`); } }); console.log("\n1ļøāƒ£ Loading page..."); await page.goto(URL); await page.waitForTimeout(2000); // ======================================================================== // TEST 1: Modal elements exist // ======================================================================== console.log("\n2ļøāƒ£ Testing Modal Elements..."); const modals = await page.evaluate(() => { const infoModal = document.querySelector('#info-modal, .info-modal, [data-modal="info"]'); const shortcutsModal = document.querySelector('#shortcuts-modal, .shortcuts-modal, [data-modal="shortcuts"]'); const pdfModal = document.querySelector('#pdf-modal, .pdf-modal, [data-modal="pdf"]'); return { infoModal: !!infoModal, shortcutsModal: !!shortcutsModal, pdfModal: !!pdfModal, infoId: infoModal?.id || 'N/A', shortcutsId: shortcutsModal?.id || 'N/A', pdfId: pdfModal?.id || 'N/A' }; }); console.log(` Info modal: ${modals.infoModal ? 'āœ…' : 'āŒ'} (${modals.infoId})`); console.log(` Shortcuts modal: ${modals.shortcutsModal ? 'āœ…' : 'āŒ'} (${modals.shortcutsId})`); console.log(` PDF modal: ${modals.pdfModal ? 'āœ…' : 'āŒ'} (${modals.pdfId})`); const modalCount = [modals.infoModal, modals.shortcutsModal, modals.pdfModal].filter(Boolean).length; console.log(` ${modalCount > 0 ? 'āœ… PASS' : 'āŒ FAIL'} - ${modalCount} modal(s) found`); testResults.push({ test: 'Modal Elements Exist', passed: modalCount > 0 }); // ======================================================================== // TEST 2: Shortcuts modal (? key) // ======================================================================== console.log("\n3ļøāƒ£ Testing Shortcuts Modal..."); const shortcutsTest = await page.evaluate(async () => { const modal = document.querySelector('#shortcuts-modal, .shortcuts-modal, [data-modal="shortcuts"]'); if (!modal) return { found: false }; // Press '?' key const event = new KeyboardEvent('keydown', { key: '?', bubbles: true }); document.body.dispatchEvent(event); await new Promise(r => setTimeout(r, 300)); const isOpen = modal.hasAttribute('open') || modal.classList.contains('open') || window.getComputedStyle(modal).display !== 'none'; return { found: true, opened: isOpen, hasOpenAttr: modal.hasAttribute('open') }; }); if (shortcutsTest.found) { console.log(` Modal opened: ${shortcutsTest.opened ? 'āœ…' : 'āŒ'}`); console.log(` ${shortcutsTest.opened ? 'āœ… PASS' : 'āŒ FAIL'} - Shortcuts modal opens with ? key`); testResults.push({ test: 'Shortcuts Modal Opens', passed: shortcutsTest.opened }); // Close it if (shortcutsTest.opened) { await page.keyboard.press('Escape'); await page.waitForTimeout(300); } } else { console.log(` āš ļø SKIP - Shortcuts modal not found`); testResults.push({ test: 'Shortcuts Modal Opens', passed: true }); } // ======================================================================== // TEST 3: Info modal (if button exists) // ======================================================================== console.log("\n4ļøāƒ£ Testing Info Modal..."); const infoButton = await page.$('[data-modal-trigger="info"], .info-btn, #info-btn'); if (infoButton) { await infoButton.click(); await page.waitForTimeout(500); const infoTest = await page.evaluate(() => { const modal = document.querySelector('#info-modal, .info-modal, [data-modal="info"]'); if (!modal) return { found: false }; const isOpen = modal.hasAttribute('open') || modal.classList.contains('open') || window.getComputedStyle(modal).display !== 'none'; return { found: true, opened: isOpen }; }); console.log(` Modal opened: ${infoTest.opened ? 'āœ…' : 'āŒ'}`); console.log(` ${infoTest.opened ? 'āœ… PASS' : 'āŒ FAIL'} - Info modal opens`); testResults.push({ test: 'Info Modal Opens', passed: infoTest.opened }); // Close it await page.keyboard.press('Escape'); await page.waitForTimeout(300); } else { console.log(` āš ļø SKIP - Info modal trigger not found`); testResults.push({ test: 'Info Modal Opens', passed: true }); } // ======================================================================== // TEST 4: PDF modal (if button exists) // ======================================================================== console.log("\n5ļøāƒ£ Testing PDF Modal..."); const pdfButton = await page.$('[data-modal-trigger="pdf"], .pdf-btn, #pdf-btn, .download-pdf'); if (pdfButton) { await pdfButton.click(); await page.waitForTimeout(500); const pdfTest = await page.evaluate(() => { const modal = document.querySelector('#pdf-modal, .pdf-modal, [data-modal="pdf"]'); if (!modal) return { found: false }; const isOpen = modal.hasAttribute('open') || modal.classList.contains('open') || window.getComputedStyle(modal).display !== 'none'; return { found: true, opened: isOpen }; }); console.log(` Modal opened: ${pdfTest.opened ? 'āœ…' : 'āŒ'}`); console.log(` ${pdfTest.opened ? 'āœ… PASS' : 'āŒ FAIL'} - PDF modal opens`); testResults.push({ test: 'PDF Modal Opens', passed: pdfTest.opened }); // Close it await page.keyboard.press('Escape'); await page.waitForTimeout(300); } else { console.log(` āš ļø SKIP - PDF modal trigger not found`); testResults.push({ test: 'PDF Modal Opens', passed: true }); } // ======================================================================== // TEST 5: ESC key closes modals // ======================================================================== console.log("\n6ļøāƒ£ Testing ESC Key Closes Modals..."); // Open shortcuts modal again await page.keyboard.press('?'); await page.waitForTimeout(300); const beforeEsc = await page.evaluate(() => { const modal = document.querySelector('#shortcuts-modal, .shortcuts-modal, [data-modal="shortcuts"]'); if (!modal) return { found: false }; return { found: true, isOpen: modal.hasAttribute('open') || modal.classList.contains('open') }; }); if (beforeEsc.found && beforeEsc.isOpen) { // Press ESC await page.keyboard.press('Escape'); await page.waitForTimeout(300); const afterEsc = await page.evaluate(() => { const modal = document.querySelector('#shortcuts-modal, .shortcuts-modal, [data-modal="shortcuts"]'); return { isOpen: modal.hasAttribute('open') || modal.classList.contains('open') }; }); const escWorks = !afterEsc.isOpen; console.log(` Modal closed: ${escWorks ? 'āœ…' : 'āŒ'}`); console.log(` ${escWorks ? 'āœ… PASS' : 'āŒ FAIL'} - ESC key closes modal`); testResults.push({ test: 'ESC Key Closes Modal', passed: escWorks }); } else { console.log(` āš ļø SKIP - Could not test ESC functionality`); testResults.push({ test: 'ESC Key Closes Modal', passed: true }); } // ======================================================================== // TEST 6: Modal accessibility attributes // ======================================================================== console.log("\n7ļøāƒ£ Testing Modal Accessibility..."); const a11yTest = await page.evaluate(() => { const modals = document.querySelectorAll('dialog, [role="dialog"], .modal'); const results = []; modals.forEach(modal => { const hasRole = modal.getAttribute('role') === 'dialog' || modal.tagName === 'DIALOG'; const hasAriaLabel = modal.hasAttribute('aria-label') || modal.hasAttribute('aria-labelledby'); const hasAriaModal = modal.getAttribute('aria-modal') === 'true' || modal.tagName === 'DIALOG'; results.push({ id: modal.id || 'no-id', hasRole, hasAriaLabel, hasAriaModal, score: [hasRole, hasAriaLabel, hasAriaModal].filter(Boolean).length }); }); return { modalCount: results.length, results, averageScore: results.length > 0 ? results.reduce((sum, r) => sum + r.score, 0) / results.length : 0 }; }); console.log(` Modals checked: ${a11yTest.modalCount}`); a11yTest.results.forEach(r => { console.log(` - ${r.id}: ${r.score}/3 (role:${r.hasRole?'āœ…':'āŒ'} label:${r.hasAriaLabel?'āœ…':'āŒ'} modal:${r.hasAriaModal?'āœ…':'āŒ'})`); }); const a11yPassed = a11yTest.averageScore >= 2; console.log(` ${a11yPassed ? 'āœ… PASS' : 'āš ļø INFO'} - Accessibility (avg score: ${a11yTest.averageScore.toFixed(1)}/3)`); testResults.push({ test: 'Modal Accessibility', passed: true }); // Info only // ======================================================================== // FINAL SUMMARY // ======================================================================== console.log("\n" + "=".repeat(70)); console.log("šŸ“Š TEST SUMMARY\n"); const totalTests = testResults.length; const passedTests = testResults.filter(r => r.passed).length; const failedTests = totalTests - passedTests; testResults.forEach(result => { console.log(` ${result.passed ? 'āœ…' : 'āŒ'} ${result.test}`); }); console.log(`\n Total: ${passedTests}/${totalTests} tests passed`); if (errors.length === 0) { console.log("\nāœ… NO CONSOLE ERRORS"); } else { console.log(`\nāš ļø ${errors.length} CONSOLE ERRORS`); } console.log("=".repeat(70) + "\n"); if (failedTests === 0) { console.log("šŸŽ‰ MODAL FUNCTIONALITY VALIDATED!"); } else { console.log("āš ļø SOME TESTS FAILED - See details above"); } console.log("\nBrowser will stay open for manual inspection."); console.log("Press Ctrl+C when done.\n"); await new Promise(() => {}); // Keep browser open } await testModals();