#!/usr/bin/env node import { chromium } from 'playwright'; const MOBILE_VIEWPORT = { width: 375, height: 667 }; (async () => { const browser = await chromium.launch({ headless: true }); console.log('🧪 Testing Button Centering on Mobile\n'); // TEST 1: Android Portrait console.log('📱 TEST 1: Android Portrait (Pixel 5)\n'); const androidContext = await browser.newContext({ viewport: MOBILE_VIEWPORT, userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36', hasTouch: true }); const androidPage = await androidContext.newPage(); await androidPage.goto('http://localhost:1999/?lang=en&view=extended'); await androidPage.waitForLoadState('networkidle'); const androidButtons = await androidPage.evaluate(() => { const viewport = { width: window.innerWidth, height: window.innerHeight }; const buttons = { download: document.querySelector('.download-btn'), print: document.querySelector('.print-friendly-btn'), theme: document.querySelector('.color-theme-switcher'), info: document.querySelector('.info-button'), backToTop: document.querySelector('.back-to-top'), shortcuts: document.querySelector('.shortcuts-btn') }; const positions = {}; let leftmostButton = Infinity; let rightmostButton = 0; for (const [name, btn] of Object.entries(buttons)) { if (btn) { const rect = btn.getBoundingClientRect(); const computed = window.getComputedStyle(btn); const visible = computed.display !== 'none'; if (visible) { positions[name] = { left: Math.round(rect.left), right: Math.round(rect.right), centerX: Math.round(rect.left + rect.width / 2), visible: true }; leftmostButton = Math.min(leftmostButton, rect.left); rightmostButton = Math.max(rightmostButton, rect.right); } else { positions[name] = { visible: false }; } } } const buttonBarWidth = rightmostButton - leftmostButton; const buttonBarCenterX = leftmostButton + buttonBarWidth / 2; const viewportCenterX = viewport.width / 2; const centeringOffset = Math.abs(buttonBarCenterX - viewportCenterX); return { viewport, positions, buttonBarWidth: Math.round(buttonBarWidth), buttonBarCenterX: Math.round(buttonBarCenterX), viewportCenterX: Math.round(viewportCenterX), centeringOffset: Math.round(centeringOffset), isCentered: centeringOffset < 10 // Within 10px is acceptable }; }); console.log('Android Button Positions:'); Object.entries(androidButtons.positions).forEach(([name, pos]) => { if (pos.visible) { console.log(` • ${name}: ${pos.left}px to ${pos.right}px (center: ${pos.centerX}px)`); } else { console.log(` • ${name}: HIDDEN`); } }); console.log(`\nButton Bar Analysis:`); console.log(` • Button bar width: ${androidButtons.buttonBarWidth}px`); console.log(` • Button bar center: ${androidButtons.buttonBarCenterX}px`); console.log(` • Viewport center: ${androidButtons.viewportCenterX}px`); console.log(` • Centering offset: ${androidButtons.centeringOffset}px`); console.log(` • Is centered: ${androidButtons.isCentered ? '✅' : '❌'}\n`); await androidPage.screenshot({ path: 'tests/screenshots/buttons-android-portrait.png', fullPage: true }); await androidContext.close(); // TEST 2: iPhone Portrait console.log('📱 TEST 2: iPhone Portrait (iPhone 12)\n'); const iphoneContext = await browser.newContext({ viewport: MOBILE_VIEWPORT, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', hasTouch: true }); const iphonePage = await iphoneContext.newPage(); await iphonePage.goto('http://localhost:1999/?lang=en&view=extended'); await iphonePage.waitForLoadState('networkidle'); const iphoneButtons = await iphonePage.evaluate(() => { const viewport = { width: window.innerWidth, height: window.innerHeight }; const buttons = { download: document.querySelector('.download-btn'), print: document.querySelector('.print-friendly-btn'), theme: document.querySelector('.color-theme-switcher'), info: document.querySelector('.info-button'), backToTop: document.querySelector('.back-to-top') }; const positions = {}; let leftmostButton = Infinity; let rightmostButton = 0; for (const [name, btn] of Object.entries(buttons)) { if (btn) { const rect = btn.getBoundingClientRect(); const computed = window.getComputedStyle(btn); const visible = computed.display !== 'none'; if (visible) { positions[name] = { left: Math.round(rect.left), right: Math.round(rect.right), centerX: Math.round(rect.left + rect.width / 2), visible: true }; leftmostButton = Math.min(leftmostButton, rect.left); rightmostButton = Math.max(rightmostButton, rect.right); } else { positions[name] = { visible: false }; } } } const buttonBarWidth = rightmostButton - leftmostButton; const buttonBarCenterX = leftmostButton + buttonBarWidth / 2; const viewportCenterX = viewport.width / 2; const centeringOffset = Math.abs(buttonBarCenterX - viewportCenterX); return { viewport, positions, buttonBarWidth: Math.round(buttonBarWidth), buttonBarCenterX: Math.round(buttonBarCenterX), viewportCenterX: Math.round(viewportCenterX), centeringOffset: Math.round(centeringOffset), isCentered: centeringOffset < 10 }; }); console.log('iPhone Button Positions:'); Object.entries(iphoneButtons.positions).forEach(([name, pos]) => { if (pos.visible) { console.log(` • ${name}: ${pos.left}px to ${pos.right}px (center: ${pos.centerX}px)`); } else { console.log(` • ${name}: HIDDEN`); } }); console.log(`\nButton Bar Analysis:`); console.log(` • Button bar width: ${iphoneButtons.buttonBarWidth}px`); console.log(` • Button bar center: ${iphoneButtons.buttonBarCenterX}px`); console.log(` • Viewport center: ${iphoneButtons.viewportCenterX}px`); console.log(` • Centering offset: ${iphoneButtons.centeringOffset}px`); console.log(` • Is centered: ${iphoneButtons.isCentered ? '✅' : '❌'}\n`); await iphonePage.screenshot({ path: 'tests/screenshots/buttons-iphone-portrait.png', fullPage: true }); await iphoneContext.close(); const allPassed = androidButtons.isCentered && iphoneButtons.isCentered; console.log(`${allPassed ? '✅' : '❌'} Tests ${allPassed ? 'PASSED' : 'FAILED'}\n`); await browser.close(); process.exit(allPassed ? 0 : 1); })();