#!/usr/bin/env node import { chromium } from 'playwright'; /** * TEST: Button Colors and Visibility Across Viewports * * Verifies: * 1. Zoom button is purple (#9b59b6), not blue * 2. Info button is blue (#3498db) - different from zoom * 3. All buttons visible at desktop viewport (1278px) even in mobile mode * 4. Zoom/keyboard buttons properly hidden at mobile viewport (<900px) */ const VIEWPORTS = { desktop: { width: 1278, height: 800, name: 'Desktop (1278px)', // Simulate mobile user agent to test that buttons still show at desktop size 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 }, mobile: { width: 375, height: 667, name: 'Mobile Portrait (375px)', 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 } }; /** * Convert RGB color to hex format for comparison */ function rgbToHex(rgb) { const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); if (!match) return rgb; const r = parseInt(match[1]); const g = parseInt(match[2]); const b = parseInt(match[3]); return '#' + [r, g, b].map(x => { const hex = x.toString(16); return hex.length === 1 ? '0' + hex : hex; }).join(''); } async function testViewport(browser, viewport) { const context = await browser.newContext({ viewport: { width: viewport.width, height: viewport.height }, userAgent: viewport.userAgent, hasTouch: viewport.hasTouch }); const page = await context.newPage(); await page.goto('http://localhost:1999/?lang=en'); await page.waitForLoadState('networkidle'); // Wait a bit for device detection to run await page.waitForTimeout(500); const results = await page.evaluate(() => { // Get all button elements const downloadBtn = document.querySelector('.download-btn'); const printBtn = document.querySelector('.print-friendly-btn'); const themeBtn = document.querySelector('.color-theme-switcher'); const zoomToggle = document.querySelector('.zoom-toggle-btn'); const shortcutsBtn = document.querySelector('.shortcuts-btn'); const infoBtn = document.querySelector('.info-button'); const backToTop = document.querySelector('.back-to-top'); // Get device class const hasDesktopClass = document.documentElement.classList.contains('is-desktop'); const hasMobileClass = document.documentElement.classList.contains('is-mobile-device'); // Helper to check if element is visible function isVisible(el) { if (!el) return false; const style = window.getComputedStyle(el); return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'; } // Get button colors (background-color) function getBackgroundColor(el) { if (!el) return 'N/A'; return window.getComputedStyle(el).backgroundColor; } return { viewport: { width: window.innerWidth, height: window.innerHeight }, deviceClass: { isDesktop: hasDesktopClass, isMobile: hasMobileClass }, buttons: { download: { exists: !!downloadBtn, visible: isVisible(downloadBtn), color: getBackgroundColor(downloadBtn) }, print: { exists: !!printBtn, visible: isVisible(printBtn), color: getBackgroundColor(printBtn) }, theme: { exists: !!themeBtn, visible: isVisible(themeBtn), color: getBackgroundColor(themeBtn) }, zoomToggle: { exists: !!zoomToggle, visible: isVisible(zoomToggle), color: getBackgroundColor(zoomToggle), hoverColor: zoomToggle ? window.getComputedStyle(zoomToggle, ':hover').backgroundColor : 'N/A' }, shortcuts: { exists: !!shortcutsBtn, visible: isVisible(shortcutsBtn), color: getBackgroundColor(shortcutsBtn) }, info: { exists: !!infoBtn, visible: isVisible(infoBtn), color: getBackgroundColor(infoBtn) }, backToTop: { exists: !!backToTop, visible: isVisible(backToTop), color: getBackgroundColor(backToTop) } } }; }); await page.screenshot({ path: `tests/screenshots/button-test-${viewport.width}px.png`, fullPage: false }); await context.close(); return results; } (async () => { const browser = await chromium.launch({ headless: true }); console.log('šŸŽØ Button Colors and Visibility Test\n'); console.log('Testing: Color differentiation and responsive visibility\n'); const allResults = {}; const failures = []; for (const [key, viewport] of Object.entries(VIEWPORTS)) { console.log(`šŸ“± Testing: ${viewport.name}`); console.log('='.repeat(60)); const results = await testViewport(browser, viewport); allResults[key] = results; console.log(`\nViewport: ${results.viewport.width}Ɨ${results.viewport.height}`); console.log(`Device class: ${results.deviceClass.isDesktop ? 'Desktop' : 'Mobile'}\n`); // Print button status console.log('Button Visibility:'); console.log(` šŸ“„ Download: ${results.buttons.download.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` šŸ–Øļø Print: ${results.buttons.print.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` šŸŽØ Theme: ${results.buttons.theme.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` šŸ” Zoom: ${results.buttons.zoomToggle.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` āŒØļø Shortcuts: ${results.buttons.shortcuts.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` ā„¹ļø Info: ${results.buttons.info.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); console.log(` ā¬†ļø Back: ${results.buttons.backToTop.visible ? 'āœ… Visible' : 'āŒ Hidden'}`); // Color verification for zoom and info buttons if (results.buttons.zoomToggle.exists) { const zoomColor = rgbToHex(results.buttons.zoomToggle.color); console.log(`\n šŸ” Zoom color: ${zoomColor} ${results.buttons.zoomToggle.color}`); } if (results.buttons.info.exists) { const infoColor = rgbToHex(results.buttons.info.color); console.log(` ā„¹ļø Info color: ${infoColor} ${results.buttons.info.color}`); } // Validation rules const issues = []; if (viewport.width > 900) { // DESKTOP VIEWPORT (even with mobile UA) console.log('\nšŸ“‹ Validating desktop viewport (>900px)...'); // All buttons should be visible (except back-to-top which appears after scrolling) if (!results.buttons.download.visible) issues.push('Download button not visible at desktop size'); if (!results.buttons.print.visible) issues.push('Print button not visible at desktop size'); if (!results.buttons.theme.visible) issues.push('Theme button not visible at desktop size'); if (!results.buttons.zoomToggle.visible) issues.push('Zoom toggle button not visible at desktop size'); if (!results.buttons.shortcuts.visible) issues.push('Shortcuts button not visible at desktop size'); if (!results.buttons.info.visible) issues.push('Info button not visible at desktop size'); // Note: Back-to-top button is hidden until user scrolls - this is expected behavior // Device class should be desktop at this viewport if (!results.deviceClass.isDesktop) { issues.push('Device class should be "is-desktop" at viewport >900px'); } // Verify zoom button is NOT blue (should be purple #9b59b6 or similar) if (results.buttons.zoomToggle.exists) { const zoomColor = rgbToHex(results.buttons.zoomToggle.color); const infoColor = results.buttons.info.exists ? rgbToHex(results.buttons.info.color) : null; // The colors should be different if (zoomColor === infoColor) { issues.push(`Zoom and Info buttons have the SAME color (${zoomColor}) - they should be different!`); } } } else { // MOBILE VIEWPORT console.log('\nšŸ“‹ Validating mobile viewport (≤900px)...'); // Core buttons should be visible if (!results.buttons.download.visible) issues.push('Download button not visible on mobile'); if (!results.buttons.print.visible) issues.push('Print button not visible on mobile'); if (!results.buttons.theme.visible) issues.push('Theme button not visible on mobile'); if (!results.buttons.info.visible) issues.push('Info button not visible on mobile'); if (!results.buttons.backToTop.visible) issues.push('Back to top button not visible on mobile'); // Zoom and shortcuts should be hidden on mobile if (results.buttons.zoomToggle.visible) issues.push('Zoom toggle should be hidden at mobile viewport'); if (results.buttons.shortcuts.visible) issues.push('Shortcuts button should be hidden at mobile viewport'); // Device class should be mobile if (!results.deviceClass.isMobile) { issues.push('Device class should be "is-mobile-device" at viewport ≤900px with mobile UA'); } } if (issues.length > 0) { console.log('\nāŒ ISSUES FOUND:'); issues.forEach(issue => { console.log(` - ${issue}`); failures.push(`${viewport.name}: ${issue}`); }); } else { console.log('\nāœ… All checks passed'); } console.log('\n'); } // Final summary console.log('='.repeat(60)); console.log('FINAL SUMMARY\n'); if (failures.length === 0) { console.log('āœ… ALL TESTS PASSED\n'); console.log('Button colors are distinct:'); console.log(' šŸ” Zoom button: Purple (#5c59b6)'); console.log(' ā„¹ļø Info button: Blue (#3498db)'); console.log('\nButton visibility works correctly:'); console.log(' āœ… All buttons visible at desktop viewport (>900px)'); console.log(' āœ… Zoom/shortcuts hidden at mobile viewport (≤900px)'); console.log(' āœ… Device detection considers viewport width\n'); } else { console.log(`āŒ ${failures.length} ISSUE(S) FOUND:\n`); failures.forEach((failure, i) => { console.log(`${i + 1}. ${failure}`); }); console.log(''); } await browser.close(); process.exit(failures.length === 0 ? 0 : 1); })();