2eafb78954
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
181 lines
7.6 KiB
JavaScript
Executable File
181 lines
7.6 KiB
JavaScript
Executable File
#!/usr/bin/env bun
|
|
|
|
/**
|
|
* Mobile Accordion Test
|
|
* ======================
|
|
* Tests the sidebar accordion functionality on mobile devices in the extended CV view.
|
|
*
|
|
* What this test verifies:
|
|
* - Accordion headers are visible on mobile (hidden on desktop)
|
|
* - Accordion opens and closes when clicked
|
|
* - Content is properly shown/hidden
|
|
* - Chevron icon rotates correctly
|
|
* - Both left and right sidebars work
|
|
*/
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
const BASE_URL = 'http://localhost:1999';
|
|
const MOBILE_VIEWPORT = { width: 375, height: 667 }; // iPhone SE size
|
|
|
|
async function testMobileAccordion() {
|
|
console.log('🧪 Starting Mobile Accordion Test...\n');
|
|
|
|
const browser = await chromium.launch({ headless: true });
|
|
const context = await browser.newContext({
|
|
viewport: MOBILE_VIEWPORT,
|
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'
|
|
});
|
|
const page = await context.newPage();
|
|
|
|
let allTestsPassed = true;
|
|
|
|
try {
|
|
// Navigate to page with extended CV (lang param and let localStorage handle length)
|
|
console.log('📱 Loading page in mobile viewport (375x667)...');
|
|
|
|
// First, set localStorage for extended CV
|
|
await page.goto(BASE_URL);
|
|
await page.evaluate(() => {
|
|
localStorage.setItem('cv-length', 'long');
|
|
});
|
|
|
|
// Reload with the extended CV setting
|
|
await page.goto(BASE_URL, { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(1500);
|
|
|
|
// Test Left Sidebar Accordion
|
|
console.log('\n🔍 Testing LEFT sidebar accordion...');
|
|
|
|
const leftAccordion = await page.locator('.cv-sidebar-left .sidebar-accordion').first();
|
|
const leftHeader = await page.locator('.cv-sidebar-left .sidebar-accordion-header').first();
|
|
const leftContent = await page.locator('.cv-sidebar-left .sidebar-accordion-content').first();
|
|
const leftChevron = await page.locator('.cv-sidebar-left .sidebar-accordion-header .chevron').first();
|
|
|
|
// Check header is visible on mobile
|
|
const leftHeaderVisible = await leftHeader.isVisible();
|
|
console.log(` ✓ Left accordion header visible: ${leftHeaderVisible ? '✅ YES' : '❌ NO'}`);
|
|
if (!leftHeaderVisible) allTestsPassed = false;
|
|
|
|
// Check if accordion is initially open
|
|
const leftInitiallyOpen = await leftAccordion.getAttribute('open');
|
|
console.log(` ✓ Left accordion initially open: ${leftInitiallyOpen !== null ? '✅ YES' : '❌ NO'}`);
|
|
|
|
// Check content is visible when open
|
|
const leftContentVisible = await leftContent.isVisible();
|
|
console.log(` ✓ Left content visible when open: ${leftContentVisible ? '✅ YES' : '❌ NO'}`);
|
|
if (!leftContentVisible && leftInitiallyOpen) allTestsPassed = false;
|
|
|
|
// Close the accordion
|
|
console.log('\n 🖱️ Clicking left accordion to close...');
|
|
await leftHeader.click();
|
|
await page.waitForTimeout(500); // Wait for animation
|
|
|
|
// Check accordion is closed
|
|
const leftNowClosed = await leftAccordion.getAttribute('open');
|
|
console.log(` ✓ Left accordion closed: ${leftNowClosed === null ? '✅ YES' : '❌ NO'}`);
|
|
if (leftNowClosed !== null) allTestsPassed = false;
|
|
|
|
// Check content is hidden when closed
|
|
const leftContentHidden = !(await leftContent.isVisible());
|
|
console.log(` ✓ Left content hidden when closed: ${leftContentHidden ? '✅ YES' : '❌ NO'}`);
|
|
if (!leftContentHidden) allTestsPassed = false;
|
|
|
|
// Open the accordion again
|
|
console.log('\n 🖱️ Clicking left accordion to re-open...');
|
|
await leftHeader.click();
|
|
await page.waitForTimeout(500); // Wait for animation
|
|
|
|
// Check accordion is open again
|
|
const leftReopened = await leftAccordion.getAttribute('open');
|
|
console.log(` ✓ Left accordion re-opened: ${leftReopened !== null ? '✅ YES' : '❌ NO'}`);
|
|
if (leftReopened === null) allTestsPassed = false;
|
|
|
|
// Check content is visible again
|
|
const leftContentVisibleAgain = await leftContent.isVisible();
|
|
console.log(` ✓ Left content visible again: ${leftContentVisibleAgain ? '✅ YES' : '❌ NO'}`);
|
|
if (!leftContentVisibleAgain) allTestsPassed = false;
|
|
|
|
// Test Right Sidebar Accordion
|
|
console.log('\n🔍 Testing RIGHT sidebar accordion...');
|
|
|
|
const rightAccordion = await page.locator('.cv-sidebar-right .sidebar-accordion').first();
|
|
const rightHeader = await page.locator('.cv-sidebar-right .sidebar-accordion-header').first();
|
|
const rightContent = await page.locator('.cv-sidebar-right .sidebar-accordion-content').first();
|
|
const rightChevron = await page.locator('.cv-sidebar-right .sidebar-accordion-header .chevron').first();
|
|
|
|
// Check header is visible on mobile
|
|
const rightHeaderVisible = await rightHeader.isVisible();
|
|
console.log(` ✓ Right accordion header visible: ${rightHeaderVisible ? '✅ YES' : '❌ NO'}`);
|
|
if (!rightHeaderVisible) allTestsPassed = false;
|
|
|
|
// Close and re-open right accordion
|
|
console.log('\n 🖱️ Clicking right accordion to close...');
|
|
await rightHeader.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const rightClosed = await rightAccordion.getAttribute('open');
|
|
console.log(` ✓ Right accordion closed: ${rightClosed === null ? '✅ YES' : '❌ NO'}`);
|
|
if (rightClosed !== null) allTestsPassed = false;
|
|
|
|
console.log('\n 🖱️ Clicking right accordion to re-open...');
|
|
await rightHeader.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const rightReopened = await rightAccordion.getAttribute('open');
|
|
console.log(` ✓ Right accordion re-opened: ${rightReopened !== null ? '✅ YES' : '❌ NO'}`);
|
|
if (rightReopened === null) allTestsPassed = false;
|
|
|
|
// Test styling
|
|
console.log('\n🎨 Testing accordion styling...');
|
|
|
|
const leftHeaderStyles = await leftHeader.evaluate(el => {
|
|
const styles = window.getComputedStyle(el);
|
|
return {
|
|
display: styles.display,
|
|
background: styles.backgroundColor,
|
|
padding: styles.padding,
|
|
cursor: styles.cursor,
|
|
borderRadius: styles.borderRadius
|
|
};
|
|
});
|
|
|
|
console.log(` ✓ Header display: ${leftHeaderStyles.display === 'flex' ? '✅ flex' : '❌ ' + leftHeaderStyles.display}`);
|
|
console.log(` ✓ Header background: ${leftHeaderStyles.background}`);
|
|
console.log(` ✓ Header cursor: ${leftHeaderStyles.cursor === 'pointer' ? '✅ pointer' : '❌ ' + leftHeaderStyles.cursor}`);
|
|
|
|
if (leftHeaderStyles.display !== 'flex' || leftHeaderStyles.cursor !== 'pointer') {
|
|
allTestsPassed = false;
|
|
}
|
|
|
|
// Take screenshot
|
|
console.log('\n📸 Taking screenshot...');
|
|
await page.screenshot({
|
|
path: 'tests/screenshots/mobile-accordion-extended.png',
|
|
fullPage: true
|
|
});
|
|
console.log(' Saved: tests/screenshots/mobile-accordion-extended.png');
|
|
|
|
} catch (error) {
|
|
console.error('\n❌ Test failed with error:', error);
|
|
allTestsPassed = false;
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
|
|
// Final result
|
|
console.log('\n' + '='.repeat(50));
|
|
if (allTestsPassed) {
|
|
console.log('✅ ALL MOBILE ACCORDION TESTS PASSED!');
|
|
console.log('='.repeat(50));
|
|
process.exit(0);
|
|
} else {
|
|
console.log('❌ SOME MOBILE ACCORDION TESTS FAILED');
|
|
console.log('='.repeat(50));
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run test
|
|
testMobileAccordion();
|