#!/usr/bin/env node import { chromium } from 'playwright'; (async () => { console.log('๐Ÿงช COMPREHENSIVE FEATURE TEST\n'); console.log('Testing ALL CV site features systematically\n'); const browser = await chromium.launch({ headless: true, args: ['--disable-http-cache', '--disable-cache'] }); const context = await browser.newContext({ ignoreHTTPSErrors: true, bypassCSP: true }); const page = await context.newPage(); // Track errors const errors = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); } }); page.on('pageerror', err => { errors.push(err.message); }); // Load page with cache busting const url = `http://localhost:1999/?lang=en&_=${Date.now()}`; console.log(`๐Ÿ“„ Loading: ${url}\n`); await page.goto(url, { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); console.log('โ•'.repeat(80)); console.log('TEST RESULTS'); console.log('โ•'.repeat(80) + '\n'); let passCount = 0; let failCount = 0; // TEST 1: Parse Errors console.log('1. HYPERSCRIPT PARSE ERRORS'); const hasParseError = errors.some(e => e.includes('Expected') || e.includes('hyperscript')); if (!hasParseError) { console.log(' โœ… PASS - No parse errors\n'); passCount++; } else { console.log(' โŒ FAIL - Parse errors detected:\n'); errors.forEach(e => console.log(' ' + e)); failCount++; } // TEST 2: Function Availability console.log('2. HYPERSCRIPT FUNCTIONS'); const funcs = await page.evaluate(() => { return { printFriendly: typeof printFriendly !== 'undefined', handleScroll: typeof handleScroll !== 'undefined', initScrollBehavior: typeof initScrollBehavior !== 'undefined', toggleCVLength: typeof toggleCVLength !== 'undefined', toggleIcons: typeof toggleIcons !== 'undefined', toggleTheme: typeof toggleTheme !== 'undefined', syncPdfHover: typeof syncPdfHover !== 'undefined', syncPrintHover: typeof syncPrintHover !== 'undefined', highlightZoomControl: typeof highlightZoomControl !== 'undefined' }; }); const allFuncsExist = Object.values(funcs).every(v => v); if (allFuncsExist) { console.log(' โœ… PASS - All 9 functions defined\n'); passCount++; } else { console.log(' โŒ FAIL - Missing functions:'); Object.entries(funcs).forEach(([name, exists]) => { if (!exists) console.log(` - ${name}`); }); console.log(); failCount++; } // TEST 3: Toggle CV Length console.log('3. TOGGLE CV LENGTH'); const lengthTest = await page.evaluate(() => { const paper = document.querySelector('.cv-paper'); const initialLong = paper.classList.contains('cv-long'); // Call toggle function toggleCVLength(true); const afterLong = paper.classList.contains('cv-long'); toggleCVLength(false); const afterShort = paper.classList.contains('cv-short'); return { initialLong, afterLong, afterShort }; }); if (lengthTest.afterLong && lengthTest.afterShort) { console.log(' โœ… PASS - CV length toggle works\n'); passCount++; } else { console.log(' โŒ FAIL - Toggle not working:', lengthTest, '\n'); failCount++; } // TEST 4: Toggle Icons console.log('4. TOGGLE ICONS'); const iconsTest = await page.evaluate(() => { const paper = document.querySelector('.cv-paper'); toggleIcons(true); const withIcons = paper.classList.contains('show-icons'); toggleIcons(false); const withoutIcons = !paper.classList.contains('show-icons'); return { withIcons, withoutIcons }; }); if (iconsTest.withIcons && iconsTest.withoutIcons) { console.log(' โœ… PASS - Icons toggle works\n'); passCount++; } else { console.log(' โŒ FAIL - Toggle not working:', iconsTest, '\n'); failCount++; } // TEST 5: Toggle Theme console.log('5. TOGGLE THEME'); const themeTest = await page.evaluate(() => { const container = document.querySelector('.cv-container'); toggleTheme(true); const isClean = container.classList.contains('theme-clean'); toggleTheme(false); const isDefault = !container.classList.contains('theme-clean'); return { isClean, isDefault }; }); if (themeTest.isClean && themeTest.isDefault) { console.log(' โœ… PASS - Theme toggle works\n'); passCount++; } else { console.log(' โŒ FAIL - Toggle not working:', themeTest, '\n'); failCount++; } // TEST 6: PDF Hover Sync console.log('6. PDF HOVER SYNC'); const pdfHoverTest = await page.evaluate(() => { syncPdfHover(true); const fixedBtn = document.querySelector('#download-button'); const hasClass = fixedBtn ? fixedBtn.classList.contains('pdf-hover-sync') : false; syncPdfHover(false); const classRemoved = fixedBtn ? !fixedBtn.classList.contains('pdf-hover-sync') : false; return { hasClass, classRemoved }; }); if (pdfHoverTest.hasClass && pdfHoverTest.classRemoved) { console.log(' โœ… PASS - PDF hover sync works\n'); passCount++; } else { console.log(' โŒ FAIL - Hover sync not working:', pdfHoverTest, '\n'); failCount++; } // TEST 7: Print Hover Sync console.log('7. PRINT HOVER SYNC'); const printHoverTest = await page.evaluate(() => { syncPrintHover(true); const fixedBtn = document.querySelector('#print-friendly-button'); const hasClass = fixedBtn ? fixedBtn.classList.contains('print-hover-sync') : false; syncPrintHover(false); const classRemoved = fixedBtn ? !fixedBtn.classList.contains('print-hover-sync') : false; return { hasClass, classRemoved }; }); if (printHoverTest.hasClass && printHoverTest.classRemoved) { console.log(' โœ… PASS - Print hover sync works\n'); passCount++; } else { console.log(' โŒ FAIL - Hover sync not working:', printHoverTest, '\n'); failCount++; } // TEST 8: Zoom Control Highlight console.log('8. ZOOM CONTROL HIGHLIGHT'); const zoomHighlightTest = await page.evaluate(() => { highlightZoomControl(true); const zoomCtrl = document.querySelector('#zoom-control'); const hasHighlight = zoomCtrl ? zoomCtrl.classList.contains('zoom-highlight') : false; highlightZoomControl(false); const highlightRemoved = zoomCtrl ? !zoomCtrl.classList.contains('zoom-highlight') : false; return { hasHighlight, highlightRemoved }; }); if (zoomHighlightTest.hasHighlight && zoomHighlightTest.highlightRemoved) { console.log(' โœ… PASS - Zoom highlight works\n'); passCount++; } else { console.log(' โŒ FAIL - Highlight not working:', zoomHighlightTest, '\n'); failCount++; } // TEST 9: Scroll Behavior console.log('9. SCROLL BEHAVIOR'); await page.evaluate(() => window.scrollTo(0, 0)); await page.waitForTimeout(300); const scrollTest = await page.evaluate(async () => { const btn = document.querySelector('#back-to-top'); const hiddenAtTop = window.getComputedStyle(btn).display === 'none'; window.scrollTo(0, 500); const visibleWhenScrolled = window.getComputedStyle(btn).display === 'flex'; return { hiddenAtTop, visibleWhenScrolled }; }); if (scrollTest.hiddenAtTop && scrollTest.visibleWhenScrolled) { console.log(' โœ… PASS - Scroll behavior works\n'); passCount++; } else { console.log(' โŒ FAIL - Scroll not working:', scrollTest, '\n'); failCount++; } // TEST 10: Zoom Functionality console.log('10. ZOOM FUNCTIONALITY'); const zoomTest = await page.evaluate(() => { const slider = document.querySelector('#zoom-slider'); const wrapper = document.querySelector('#zoom-wrapper'); if (!slider || !wrapper) { return { error: 'Zoom elements not found' }; } // Trigger zoom slider.value = '110'; slider.dispatchEvent(new Event('input', { bubbles: true })); const zoomValue = wrapper.style.zoom || window.getComputedStyle(wrapper).zoom; // Reset slider.value = '100'; slider.dispatchEvent(new Event('input', { bubbles: true })); return { zoomValue, sliderExists: true }; }); if (zoomTest.zoomValue && zoomTest.zoomValue !== '1') { console.log(' โœ… PASS - Zoom functionality works\n'); passCount++; } else { console.log(' โŒ FAIL - Zoom not working:', zoomTest, '\n'); failCount++; } console.log('โ•'.repeat(80)); console.log(`SUMMARY: ${passCount} passed, ${failCount} failed out of 10 tests`); console.log('โ•'.repeat(80)); if (failCount === 0) { console.log('\nโœ… ALL TESTS PASSED! Site is fully functional.\n'); } else { console.log(`\nโŒ ${failCount} test(s) failed. See details above.\n`); } console.log('๐Ÿ’ก Browser window left open for manual inspection'); console.log(' Press Ctrl+C to exit\n'); })();