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
217 lines
7.1 KiB
JavaScript
Executable File
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();
|