307 lines
12 KiB
JavaScript
307 lines
12 KiB
JavaScript
|
|
#!/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();
|