Files
cv-site/tests/mjs/39-mobile-updates-verification.mjs
juanatsap 2eafb78954 fix: Mobile view improvements - accordion styling and modal centering
Fixed two critical mobile view issues:

1. Extended CV Sidebar Accordion:
   - Updated sidebar.html to use native <details> element (was div with onclick)
   - Styled accordion header to match CV title badges dark theme (#303030)
   - Applied consistent styling: dark gray background, light text, uppercase, no spacing
   - Result: Sidebars now collapse/expand properly with native HTML functionality

2. PDF Download Modal Centering:
   - Added JavaScript-based centering for mobile viewports (≤768px)
   - Uses inline styles with !important flag to override browser defaults
   - Updated download button to call openPdfModal() function
   - Result: Modal is perfectly centered on mobile (0px offset)

Technical notes:
   - Modal centering required setProperty() with 'important' flag
   - Accordion matches cv-title-badges-header style exactly
   - All tests passing: accordion toggle, modal centering

Files modified:
   - templates/partials/cv/sidebar.html
   - static/css/05-responsive/_breakpoints.css
   - static/js/main.js
   - templates/partials/widgets/download-button.html

Tests added:
   - tests/mjs/43-mobile-accordion-and-modal-test.mjs
   - tests/mjs/46-visual-accordion-style-test.mjs
2025-11-22 16:23:05 +00:00

217 lines
7.1 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Test: Mobile Updates Verification
*
* Verifies the updated mobile features:
* 1. Keyboard shortcuts button is visible
* 2. All 6 buttons are positioned in a single row
* 3. Footer has 120px bottom padding
* 4. Footer hover effect enlarges text
*/
import { chromium } from 'playwright';
const TEST_URL = 'http://localhost:1999';
const VIEWPORT_WIDTH = 375; // Mobile width
const VIEWPORT_HEIGHT = 812; // iPhone X height
async function testMobileUpdates() {
console.log('🧪 Testing Mobile Updates (6-Button Row, Footer Padding, Hover Effect)');
console.log('='.repeat(70));
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT },
deviceScaleFactor: 2,
});
const page = await context.newPage();
// Disable cache
await page.route('**/*', (route) => {
route.continue({
headers: {
...route.request().headers(),
'Cache-Control': 'no-cache, no-store, must-revalidate',
},
});
});
try {
await page.goto(TEST_URL, { waitUntil: 'networkidle' });
console.log(`✅ Navigated to ${TEST_URL}`);
await page.waitForTimeout(1500);
let allTestsPassed = true;
// TEST 1: Keyboard shortcuts button should be visible on mobile
console.log('\n📊 TEST 1: Keyboard shortcuts button visible on mobile');
console.log('-'.repeat(70));
const shortcutsBtn = await page.$('.shortcuts-btn');
if (shortcutsBtn) {
const isVisible = await shortcutsBtn.isVisible();
if (isVisible) {
console.log('✅ Keyboard shortcuts button is visible on mobile');
} else {
console.log('❌ Keyboard shortcuts button is hidden (should be visible)');
allTestsPassed = false;
}
} else {
console.log('❌ Keyboard shortcuts button not found in DOM');
allTestsPassed = false;
}
// TEST 2: All 6 buttons should be positioned in a row
console.log('\n📊 TEST 2: All 6 buttons positioned in a row');
console.log('-'.repeat(70));
const buttonPositions = await page.evaluate(() => {
const buttons = [
{ name: 'Download', selector: '.download-btn' },
{ name: 'Print', selector: '.print-friendly-btn' },
{ name: 'Shortcuts', selector: '.shortcuts-btn' },
{ name: 'Theme', selector: '.color-theme-switcher' },
{ name: 'Info', selector: '.info-button' },
{ name: 'Back-to-top', selector: '.back-to-top' }
];
return buttons.map(({ name, selector }) => {
const btn = document.querySelector(selector);
if (!btn) return { name, exists: false };
const rect = btn.getBoundingClientRect();
const styles = window.getComputedStyle(btn);
return {
name,
exists: true,
bottom: rect.bottom,
left: rect.left,
width: rect.width,
position: styles.position
};
});
});
// Check all buttons exist and are at same bottom position
const bottomPosition = buttonPositions[0]?.bottom;
let allButtonsAligned = true;
for (const btn of buttonPositions) {
if (!btn.exists) {
console.log(`${btn.name}: Button not found`);
allTestsPassed = false;
allButtonsAligned = false;
} else {
const bottomMatch = Math.abs(btn.bottom - bottomPosition) < 5; // 5px tolerance
if (bottomMatch) {
console.log(`${btn.name}: Positioned at bottom=${btn.bottom.toFixed(0)}px, left=${btn.left.toFixed(0)}px`);
} else {
console.log(`${btn.name}: Wrong bottom position (${btn.bottom.toFixed(0)}px, expected ~${bottomPosition.toFixed(0)}px)`);
allTestsPassed = false;
allButtonsAligned = false;
}
}
}
if (allButtonsAligned && buttonPositions.every(b => b.exists)) {
console.log('✅ All 6 buttons are aligned in a row');
}
// TEST 3: Footer should have 120px bottom padding
console.log('\n📊 TEST 3: Footer has 120px bottom padding');
console.log('-'.repeat(70));
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
await page.waitForTimeout(500);
const footerPadding = await page.evaluate(() => {
const footer = document.querySelector('footer.no-print');
if (!footer) return { exists: false };
const styles = window.getComputedStyle(footer);
return {
exists: true,
paddingBottom: styles.paddingBottom,
paddingBottomValue: parseInt(styles.paddingBottom, 10)
};
});
if (footerPadding.exists) {
if (footerPadding.paddingBottomValue >= 110) {
console.log(`✅ Footer has adequate padding: ${footerPadding.paddingBottom}`);
} else {
console.log(`❌ Footer padding insufficient: ${footerPadding.paddingBottom} (expected >= 110px)`);
allTestsPassed = false;
}
} else {
console.log('❌ Footer element not found');
allTestsPassed = false;
}
// TEST 4: Footer hover effect should enlarge text
console.log('\n📊 TEST 4: Footer hover effect enlarges text');
console.log('-'.repeat(70));
// Get initial font size
const initialFontSize = await page.evaluate(() => {
const footer = document.querySelector('footer.no-print p');
if (!footer) return null;
return parseFloat(window.getComputedStyle(footer).fontSize);
});
// Trigger footer hover by adding class
await page.evaluate(() => {
const footer = document.querySelector('footer.no-print');
if (footer) {
footer.classList.add('footer-hovered');
// Trigger transition
footer.offsetHeight; // Force reflow
}
});
await page.waitForTimeout(500); // Wait for CSS transition
const hoveredFontSize = await page.evaluate(() => {
const footer = document.querySelector('footer.no-print p');
if (!footer) return null;
return parseFloat(window.getComputedStyle(footer).fontSize);
});
if (initialFontSize && hoveredFontSize) {
if (hoveredFontSize > initialFontSize) {
console.log(`✅ Footer text enlarged on hover: ${initialFontSize.toFixed(1)}px → ${hoveredFontSize.toFixed(1)}px`);
} else {
console.log(`❌ Footer text not enlarged: ${initialFontSize.toFixed(1)}px → ${hoveredFontSize.toFixed(1)}px`);
allTestsPassed = false;
}
} else {
console.log('❌ Could not measure footer font sizes');
allTestsPassed = false;
}
console.log('-'.repeat(70));
if (allTestsPassed) {
console.log('\n✅ ALL TESTS PASSED!');
console.log(' • Keyboard shortcuts button visible');
console.log(' • All 6 buttons in a single row');
console.log(' • Footer has 120px bottom padding');
console.log(' • Footer hover effect enlarges text');
} else {
console.log('\n❌ SOME TESTS FAILED - Check output above');
}
await browser.close();
process.exit(allTestsPassed ? 0 : 1);
} catch (error) {
console.error('\n❌ Test error:', error);
await browser.close();
process.exit(1);
}
}
testMobileUpdates();