52e97f1411
The footer was being zoomed along with CV content because it was inside the #zoom-wrapper div. This fix moves the footer outside the zoom-wrapper so it remains at normal size regardless of zoom level. Changes: - Move footer template outside zoom-wrapper in index.html - Add UI exclusion test (11-zoom-ui-exclusion.test.mjs) Test coverage: - Verify footer, action bar, and menu are outside zoom-wrapper (DOM) - Verify UI elements remain unchanged when zooming to 200% - Verify CV content properly zooms while UI stays fixed Before: Footer inside zoom-wrapper (zoomed with content) After: Footer outside zoom-wrapper (stays normal size)
244 lines
9.3 KiB
JavaScript
244 lines
9.3 KiB
JavaScript
#!/usr/bin/env bun
|
|
/**
|
|
* ZOOM UI EXCLUSION TEST
|
|
* =======================
|
|
* Verifies that UI elements (footer, menu, buttons) are NOT affected by zoom
|
|
*
|
|
* Test Requirements:
|
|
* - Footer should NOT be inside #zoom-wrapper
|
|
* - Action bar should NOT be inside #zoom-wrapper
|
|
* - Hamburger menu should NOT be inside #zoom-wrapper
|
|
* - Fixed buttons should NOT be inside #zoom-wrapper
|
|
* - Only .cv-container content should be affected by zoom
|
|
*
|
|
* Test Flow:
|
|
* 1. Load page and show zoom control
|
|
* 2. Measure initial sizes of UI elements
|
|
* 3. Set zoom to 200%
|
|
* 4. Verify UI elements remain same size
|
|
* 5. Verify CV content has doubled in size
|
|
*/
|
|
|
|
import { chromium } from "playwright";
|
|
|
|
const URL = "http://localhost:1999";
|
|
|
|
async function testZoomUIExclusion() {
|
|
console.log("🧪 ZOOM UI EXCLUSION TEST\n");
|
|
console.log("=".repeat(70));
|
|
|
|
const browser = await chromium.launch({ headless: false });
|
|
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}`);
|
|
});
|
|
|
|
// ========================================================================
|
|
// TEST 1: Verify DOM Structure
|
|
// ========================================================================
|
|
console.log("\n1️⃣ Verifying DOM structure (UI outside zoom-wrapper)...");
|
|
await page.goto(URL);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Set zoom visible
|
|
await page.evaluate(() => localStorage.setItem('cv-zoom-visible', 'true'));
|
|
await page.reload();
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Check that footer is NOT inside zoom-wrapper
|
|
const footerOutsideZoom = await page.evaluate(() => {
|
|
const footer = document.querySelector('.page-footer');
|
|
const zoomWrapper = document.querySelector('#zoom-wrapper');
|
|
if (!footer || !zoomWrapper) return false;
|
|
return !zoomWrapper.contains(footer);
|
|
});
|
|
|
|
// Check that action bar is NOT inside zoom-wrapper
|
|
const actionBarOutsideZoom = await page.evaluate(() => {
|
|
const actionBar = document.querySelector('.action-bar');
|
|
const zoomWrapper = document.querySelector('#zoom-wrapper');
|
|
if (!actionBar || !zoomWrapper) return false;
|
|
return !zoomWrapper.contains(actionBar);
|
|
});
|
|
|
|
// Check that hamburger menu is NOT inside zoom-wrapper
|
|
const menuOutsideZoom = await page.evaluate(() => {
|
|
const menu = document.querySelector('.navigation-menu');
|
|
const zoomWrapper = document.querySelector('#zoom-wrapper');
|
|
if (!menu || !zoomWrapper) return false;
|
|
return !zoomWrapper.contains(menu);
|
|
});
|
|
|
|
// Check that CV content IS inside zoom-wrapper
|
|
const cvInsideZoom = await page.evaluate(() => {
|
|
const cv = document.querySelector('.cv-container');
|
|
const zoomWrapper = document.querySelector('#zoom-wrapper');
|
|
if (!cv || !zoomWrapper) return false;
|
|
return zoomWrapper.contains(cv);
|
|
});
|
|
|
|
const test1Passed = footerOutsideZoom && actionBarOutsideZoom && menuOutsideZoom && cvInsideZoom;
|
|
|
|
console.log(` Footer outside zoom-wrapper: ${footerOutsideZoom ? '✅ YES' : '❌ NO'}`);
|
|
console.log(` Action bar outside zoom-wrapper: ${actionBarOutsideZoom ? '✅ YES' : '❌ NO'}`);
|
|
console.log(` Menu outside zoom-wrapper: ${menuOutsideZoom ? '✅ YES' : '❌ NO'}`);
|
|
console.log(` CV content inside zoom-wrapper: ${cvInsideZoom ? '✅ YES' : '❌ NO'}`);
|
|
console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'}`);
|
|
|
|
testResults.push({ test: 'DOM Structure - UI Outside Zoom', passed: test1Passed });
|
|
|
|
// ========================================================================
|
|
// TEST 2: Measure UI Elements at 100% Zoom
|
|
// ========================================================================
|
|
console.log("\n2️⃣ Measuring UI elements at 100% zoom...");
|
|
|
|
const initialSizes = await page.evaluate(() => {
|
|
const footer = document.querySelector('.page-footer');
|
|
const actionBar = document.querySelector('.action-bar');
|
|
const cvPaper = document.querySelector('.cv-paper');
|
|
|
|
return {
|
|
footerHeight: footer ? footer.offsetHeight : 0,
|
|
actionBarHeight: actionBar ? actionBar.offsetHeight : 0,
|
|
cvWidth: cvPaper ? cvPaper.offsetWidth : 0,
|
|
};
|
|
});
|
|
|
|
console.log(` Footer height: ${initialSizes.footerHeight}px`);
|
|
console.log(` Action bar height: ${initialSizes.actionBarHeight}px`);
|
|
console.log(` CV width: ${initialSizes.cvWidth}px`);
|
|
|
|
// ========================================================================
|
|
// TEST 3: Set Zoom to 200% and Re-measure
|
|
// ========================================================================
|
|
console.log("\n3️⃣ Setting zoom to 200% and re-measuring...");
|
|
|
|
const slider = await page.$('#zoom-slider');
|
|
if (slider) {
|
|
await slider.evaluate(el => {
|
|
el.value = '200';
|
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
});
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const zoomedSizes = await page.evaluate(() => {
|
|
const footer = document.querySelector('.page-footer');
|
|
const actionBar = document.querySelector('.action-bar');
|
|
const cvPaper = document.querySelector('.cv-paper');
|
|
|
|
return {
|
|
footerHeight: footer ? footer.offsetHeight : 0,
|
|
actionBarHeight: actionBar ? actionBar.offsetHeight : 0,
|
|
cvWidth: cvPaper ? cvPaper.offsetWidth : 0,
|
|
};
|
|
});
|
|
|
|
console.log(` Footer height: ${zoomedSizes.footerHeight}px`);
|
|
console.log(` Action bar height: ${zoomedSizes.actionBarHeight}px`);
|
|
console.log(` CV width: ${zoomedSizes.cvWidth}px`);
|
|
|
|
// ========================================================================
|
|
// TEST 4: Verify UI Elements Unchanged
|
|
// ========================================================================
|
|
console.log("\n4️⃣ Verifying UI elements unchanged at 200% zoom...");
|
|
|
|
const footerUnchanged = Math.abs(initialSizes.footerHeight - zoomedSizes.footerHeight) < 5;
|
|
const actionBarUnchanged = Math.abs(initialSizes.actionBarHeight - zoomedSizes.actionBarHeight) < 5;
|
|
|
|
// CV should be approximately doubled (with some tolerance)
|
|
const cvGrew = zoomedSizes.cvWidth > initialSizes.cvWidth * 1.8;
|
|
|
|
const test4Passed = footerUnchanged && actionBarUnchanged && cvGrew;
|
|
|
|
console.log(` Footer unchanged: ${footerUnchanged ? '✅ YES' : '❌ NO'} (${initialSizes.footerHeight}px → ${zoomedSizes.footerHeight}px)`);
|
|
console.log(` Action bar unchanged: ${actionBarUnchanged ? '✅ YES' : '❌ NO'} (${initialSizes.actionBarHeight}px → ${zoomedSizes.actionBarHeight}px)`);
|
|
console.log(` CV content grew: ${cvGrew ? '✅ YES' : '❌ NO'} (${initialSizes.cvWidth}px → ${zoomedSizes.cvWidth}px)`);
|
|
console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'}`);
|
|
|
|
testResults.push({ test: 'UI Elements Unchanged at 200%', passed: test4Passed });
|
|
|
|
// ========================================================================
|
|
// TEST 5: Reset and Verify
|
|
// ========================================================================
|
|
console.log("\n5️⃣ Resetting zoom to 100%...");
|
|
|
|
const resetBtn = await page.$('#zoom-reset');
|
|
if (resetBtn) {
|
|
await resetBtn.click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const resetSizes = await page.evaluate(() => {
|
|
const footer = document.querySelector('.page-footer');
|
|
const actionBar = document.querySelector('.action-bar');
|
|
const cvPaper = document.querySelector('.cv-paper');
|
|
|
|
return {
|
|
footerHeight: footer ? footer.offsetHeight : 0,
|
|
actionBarHeight: actionBar ? actionBar.offsetHeight : 0,
|
|
cvWidth: cvPaper ? cvPaper.offsetWidth : 0,
|
|
};
|
|
});
|
|
|
|
const resetPassed = Math.abs(resetSizes.cvWidth - initialSizes.cvWidth) < 10;
|
|
|
|
console.log(` CV width restored: ${resetPassed ? '✅ YES' : '❌ NO'} (${resetSizes.cvWidth}px vs ${initialSizes.cvWidth}px initial)`);
|
|
console.log(` ${resetPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
|
|
testResults.push({ test: 'Zoom Reset to 100%', passed: resetPassed });
|
|
|
|
// ========================================================================
|
|
// 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! UI elements properly excluded from zoom.");
|
|
} 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 testZoomUIExclusion();
|