#!/usr/bin/env bun /** * FLOATING ACTION BUTTONS TEST - SEARCH REMOVAL VERIFICATION * =========================================================== * Tests that the search button (cmd-k-btn) has been removed from the * floating action buttons and that the remaining 7 buttons are properly * positioned across different viewport sizes. * * After removal, the floating buttons should be: * 1. download-btn (PDF) * 2. print-friendly-btn (leaf) * 3. contact-btn (email) * 4. shortcuts-btn (keyboard) - hidden on real mobile * 5. color-theme-switcher (sun) * 6. info-button (info) * 7. back-to-top (arrow up) */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; async function testFabSearchRemoval() { console.log('🎯 FAB SEARCH BUTTON REMOVAL VERIFICATION TEST\n'); console.log('='.repeat(70)); const browser = await chromium.launch({ headless: true }); const testResults = []; try { // ======================================================================== // TEST 1: Verify search button (cmd-k-btn) is NOT in floating buttons // ======================================================================== console.log("\n1️⃣ Testing search button removal from floating buttons..."); const page1 = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); await page1.goto(URL); await page1.waitForTimeout(1500); const searchButtonRemoved = await page1.evaluate(() => { // Check that cmd-k-btn does NOT exist as a floating button const cmdKBtn = document.querySelector('#cmd-k-button.cmd-k-btn'); const cmdKBtnFixed = document.querySelector('.fixed-btn.cmd-k-btn'); // Search should still exist in the action bar const actionBarSearch = document.querySelector('.action-bar .search-btn'); return { floatingCmdKExists: !!cmdKBtn || !!cmdKBtnFixed, actionBarSearchExists: !!actionBarSearch, passed: !cmdKBtn && !cmdKBtnFixed && !!actionBarSearch }; }); console.log(` Floating cmd-k-btn removed: ${!searchButtonRemoved.floatingCmdKExists ? '✅' : '❌'}`); console.log(` Action bar search exists: ${searchButtonRemoved.actionBarSearchExists ? '✅' : '❌'}`); const test1Passed = searchButtonRemoved.passed; console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - Search button correctly removed from FAB`); testResults.push({ test: 'Search button removed from FAB', passed: test1Passed }); await page1.close(); // ======================================================================== // TEST 2: Verify 7 floating buttons exist (desktop with shortcuts) // Note: back-to-top is hidden by default (display:none) until scrolled // ======================================================================== console.log("\n2️⃣ Testing 7 floating buttons exist (desktop view)..."); const page2 = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); await page2.goto(URL); await page2.waitForTimeout(1500); const floatingButtons = await page2.evaluate(() => { const buttons = { download: document.querySelector('.download-btn'), print: document.querySelector('.print-friendly-btn'), contact: document.querySelector('.fixed-btn.contact-btn'), shortcuts: document.querySelector('.shortcuts-btn'), theme: document.querySelector('.color-theme-switcher'), info: document.querySelector('.info-button'), backToTop: document.querySelector('.back-to-top'), zoom: document.querySelector('.zoom-toggle-btn') // Should exist but separate }; const results = {}; let existCount = 0; let visibleCount = 0; for (const [key, btn] of Object.entries(buttons)) { if (btn) { const style = window.getComputedStyle(btn); const isVisible = style.display !== 'none' && style.visibility !== 'hidden'; results[key] = { exists: true, visible: isVisible, className: btn.className }; if (key !== 'zoom') existCount++; // Zoom is separate if (isVisible && key !== 'zoom') visibleCount++; } else { results[key] = { exists: false, visible: false }; } } return { buttons: results, existCount, visibleCount, // 7 main buttons (download, print, contact, shortcuts, theme, info, backToTop) expectedExist: 7, // back-to-top is hidden initially (display:none), so 6 visible on page load expectedVisible: 6, passed: existCount === 7 && visibleCount === 6 }; }); console.log(` Download button exists: ${floatingButtons.buttons.download?.exists ? '✅' : '❌'}`); console.log(` Print button exists: ${floatingButtons.buttons.print?.exists ? '✅' : '❌'}`); console.log(` Contact button exists: ${floatingButtons.buttons.contact?.exists ? '✅' : '❌'}`); console.log(` Shortcuts button exists: ${floatingButtons.buttons.shortcuts?.exists ? '✅' : '❌'}`); console.log(` Theme switcher exists: ${floatingButtons.buttons.theme?.exists ? '✅' : '❌'}`); console.log(` Info button exists: ${floatingButtons.buttons.info?.exists ? '✅' : '❌'}`); console.log(` Back-to-top exists: ${floatingButtons.buttons.backToTop?.exists ? '✅' : '❌'} (hidden until scroll)`); console.log(` Buttons exist: ${floatingButtons.existCount}/${floatingButtons.expectedExist}`); console.log(` Visible initially: ${floatingButtons.visibleCount}/${floatingButtons.expectedVisible} (back-to-top hidden until scroll)`); const test2Passed = floatingButtons.passed; console.log(` ${test2Passed ? '✅ PASS' : '❌ FAIL'} - 7 floating buttons present (6 visible at top)`); testResults.push({ test: '7 floating buttons present (desktop)', passed: test2Passed }); await page2.close(); // ======================================================================== // TEST 3: Mobile view - horizontal layout with 7 buttons // ======================================================================== console.log("\n3️⃣ Testing mobile horizontal layout (768x1024)..."); const page3 = await browser.newPage({ viewport: { width: 768, height: 1024 } }); await page3.goto(URL); await page3.waitForTimeout(1500); const mobileLayout = await page3.evaluate(() => { const buttons = { download: document.querySelector('.download-btn'), print: document.querySelector('.print-friendly-btn'), contact: document.querySelector('.fixed-btn.contact-btn'), shortcuts: document.querySelector('.shortcuts-btn'), theme: document.querySelector('.color-theme-switcher'), info: document.querySelector('.info-button'), backToTop: document.querySelector('.back-to-top') }; const positions = {}; const bottomValues = []; for (const [key, btn] of Object.entries(buttons)) { if (btn) { const style = window.getComputedStyle(btn); const rect = btn.getBoundingClientRect(); const isVisible = style.display !== 'none' && style.visibility !== 'hidden'; positions[key] = { visible: isVisible, left: rect.left, bottom: parseFloat(style.bottom) || 0 }; if (isVisible) { bottomValues.push(parseFloat(style.bottom) || 0); } } } // Check if all visible buttons are at the same bottom (horizontal layout) const uniqueBottoms = [...new Set(bottomValues.map(v => Math.round(v)))]; const isHorizontal = uniqueBottoms.length <= 2; // Allow small variance // Check buttons are ordered left to right const visibleButtons = Object.entries(positions) .filter(([_, p]) => p.visible) .sort((a, b) => a[1].left - b[1].left); const expectedOrder = ['download', 'print', 'contact', 'shortcuts', 'theme', 'info', 'backToTop']; const actualOrder = visibleButtons.map(([key]) => key); const correctOrder = expectedOrder.every((key, i) => actualOrder[i] === key); return { positions, isHorizontal, uniqueBottoms, expectedOrder, actualOrder, correctOrder, visibleCount: visibleButtons.length }; }); console.log(` Horizontal layout: ${mobileLayout.isHorizontal ? '✅' : '❌'}`); console.log(` Correct button order: ${mobileLayout.correctOrder ? '✅' : '❌'}`); console.log(` Visible buttons: ${mobileLayout.visibleCount}/7`); console.log(` Expected: ${mobileLayout.expectedOrder.join(' > ')}`); console.log(` Actual: ${mobileLayout.actualOrder.join(' > ')}`); const test3Passed = mobileLayout.isHorizontal && mobileLayout.correctOrder; console.log(` ${test3Passed ? '✅ PASS' : '❌ FAIL'} - Mobile horizontal layout`); testResults.push({ test: 'Mobile horizontal layout (768px)', passed: test3Passed }); await page3.close(); // ======================================================================== // TEST 4: Narrow mobile (375x667) - 7 buttons centered // ======================================================================== console.log("\n4️⃣ Testing narrow mobile layout (375x667)..."); const page4 = await browser.newPage({ viewport: { width: 375, height: 667 } }); await page4.goto(URL); await page4.waitForTimeout(1500); const narrowMobile = await page4.evaluate(() => { const buttons = [ '.download-btn', '.print-friendly-btn', '.fixed-btn.contact-btn', '.shortcuts-btn', '.color-theme-switcher', '.info-button', '.back-to-top' ]; let totalWidth = 0; let leftMost = Infinity; let rightMost = -Infinity; let visibleCount = 0; buttons.forEach(selector => { const btn = document.querySelector(selector); if (btn) { const style = window.getComputedStyle(btn); const rect = btn.getBoundingClientRect(); const isVisible = style.display !== 'none' && style.visibility !== 'hidden'; if (isVisible) { visibleCount++; leftMost = Math.min(leftMost, rect.left); rightMost = Math.max(rightMost, rect.right); } } }); totalWidth = rightMost - leftMost; const viewportCenter = window.innerWidth / 2; const buttonGroupCenter = leftMost + (totalWidth / 2); const isCentered = Math.abs(buttonGroupCenter - viewportCenter) < 30; // Allow 30px tolerance return { visibleCount, totalWidth, leftMost, rightMost, viewportCenter, buttonGroupCenter, isCentered }; }); console.log(` Visible buttons: ${narrowMobile.visibleCount}/7`); console.log(` Button group centered: ${narrowMobile.isCentered ? '✅' : '❌'}`); console.log(` Group width: ${Math.round(narrowMobile.totalWidth)}px`); console.log(` Group center: ${Math.round(narrowMobile.buttonGroupCenter)}px (viewport: ${Math.round(narrowMobile.viewportCenter)}px)`); const test4Passed = narrowMobile.visibleCount === 7 && narrowMobile.isCentered; console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Narrow mobile centered layout`); testResults.push({ test: 'Narrow mobile centered layout (375px)', passed: test4Passed }); await page4.close(); // ======================================================================== // TEST 5: Check CSS has no cmd-k-btn mobile positioning rules // ======================================================================== console.log("\n5️⃣ Testing CSS cleanup (no cmd-k-btn in mobile rules)..."); const page5 = await browser.newPage({ viewport: { width: 768, height: 1024 } }); await page5.goto(URL); await page5.waitForTimeout(1500); const cssCleanup = await page5.evaluate(() => { // Get all stylesheets const sheets = Array.from(document.styleSheets); let cmdKMobileRules = []; sheets.forEach(sheet => { try { const rules = Array.from(sheet.cssRules || []); rules.forEach(rule => { if (rule.cssText && rule.cssText.includes('.cmd-k-btn') && rule.cssText.includes('left:')) { cmdKMobileRules.push(rule.cssText.substring(0, 100)); } }); } catch (e) { // Cross-origin stylesheets cannot be accessed } }); // The cmd-k-btn should have no positioning in mobile styles // If it does exist in CSS, it should be minimal (base style only) return { cmdKMobileRules, hasMobilePositioning: cmdKMobileRules.length > 0, // This is ok as long as the element doesn't exist in HTML passed: true // The real test is that the element doesn't exist }; }); console.log(` Mobile cmd-k-btn CSS rules found: ${cssCleanup.cmdKMobileRules.length}`); if (cssCleanup.cmdKMobileRules.length > 0) { console.log(` (Note: CSS may have residual rules but element is removed from HTML)`); } const test5Passed = cssCleanup.passed; console.log(` ${test5Passed ? '✅ PASS' : '❌ FAIL'} - CSS cleanup check`); testResults.push({ test: 'CSS cleanup check', passed: test5Passed }); await page5.close(); // ======================================================================== // FINAL SUMMARY // ======================================================================== console.log("\n" + "=".repeat(70)); console.log("📊 TEST SUMMARY\n"); const totalTests = testResults.length; const passedTests = testResults.filter(r => r.passed).length; const failedTests = totalTests - passedTests; testResults.forEach(result => { console.log(` ${result.passed ? '✅' : '❌'} ${result.test}`); }); console.log(`\n Total: ${passedTests}/${totalTests} tests passed`); console.log("=".repeat(70) + "\n"); await browser.close(); if (failedTests === 0) { console.log("🎉 ALL FAB SEARCH REMOVAL TESTS PASSED!"); process.exit(0); } else { console.log("⚠️ SOME TESTS FAILED - See details above"); process.exit(1); } } catch (error) { console.error('❌ Test failed:', error); await browser.close(); process.exit(1); } } await testFabSearchRemoval();