Files
cv-site/tests/mjs/8-hover-sync.test.mjs
juanatsap f87a1a5c28 fix: implement complete hover synchronization for PDF/Print buttons
CRITICAL BUG FIX: Hover states now sync between action bar and hamburger menu

Changes:
1. Added mouseenter/mouseleave handlers to menu PDF button
   - templates/partials/navigation/hamburger-menu.html:178-181
   - Added .menu-pdf-btn class for targeting
   - Added hyperscript hover sync events

2. Updated syncPdfHover() function
   - static/js/cv-functions.js:71-82
   - Now selects both .pdf-btn and .menu-pdf-btn
   - Both buttons get .pdf-hover-sync class on hover

3. Updated syncPrintHover() function
   - static/js/cv-functions.js:88-99
   - Now selects both .print-btn and .menu-print-btn
   - Both buttons get .print-hover-sync class on hover

4. Added CSS for menu PDF button hover sync
   - static/css/main.css:2690-2700
   - .menu-pdf-btn.pdf-hover-sync styling (white bg, red icon)
   - Matches action bar PDF button hover state

5. Created comprehensive hover sync test
   - tests/mjs/8-hover-sync.test.mjs
   - Tests all 4 hover scenarios (bar→menu, menu→bar for both buttons)
   - Validates event handlers and CSS class application
   - Manual verification instructions included

Behavior now correct:
 Hovering action bar PDF button highlights menu PDF button
 Hovering action bar Print button highlights menu Print button
 Hovering menu PDF button highlights action bar PDF button
 Hovering menu Print button highlights action bar Print button

Fixes documented bug from PROJECT-MEMORY.md Section 3.
2025-11-17 14:35:32 +00:00

307 lines
12 KiB
JavaScript
Executable File

#!/usr/bin/env bun
/**
* HOVER SYNCHRONIZATION TEST
* ===========================
* Tests hover state synchronization between action bar and hamburger menu buttons
*
* CRITICAL BUG: PDF/Print button hover states don't sync between locations
*
* Expected behavior:
* - Hovering action bar PDF button should highlight menu PDF button
* - Hovering action bar Print button should highlight menu Print button
* - Hovering menu buttons should highlight action bar buttons
* - Classes added: .pdf-hover-sync and .print-hover-sync
*/
import { chromium } from 'playwright';
const URL = "http://localhost:1999";
async function testHoverSync() {
console.log('🔄 HOVER SYNCHRONIZATION 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 = [];
// Track errors
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
console.log(`❌ ERROR: ${msg.text()}`);
}
});
await page.goto(URL);
await page.waitForTimeout(2000);
// ========================================================================
// TEST 1: PDF Button Hover Sync (Action Bar → Menu)
// ========================================================================
console.log("\n1️⃣ Testing PDF Button Hover Sync (Action Bar → Menu)...");
// Open hamburger menu to make menu buttons visible
const hamburger = await page.$('.hamburger-btn');
if (hamburger) {
await hamburger.click();
await page.waitForTimeout(500);
}
const pdfTest1 = await page.evaluate(() => {
// Find buttons
const actionBarPdf = document.querySelector('#action-bar-pdf-btn');
const menuPdf = document.querySelector('.menu-action-btn'); // First menu-action-btn is PDF
if (!actionBarPdf || !menuPdf) {
return { found: false, actionBarPdf: !!actionBarPdf, menuPdf: !!menuPdf };
}
// Simulate hover on action bar button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
actionBarPdf.dispatchEvent(mouseEnter);
// Check if both have hover class
const actionBarHasClass = actionBarPdf.classList.contains('pdf-hover-sync');
const menuHasClass = menuPdf.classList.contains('pdf-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
actionBarPdf.dispatchEvent(mouseLeave);
return {
found: true,
actionBarHasClass,
menuHasClass,
actionBarClasses: Array.from(actionBarPdf.classList),
menuClasses: Array.from(menuPdf.classList)
};
});
if (!pdfTest1.found) {
console.log(` ❌ FAIL - Buttons not found`);
console.log(` Action bar PDF: ${pdfTest1.actionBarPdf ? '✅' : '❌'}`);
console.log(` Menu PDF: ${pdfTest1.menuPdf ? '✅' : '❌'}`);
testResults.push({ test: 'PDF Hover Sync (Bar→Menu)', passed: false });
} else {
console.log(` Action bar button adds class: ${pdfTest1.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button gets class: ${pdfTest1.menuHasClass ? '✅' : '❌'}`);
console.log(` Action bar classes: ${pdfTest1.actionBarClasses.join(', ')}`);
console.log(` Menu classes: ${pdfTest1.menuClasses.join(', ')}`);
const passed = pdfTest1.actionBarHasClass && pdfTest1.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - PDF hover sync (bar→menu)`);
testResults.push({ test: 'PDF Hover Sync (Bar→Menu)', passed });
}
// ========================================================================
// TEST 2: Print Button Hover Sync (Action Bar → Menu)
// ========================================================================
console.log("\n2️⃣ Testing Print Button Hover Sync (Action Bar → Menu)...");
const printTest1 = await page.evaluate(() => {
const actionBarPrint = document.querySelector('.action-bar-print-btn');
const menuPrint = document.querySelector('.menu-print-btn');
if (!actionBarPrint || !menuPrint) {
return { found: false, actionBarPrint: !!actionBarPrint, menuPrint: !!menuPrint };
}
// Simulate hover on action bar button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
actionBarPrint.dispatchEvent(mouseEnter);
// Check if both have hover class
const actionBarHasClass = actionBarPrint.classList.contains('print-hover-sync');
const menuHasClass = menuPrint.classList.contains('print-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
actionBarPrint.dispatchEvent(mouseLeave);
return {
found: true,
actionBarHasClass,
menuHasClass,
actionBarClasses: Array.from(actionBarPrint.classList),
menuClasses: Array.from(menuPrint.classList)
};
});
if (!printTest1.found) {
console.log(` ❌ FAIL - Buttons not found`);
console.log(` Action bar Print: ${printTest1.actionBarPrint ? '✅' : '❌'}`);
console.log(` Menu Print: ${printTest1.menuPrint ? '✅' : '❌'}`);
testResults.push({ test: 'Print Hover Sync (Bar→Menu)', passed: false });
} else {
console.log(` Action bar button adds class: ${printTest1.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button gets class: ${printTest1.menuHasClass ? '✅' : '❌'}`);
console.log(` Action bar classes: ${printTest1.actionBarClasses.join(', ')}`);
console.log(` Menu classes: ${printTest1.menuClasses.join(', ')}`);
const passed = printTest1.actionBarHasClass && printTest1.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - Print hover sync (bar→menu)`);
testResults.push({ test: 'Print Hover Sync (Bar→Menu)', passed });
}
// ========================================================================
// TEST 3: PDF Button Hover Sync (Menu → Action Bar)
// ========================================================================
console.log("\n3️⃣ Testing PDF Button Hover Sync (Menu → Action Bar)...");
const pdfTest2 = await page.evaluate(() => {
const actionBarPdf = document.querySelector('#action-bar-pdf-btn');
const menuPdf = document.querySelector('.menu-action-btn');
if (!actionBarPdf || !menuPdf) {
return { found: false };
}
// Check if menu button has event handlers
const hasMouseEnter = menuPdf.getAttribute('_')?.includes('mouseenter');
const hasMouseLeave = menuPdf.getAttribute('_')?.includes('mouseleave');
// Simulate hover on menu button (if it has handlers)
if (hasMouseEnter) {
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
menuPdf.dispatchEvent(mouseEnter);
}
const actionBarHasClass = actionBarPdf.classList.contains('pdf-hover-sync');
const menuHasClass = menuPdf.classList.contains('pdf-hover-sync');
// Clean up
if (hasMouseLeave) {
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
menuPdf.dispatchEvent(mouseLeave);
}
return {
found: true,
hasMouseEnter,
hasMouseLeave,
actionBarHasClass,
menuHasClass
};
});
if (!pdfTest2.found) {
console.log(` ❌ FAIL - Buttons not found`);
testResults.push({ test: 'PDF Hover Sync (Menu→Bar)', passed: false });
} else {
console.log(` Menu button has mouseenter handler: ${pdfTest2.hasMouseEnter ? '✅' : '❌'}`);
console.log(` Menu button has mouseleave handler: ${pdfTest2.hasMouseLeave ? '✅' : '❌'}`);
console.log(` Action bar button gets class: ${pdfTest2.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button adds class: ${pdfTest2.menuHasClass ? '✅' : '❌'}`);
const passed = pdfTest2.hasMouseEnter && pdfTest2.hasMouseLeave &&
pdfTest2.actionBarHasClass && pdfTest2.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - PDF hover sync (menu→bar)`);
testResults.push({ test: 'PDF Hover Sync (Menu→Bar)', passed });
}
// ========================================================================
// TEST 4: Print Button Hover Sync (Menu → Action Bar)
// ========================================================================
console.log("\n4️⃣ Testing Print Button Hover Sync (Menu → Action Bar)...");
const printTest2 = await page.evaluate(() => {
const actionBarPrint = document.querySelector('.action-bar-print-btn');
const menuPrint = document.querySelector('.menu-print-btn');
if (!actionBarPrint || !menuPrint) {
return { found: false };
}
// Check if menu button has event handlers
const hasMouseEnter = menuPrint.getAttribute('_')?.includes('mouseenter');
const hasMouseLeave = menuPrint.getAttribute('_')?.includes('mouseleave');
// Simulate hover on menu button
const mouseEnter = new MouseEvent('mouseenter', { bubbles: true });
menuPrint.dispatchEvent(mouseEnter);
const actionBarHasClass = actionBarPrint.classList.contains('print-hover-sync');
const menuHasClass = menuPrint.classList.contains('print-hover-sync');
// Clean up
const mouseLeave = new MouseEvent('mouseleave', { bubbles: true });
menuPrint.dispatchEvent(mouseLeave);
return {
found: true,
hasMouseEnter,
hasMouseLeave,
actionBarHasClass,
menuHasClass
};
});
if (!printTest2.found) {
console.log(` ❌ FAIL - Buttons not found`);
testResults.push({ test: 'Print Hover Sync (Menu→Bar)', passed: false });
} else {
console.log(` Menu button has mouseenter handler: ${printTest2.hasMouseEnter ? '✅' : '❌'}`);
console.log(` Menu button has mouseleave handler: ${printTest2.hasMouseLeave ? '✅' : '❌'}`);
console.log(` Action bar button gets class: ${printTest2.actionBarHasClass ? '✅' : '❌'}`);
console.log(` Menu button adds class: ${printTest2.menuHasClass ? '✅' : '❌'}`);
const passed = printTest2.hasMouseEnter && printTest2.hasMouseLeave &&
printTest2.actionBarHasClass && printTest2.menuHasClass;
console.log(` ${passed ? '✅ PASS' : '❌ FAIL'} - Print hover sync (menu→bar)`);
testResults.push({ test: 'Print Hover Sync (Menu→Bar)', passed });
}
// ========================================================================
// TEST 5: Visual hover test (manual verification)
// ========================================================================
console.log("\n5️⃣ Manual Visual Test...");
console.log(" Please hover over the following and verify visual sync:");
console.log(" 1. Hover action bar PDF button → menu PDF should highlight");
console.log(" 2. Hover action bar Print button → menu Print should highlight");
console.log(" 3. Hover menu PDF button → action bar PDF should highlight");
console.log(" 4. Hover menu Print button → action bar Print should highlight");
// ========================================================================
// 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("🎉 ALL HOVER SYNC TESTS PASSED!");
} else {
console.log("⚠️ SOME TESTS FAILED - Hover synchronization broken");
console.log("\nExpected bug locations:");
console.log(" - templates/partials/navigation/hamburger-menu.html (missing mouseenter/leave on PDF button)");
console.log(" - static/js/cv-functions.js (hover sync functions exist but not called properly)");
}
console.log("\nBrowser will stay open for manual verification.");
console.log("Press Ctrl+C when done.\n");
await new Promise(() => {}); // Keep browser open
}
await testHoverSync();