Files
cv-site/tests/mjs/43-mobile-accordion-and-modal-test.mjs
T
juanatsap fb313d8dc6 fix: Accordion starts closed on mobile by default
Removed 'open' attribute from accordion <details> elements to ensure sidebars
start collapsed on mobile view, providing a cleaner initial state.

Changes:
- templates/partials/cv/sidebar.html: Removed open attribute
- templates/cv-content.html: Removed open attributes (2 occurrences)
- templates/language-switch.html: Removed open attributes (2 occurrences)
- tests/mjs/43-mobile-accordion-and-modal-test.mjs: Updated test expectations

Test results:
   Accordion initially closed
   Content initially hidden
   Toggle functionality working perfectly
   Modal centering maintained (0px offset)
2025-11-22 16:31:29 +00:00

179 lines
8.1 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { chromium } from 'playwright';
const MOBILE_VIEWPORT = { width: 375, height: 667 }; // iPhone SE size
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ viewport: MOBILE_VIEWPORT });
const page = await context.newPage();
try {
console.log('🧪 Testing Mobile Accordion and Modal Centering\n');
console.log(`📱 Mobile Viewport: ${MOBILE_VIEWPORT.width}x${MOBILE_VIEWPORT.height}`);
// Navigate to extended view
await page.goto('http://localhost:1999/?lang=en&view=extended');
await page.waitForLoadState('networkidle');
console.log('✅ Extended view loaded\n');
// ===== TEST 1: SIDEBAR ACCORDION =====
console.log('🔍 TEST 1: Sidebar Accordion Structure\n');
// Check for accordion elements
const accordionDetails = await page.locator('.sidebar-accordion').first();
const accordionSummary = await page.locator('.sidebar-accordion-header').first();
const accordionContent = await page.locator('.sidebar-accordion-content').first();
const detailsCount = await page.locator('.sidebar-accordion').count();
const summaryCount = await page.locator('.sidebar-accordion-header').count();
const contentCount = await page.locator('.sidebar-accordion-content').count();
console.log(`📊 Accordion Structure:`);
console.log(` • Details elements: ${detailsCount}`);
console.log(` • Summary elements: ${summaryCount}`);
console.log(` • Content elements: ${contentCount}`);
// Check if accordion is initially closed (should be closed on mobile)
const isOpen = await accordionDetails.evaluate(el => el.hasAttribute('open'));
console.log(` • Initially closed: ${!isOpen ? '✅ YES' : '❌ NO (should be closed)'}`);
console.log(` • Initially open: ${isOpen ? '❌ YES (should be closed)' : '✅ NO'}`);
// Check if summary is visible on mobile
const summaryDisplay = await accordionSummary.evaluate(el => {
const style = window.getComputedStyle(el);
return style.display;
});
console.log(` • Summary display: ${summaryDisplay}`);
console.log(` • Summary visible: ${summaryDisplay !== 'none' ? '✅ YES' : '❌ NO'}`);
// Check if content is initially hidden (since accordion starts closed)
const initialContentHidden = await accordionContent.evaluate(el => {
const style = window.getComputedStyle(el);
return style.maxHeight === '0px' || style.opacity === '0';
});
console.log(` • Content initially hidden: ${initialContentHidden ? '✅ YES' : '❌ NO'}`);
// Test accordion toggle
if (detailsCount > 0) {
console.log('\n🖱️ Testing Accordion Toggle...');
// Click to OPEN (since it starts closed)
await accordionSummary.click();
await page.waitForTimeout(500);
const isOpenedAfterClick = await accordionDetails.evaluate(el => el.hasAttribute('open'));
console.log(` • Opened after click: ${isOpenedAfterClick ? '✅ YES' : '❌ NO'}`);
// Check if content is now visible
const contentVisible = await accordionContent.evaluate(el => {
const style = window.getComputedStyle(el);
return style.opacity === '1';
});
console.log(` • Content visible: ${contentVisible ? '✅ YES' : '❌ NO'}`);
// Click to close again
await accordionSummary.click();
await page.waitForTimeout(500);
const isClosedAgain = await accordionDetails.evaluate(el => !el.hasAttribute('open'));
console.log(` • Closed after second click: ${isClosedAgain ? '✅ YES' : '❌ NO'}`);
// Verify content is hidden again
const contentHiddenAgain = await accordionContent.evaluate(el => {
const style = window.getComputedStyle(el);
return style.maxHeight === '0px' || style.opacity === '0';
});
console.log(` • Content hidden again: ${contentHiddenAgain ? '✅ YES' : '❌ NO'}`);
}
// ===== TEST 2: MODAL CENTERING =====
console.log('\n🔍 TEST 2: Modal Centering\n');
// Open the PDF modal
await page.click('#download-button');
await page.waitForTimeout(500);
const modal = await page.locator('#pdf-modal');
const isModalOpen = await modal.evaluate(el => el.hasAttribute('open'));
console.log(`📦 Modal Status:`);
console.log(` • Modal open: ${isModalOpen ? '✅ YES' : '❌ NO'}`);
if (isModalOpen) {
// Get modal bounding box
const modalBox = await modal.boundingBox();
console.log(`\n📐 Modal Position:`);
console.log(` • X position: ${modalBox.x.toFixed(2)}px`);
console.log(` • Y position: ${modalBox.y.toFixed(2)}px`);
console.log(` • Width: ${modalBox.width.toFixed(2)}px`);
console.log(` • Height: ${modalBox.height.toFixed(2)}px`);
// Calculate centers
const modalCenterX = modalBox.x + modalBox.width / 2;
const modalCenterY = modalBox.y + modalBox.height / 2;
const viewportCenterX = MOBILE_VIEWPORT.width / 2;
const viewportCenterY = MOBILE_VIEWPORT.height / 2;
console.log(`\n🎯 Centering Analysis:`);
console.log(` • Modal center X: ${modalCenterX.toFixed(2)}px`);
console.log(` • Viewport center X: ${viewportCenterX.toFixed(2)}px`);
console.log(` • Modal center Y: ${modalCenterY.toFixed(2)}px`);
console.log(` • Viewport center Y: ${viewportCenterY.toFixed(2)}px`);
// Check if centered (10px tolerance)
const horizontalOffset = Math.abs(modalCenterX - viewportCenterX);
const verticalOffset = Math.abs(modalCenterY - viewportCenterY);
const TOLERANCE = 10;
console.log(`\n📏 Offset Analysis:`);
console.log(` • Horizontal offset: ${horizontalOffset.toFixed(2)}px`);
console.log(` • Vertical offset: ${verticalOffset.toFixed(2)}px`);
console.log(` • Tolerance: ${TOLERANCE}px`);
const isHorizontallyCentered = horizontalOffset <= TOLERANCE;
const isVerticallyCentered = verticalOffset <= TOLERANCE;
console.log(`\n✅ Centering Results:`);
console.log(` • Horizontally centered: ${isHorizontallyCentered ? '✅ YES' : '❌ NO'}`);
console.log(` • Vertically centered: ${isVerticallyCentered ? '✅ YES' : '❌ NO'}`);
// Get CSS properties
const cssProps = await modal.evaluate(el => {
const style = window.getComputedStyle(el);
return {
position: style.position,
top: style.top,
left: style.left,
transform: style.transform
};
});
console.log(`\n🎨 CSS Properties:`);
console.log(` • position: ${cssProps.position}`);
console.log(` • top: ${cssProps.top}`);
console.log(` • left: ${cssProps.left}`);
console.log(` • transform: ${cssProps.transform}`);
// Overall test results
console.log(`\n📊 OVERALL RESULTS:`);
if (isHorizontallyCentered && isVerticallyCentered) {
console.log('✅ Modal is PROPERLY CENTERED on mobile!');
} else {
console.log('❌ Modal is NOT centered on mobile');
console.log(` Needs adjustment: ${!isHorizontallyCentered ? 'horizontal' : ''} ${!isVerticallyCentered ? 'vertical' : ''}`);
}
}
console.log('\n✅ Test completed successfully!\n');
} catch (error) {
console.error('❌ Test failed:', error.message);
console.error(error.stack);
process.exit(1);
} finally {
await browser.close();
}
})();