#!/usr/bin/env node /** * Test: Info Modal Mobile Font Sizes * Purpose: Verify that all font sizes in the info modal are appropriately sized for mobile * Date: 2025-11-27 */ import { launch } from 'puppeteer'; const BASE_URL = process.env.CV_TEST_URL || 'http://localhost:1999'; const MOBILE_WIDTH = 375; // iPhone SE width const MOBILE_HEIGHT = 667; // iPhone SE height async function testInfoModalMobileFonts() { console.log('๐Ÿงช Testing Info Modal Mobile Font Sizes...\n'); const browser = await launch({ headless: false, // Show browser for visual verification args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); // Set mobile viewport await page.setViewport({ width: MOBILE_WIDTH, height: MOBILE_HEIGHT, isMobile: true, hasTouch: true }); try { // Navigate to the CV page console.log(`๐Ÿ“ฑ Navigating to ${BASE_URL}?lang=en (Mobile: ${MOBILE_WIDTH}x${MOBILE_HEIGHT})`); await page.goto(`${BASE_URL}?lang=en`, { waitUntil: 'networkidle0' }); // Wait for info button to be visible await page.waitForSelector('#info-button', { visible: true }); console.log('โœ… Info button found'); // Click the info button to open modal console.log('๐Ÿ–ฑ๏ธ Opening info modal...'); await page.click('#info-button'); // Wait for modal to appear await page.waitForSelector('#info-modal[open]', { visible: true, timeout: 3000 }); console.log('โœ… Info modal opened'); // Wait a bit for animation await new Promise(resolve => setTimeout(resolve, 500)); // Take screenshot of the modal await page.screenshot({ path: '/Users/txeo/Git/yo/cv/tests/screenshots/info-modal-mobile-fonts.png', fullPage: false }); console.log('๐Ÿ“ธ Screenshot saved: tests/screenshots/info-modal-mobile-fonts.png'); // Check font sizes of key elements const fontSizes = await page.evaluate(() => { const getComputedFontSize = (selector) => { const element = document.querySelector(selector); if (!element) return null; return window.getComputedStyle(element).fontSize; }; return { title: getComputedFontSize('.info-modal-header h2'), cvTitle: getComputedFontSize('.info-modal-cv-title'), description: getComputedFontSize('.info-modal-description'), techItem: getComputedFontSize('.info-tech-item span'), githubButton: getComputedFontSize('.info-modal-github'), githubSubtext: getComputedFontSize('.info-modal-github-subtext'), bracket: getComputedFontSize('.photo-bracket-wrapper::before') || getComputedFontSize('.photo-bracket-wrapper'), closeButton: (() => { const btn = document.querySelector('.info-modal-close'); if (!btn) return null; return `${btn.offsetWidth}x${btn.offsetHeight}`; })() }; }); console.log('\n๐Ÿ“ Font Sizes on Mobile:'); console.log(' โ€ข Title (h2):', fontSizes.title, '(expected: ~17.6px / 1.1rem)'); console.log(' โ€ข CV Title:', fontSizes.cvTitle, '(expected: 16px / 1rem)'); console.log(' โ€ข Description:', fontSizes.description, '(expected: ~13.6px / 0.85rem)'); console.log(' โ€ข Tech Items:', fontSizes.techItem, '(expected: ~12.8px / 0.8rem)'); console.log(' โ€ข GitHub Button:', fontSizes.githubButton, '(expected: 14px / 0.875rem)'); console.log(' โ€ข GitHub Subtext:', fontSizes.githubSubtext, '(expected: ~12.8px / 0.8rem)'); console.log(' โ€ข Close Button Size:', fontSizes.closeButton, '(expected: 32x32)'); // Check modal dimensions const modalDimensions = await page.evaluate(() => { const modal = document.querySelector('#info-modal'); const content = document.querySelector('.info-modal-content'); if (!modal || !content) return null; const modalRect = modal.getBoundingClientRect(); const contentRect = content.getBoundingClientRect(); return { modal: { width: Math.round(modalRect.width), height: Math.round(modalRect.height), top: Math.round(modalRect.top), left: Math.round(modalRect.left) }, content: { width: Math.round(contentRect.width), height: Math.round(contentRect.height), padding: window.getComputedStyle(content).padding } }; }); console.log('\n๐Ÿ“ Modal Dimensions:'); console.log(' โ€ข Modal:', `${modalDimensions.modal.width}x${modalDimensions.modal.height}px`); console.log(' โ€ข Position:', `top: ${modalDimensions.modal.top}px, left: ${modalDimensions.modal.left}px`); console.log(' โ€ข Content:', `${modalDimensions.content.width}x${modalDimensions.content.height}px`); console.log(' โ€ข Content Padding:', modalDimensions.content.padding); console.log(' โ€ข Viewport:', `${MOBILE_WIDTH}x${MOBILE_HEIGHT}px`); // Check if modal fits within viewport const fitsInViewport = modalDimensions.modal.width <= MOBILE_WIDTH && modalDimensions.modal.height <= MOBILE_HEIGHT; console.log('\n๐ŸŽฏ Modal Fit Test:'); if (fitsInViewport) { console.log(' โœ… Modal fits within mobile viewport'); } else { console.log(' โŒ Modal DOES NOT fit within viewport!'); console.log(` Width: ${modalDimensions.modal.width} > ${MOBILE_WIDTH} = ${modalDimensions.modal.width > MOBILE_WIDTH}`); console.log(` Height: ${modalDimensions.modal.height} > ${MOBILE_HEIGHT} = ${modalDimensions.modal.height > MOBILE_HEIGHT}`); } // Check for overflow const hasOverflow = await page.evaluate(() => { const content = document.querySelector('.info-modal-content'); if (!content) return false; return content.scrollHeight > content.clientHeight; }); console.log(' โ€ข Content Overflow:', hasOverflow ? 'โš ๏ธ Yes (scrollable)' : 'โœ… No'); // Parse font sizes and validate they're smaller than desktop const parsePx = (sizeStr) => parseFloat(sizeStr); const titlePx = parsePx(fontSizes.title); const cvTitlePx = parsePx(fontSizes.cvTitle); const descriptionPx = parsePx(fontSizes.description); const techItemPx = parsePx(fontSizes.techItem); console.log('\nโœ… Validation:'); console.log(' โ€ข Title < 20px:', titlePx < 20 ? 'โœ…' : 'โŒ', `(${titlePx.toFixed(1)}px)`); console.log(' โ€ข CV Title โ‰ˆ 16px:', Math.abs(cvTitlePx - 16) < 2 ? 'โœ…' : 'โŒ', `(${cvTitlePx.toFixed(1)}px)`); console.log(' โ€ข Description < 15px:', descriptionPx < 15 ? 'โœ…' : 'โŒ', `(${descriptionPx.toFixed(1)}px)`); console.log(' โ€ข Tech Items โ‰ˆ 12.8px:', Math.abs(techItemPx - 12.8) < 2 ? 'โœ…' : 'โŒ', `(${techItemPx.toFixed(1)}px)`); // Keep browser open for manual inspection console.log('\n๐Ÿ‘€ Browser kept open for visual inspection...'); console.log(' Press Ctrl+C to close when done.\n'); // Wait indefinitely await new Promise(() => {}); } catch (error) { console.error('โŒ Test failed:', error.message); await browser.close(); process.exit(1); } } testInfoModalMobileFonts();