#!/usr/bin/env node /** * Test: Button Hover Full Opacity + Footer Interaction * * Verifies on mobile view (max-width: 900px): * 1. All buttons become 100% opaque (alpha = 1.0) on hover * 2. Buttons become semi-transparent (opacity 0.2) when hovering footer */ import { chromium } from 'playwright'; const TEST_URL = 'http://localhost:1999'; const VIEWPORT_WIDTH = 375; // Mobile width const VIEWPORT_HEIGHT = 812; // iPhone X height // Helper to extract RGB values from background color function parseRGB(colorString) { const match = colorString.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); if (!match) return null; return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]), a: match[4] ? parseFloat(match[4]) : 1 }; } async function testButtonHoverAndFooter() { console.log('๐Ÿงช Testing Button Hover Opacity + Footer Interaction'); 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); const buttons = [ { selector: '.download-btn', name: 'Download' }, { selector: '.print-friendly-btn', name: 'Print' }, { selector: '.shortcuts-btn', name: 'Shortcuts' }, { selector: '.color-theme-switcher', name: 'Theme' }, { selector: '.info-button', name: 'Info' }, { selector: '.back-to-top', name: 'Back to Top' }, ]; let allTestsPassed = true; // TEST 1: All buttons reach 100% opacity on hover console.log('\n๐Ÿ“Š TEST 1: Button Hover - Full Opacity (100%)'); console.log('-'.repeat(70)); for (const button of buttons) { try { const buttonElement = await page.$(button.selector); if (!buttonElement) { console.log(`โŒ ${button.name}: Button not found`); allTestsPassed = false; continue; } // Hover over the button await buttonElement.hover(); await page.waitForTimeout(500); // Get hover background color const hoverBg = await page.evaluate((sel) => { const btn = document.querySelector(sel); return btn ? window.getComputedStyle(btn).backgroundColor : null; }, button.selector); const hoverColor = parseRGB(hoverBg); if (hoverColor) { const alphaIs100 = Math.abs(hoverColor.a - 1.0) < 0.1; if (alphaIs100) { console.log(`โœ… ${button.name}: Hover opacity = ${hoverColor.a.toFixed(2)} (100%) โœ“`); } else { console.log(`โŒ ${button.name}: Hover opacity = ${hoverColor.a.toFixed(2)} (Expected 1.0)`); allTestsPassed = false; } } else { console.log(`โŒ ${button.name}: Could not parse hover background color`); allTestsPassed = false; } // Move mouse away await page.mouse.move(0, 0); await page.waitForTimeout(300); } catch (error) { console.log(`โŒ ${button.name}: Error - ${error.message}`); allTestsPassed = false; } } // TEST 2: Footer hover makes buttons semi-transparent console.log('\n๐Ÿ“Š TEST 2: Footer Hover - Buttons Become Transparent (20%)'); console.log('-'.repeat(70)); try { // Scroll to footer await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); }); await page.waitForTimeout(500); // Find footer element const footer = await page.$('footer.no-print'); if (!footer) { console.log('โŒ Footer element not found'); allTestsPassed = false; } else { // Hover over footer await footer.hover(); await page.waitForTimeout(500); // Check if buttons have footer-hovered class and opacity 0.2 for (const button of buttons) { const buttonData = await page.evaluate((sel) => { const btn = document.querySelector(sel); if (!btn) return null; return { hasClass: btn.classList.contains('footer-hovered'), opacity: window.getComputedStyle(btn).opacity }; }, button.selector); if (buttonData) { const opacityValue = parseFloat(buttonData.opacity); const opacityCorrect = Math.abs(opacityValue - 0.2) < 0.05; if (buttonData.hasClass && opacityCorrect) { console.log(`โœ… ${button.name}: Footer hover opacity = ${opacityValue.toFixed(2)} โœ“`); } else { console.log(`โŒ ${button.name}: Footer hover opacity = ${opacityValue.toFixed(2)} (Expected 0.2), Class: ${buttonData.hasClass}`); allTestsPassed = false; } } else { console.log(`โŒ ${button.name}: Could not check footer hover state`); allTestsPassed = false; } } } } catch (error) { console.log(`โŒ Footer hover test error: ${error.message}`); allTestsPassed = false; } console.log('-'.repeat(70)); if (allTestsPassed) { console.log('\nโœ… ALL TESTS PASSED!'); console.log(' โ€ข All buttons reach 100% opacity on hover'); console.log(' โ€ข Buttons become 20% opacity when hovering footer'); } 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); } } testButtonHoverAndFooter();