#!/usr/bin/env bun /** * COMPREHENSIVE TOGGLE TEST * ========================== * Tests ALL toggles work with REAL-TIME visual verification * - Checks that toggles update DOM immediately (no refresh needed) * - Verifies localStorage persistence * - Tests synchronization between action bar and menu toggles * - Validates visual rendering changes */ import { chromium } from "playwright"; const URL = "http://localhost:1999"; async function testAllToggles() { console.log("๐Ÿงช COMPREHENSIVE TOGGLE TEST\n"); console.log("=".repeat(70)); const browser = await chromium.launch({ headless: false }); // Use desktop viewport (>900px) to ensure toggles are visible const page = await browser.newPage({ viewport: { width: 1400, height: 1080 } }); const errors = []; const testResults = []; page.on('console', msg => { const text = msg.text(); if (msg.type() === 'error') { errors.push(text); console.log(`โŒ ERROR: ${text}`); } }); page.on('pageerror', err => { errors.push(err.message); console.log(`โŒ PAGE ERROR: ${err.message}`); }); console.log("\n1๏ธโƒฃ Loading page..."); await page.goto(URL); await page.waitForTimeout(2000); // ======================================================================== // VIEWPORT CHECK: Determine which toggles to use // ======================================================================== console.log("\n๐Ÿ” Checking viewport and toggle visibility..."); const desktopToggleVisible = await page.$eval('#lengthToggle', el => { const style = window.getComputedStyle(el); const rect = el.getBoundingClientRect(); return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && rect.width > 0 && rect.height > 0; }).catch(() => false); console.log(` Desktop toggles visible: ${desktopToggleVisible ? 'YES (using #lengthToggle, #iconToggle, #themeToggle)' : 'NO (will use menu toggles)'}`); // ======================================================================== // TEST 1: Length Toggle (Desktop or Menu) // ======================================================================== console.log("\n2๏ธโƒฃ Testing Length Toggle..."); let lengthToggle; if (desktopToggleVisible) { lengthToggle = await page.$('#lengthToggle'); } else { // Use menu toggle - but need to open menu first with hover console.log(" Opening hamburger menu..."); const hamburger = await page.$('.hamburger-btn'); if (hamburger) { await hamburger.hover(); await page.waitForTimeout(300); } lengthToggle = await page.$('#lengthToggleMenu'); } if (lengthToggle) { const paper = await page.$('.cv-paper'); // Get initial state const before = await paper.evaluate(el => ({ className: el.className, isLong: el.classList.contains('cv-long'), isShort: el.classList.contains('cv-short') })); // Click toggle await lengthToggle.click(); await page.waitForTimeout(300); // Wait for DOM update // Get state after click const after = await paper.evaluate(el => ({ className: el.className, isLong: el.classList.contains('cv-long'), isShort: el.classList.contains('cv-short') })); // Verify localStorage const localStorage = await page.evaluate(() => localStorage.getItem('cv-length')); const changed = before.isLong !== after.isLong; const testPassed = changed && (after.isLong ? localStorage === 'long' : localStorage === 'short'); console.log(` Before: ${before.isLong ? 'long' : 'short'}`); console.log(` After: ${after.isLong ? 'long' : 'short'}`); console.log(` localStorage: ${localStorage}`); console.log(` Visual change: ${changed ? 'โœ… YES' : 'โŒ NO'}`); console.log(` ${testPassed ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Length Toggle (Action Bar)', passed: testPassed }); } else { console.log(` โŒ Toggle not found`); testResults.push({ test: 'Length Toggle (Action Bar)', passed: false }); } // ======================================================================== // TEST 2: Icon Toggle (Desktop or Menu) // ======================================================================== console.log("\n3๏ธโƒฃ Testing Icon/Logo Toggle..."); let iconToggle; if (desktopToggleVisible) { iconToggle = await page.$('#iconToggle'); } else { // Menu should still be hovered from previous test const hamburger = await page.$('.hamburger-btn'); if (hamburger) { await hamburger.hover(); await page.waitForTimeout(200); } iconToggle = await page.$('#iconToggleMenu'); } if (iconToggle) { const paper = await page.$('.cv-paper'); // Take screenshot BEFORE toggle await page.screenshot({ path: 'tests/screenshots/before-icon-toggle.png', fullPage: false }); const before = await paper.evaluate(el => ({ className: el.className, showIcons: el.classList.contains('show-icons') })); // Click toggle await iconToggle.click(); await page.waitForTimeout(300); // Take screenshot AFTER toggle await page.screenshot({ path: 'tests/screenshots/after-icon-toggle.png', fullPage: false }); const after = await paper.evaluate(el => ({ className: el.className, showIcons: el.classList.contains('show-icons') })); const localStorage = await page.evaluate(() => localStorage.getItem('cv-icons')); const changed = before.showIcons !== after.showIcons; const testPassed = changed && (after.showIcons ? localStorage === 'true' : localStorage === 'false'); console.log(` Before: ${before.showIcons ? 'icons shown' : 'icons hidden'}`); console.log(` After: ${after.showIcons ? 'icons shown' : 'icons hidden'}`); console.log(` localStorage: ${localStorage}`); console.log(` Visual change: ${changed ? 'โœ… YES (no refresh needed)' : 'โŒ NO (requires refresh - BUG!)'}`); console.log(` Screenshots saved: before-icon-toggle.png, after-icon-toggle.png`); console.log(` ${testPassed ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Icon Toggle (Action Bar)', passed: testPassed }); } else { console.log(` โŒ Toggle not found`); testResults.push({ test: 'Icon Toggle (Action Bar)', passed: false }); } // ======================================================================== // TEST 3: Theme Toggle (Desktop or Menu) // ======================================================================== console.log("\n4๏ธโƒฃ Testing Theme Toggle..."); let themeToggle; if (desktopToggleVisible) { themeToggle = await page.$('#themeToggle'); } else { const hamburger = await page.$('.hamburger-btn'); if (hamburger) { await hamburger.hover(); await page.waitForTimeout(200); } themeToggle = await page.$('#themeToggleMenu'); } if (themeToggle) { const body = await page.$('body'); const before = await body.evaluate(el => ({ className: el.className, isClean: el.classList.contains('theme-clean') })); await themeToggle.click(); await page.waitForTimeout(300); const after = await body.evaluate(el => ({ className: el.className, isClean: el.classList.contains('theme-clean') })); const localStorage = await page.evaluate(() => localStorage.getItem('cv-theme')); const changed = before.isClean !== after.isClean; const testPassed = changed && (after.isClean ? localStorage === 'clean' : localStorage === 'default'); console.log(` Before: ${before.isClean ? 'clean' : 'default'}`); console.log(` After: ${after.isClean ? 'clean' : 'default'}`); console.log(` localStorage: ${localStorage}`); console.log(` Visual change: ${changed ? 'โœ… YES' : 'โŒ NO'}`); console.log(` ${testPassed ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Theme Toggle (Action Bar)', passed: testPassed }); } else { console.log(` โŒ Toggle not found`); testResults.push({ test: 'Theme Toggle (Action Bar)', passed: false }); } // ======================================================================== // TEST 4: Hamburger Menu Toggles + Synchronization // ======================================================================== console.log("\n5๏ธโƒฃ Testing Hamburger Menu + Toggle Synchronization..."); const hamburger = await page.$('.hamburger-btn'); if (hamburger) { await hamburger.click(); await page.waitForTimeout(500); const menu = await page.$('.navigation-menu'); const isOpen = await menu.evaluate(el => el.classList.contains('menu-open')); console.log(` ${isOpen ? 'โœ… Menu opened' : 'โŒ Menu failed to open'}`); if (isOpen) { // Test Menu Length Toggle console.log("\n6๏ธโƒฃ Testing Length Toggle (Menu)..."); const menuLengthToggle = await page.$('#lengthToggleMenu'); if (menuLengthToggle) { const paper = await page.$('.cv-paper'); const before = await paper.evaluate(el => el.classList.contains('cv-long')); await menuLengthToggle.click(); await page.waitForTimeout(300); const after = await paper.evaluate(el => el.classList.contains('cv-long')); // Check if action bar toggle synchronized const actionBarSynced = await page.$eval('#lengthToggle', el => el.checked); const menuChecked = await page.$eval('#lengthToggleMenu', el => el.checked); const changed = before !== after; const synced = actionBarSynced === menuChecked; console.log(` Visual change: ${changed ? 'โœ… YES' : 'โŒ NO'}`); console.log(` Synchronization: ${synced ? 'โœ… YES' : 'โŒ NO'}`); console.log(` ${changed && synced ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Length Toggle (Menu + Sync)', passed: changed && synced }); } // Test Menu Icon Toggle console.log("\n7๏ธโƒฃ Testing Icon Toggle (Menu)..."); const menuIconToggle = await page.$('#iconToggleMenu'); if (menuIconToggle) { const paper = await page.$('.cv-paper'); const before = await paper.evaluate(el => el.classList.contains('show-icons')); await menuIconToggle.click(); await page.waitForTimeout(300); const after = await paper.evaluate(el => el.classList.contains('show-icons')); const actionBarSynced = await page.$eval('#iconToggle', el => el.checked); const menuChecked = await page.$eval('#iconToggleMenu', el => el.checked); const changed = before !== after; const synced = actionBarSynced === menuChecked; console.log(` Visual change: ${changed ? 'โœ… YES (no refresh!)' : 'โŒ NO (BUG!)'}`); console.log(` Synchronization: ${synced ? 'โœ… YES' : 'โŒ NO'}`); console.log(` ${changed && synced ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Icon Toggle (Menu + Sync)', passed: changed && synced }); } // Test Menu Theme Toggle console.log("\n8๏ธโƒฃ Testing Theme Toggle (Menu)..."); const menuThemeToggle = await page.$('#themeToggleMenu'); if (menuThemeToggle) { const body = await page.$('body'); const before = await body.evaluate(el => el.classList.contains('theme-clean')); await menuThemeToggle.click(); await page.waitForTimeout(300); const after = await body.evaluate(el => el.classList.contains('theme-clean')); const actionBarSynced = await page.$eval('#themeToggle', el => el.checked); const menuChecked = await page.$eval('#themeToggleMenu', el => el.checked); const changed = before !== after; const synced = actionBarSynced === menuChecked; console.log(` Visual change: ${changed ? 'โœ… YES' : 'โŒ NO'}`); console.log(` Synchronization: ${synced ? 'โœ… YES' : 'โŒ NO'}`); console.log(` ${changed && synced ? 'โœ… PASS' : 'โŒ FAIL'}`); testResults.push({ test: 'Theme Toggle (Menu + Sync)', passed: changed && synced }); } } } // ======================================================================== // 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 FOUND:\n`); errors.forEach((err, i) => { console.log(`${i + 1}. ${err}`); }); } console.log("=".repeat(70) + "\n"); if (failedTests === 0 && errors.length === 0) { console.log("๐ŸŽ‰ ALL TESTS PASSED! All toggles work with real-time rendering."); } 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 testAllToggles();