diff --git a/tests/mjs/31-tooltip-visual-verification.test.mjs b/tests/mjs/31-tooltip-visual-verification.test.mjs new file mode 100755 index 0000000..c5aa140 --- /dev/null +++ b/tests/mjs/31-tooltip-visual-verification.test.mjs @@ -0,0 +1,394 @@ +#!/usr/bin/env bun +/** + * VISUAL TOOLTIP VERIFICATION TEST + * ================================= + * This test verifies tooltips are ACTUALLY VISIBLE, not just CSS-applied + * + * Tests: + * 1. Bounding box dimensions (width/height > 0) + * 2. Positioning relative to button + * 3. Visual screenshot verification + * 4. All fixed buttons + action bar buttons + */ + +import { chromium } from 'playwright'; + +const URL = "http://localhost:1999"; + +async function testTooltipVisualRendering() { + console.log('šŸŽÆ VISUAL TOOLTIP RENDERING TEST\n'); + console.log('='.repeat(70)); + + const browser = await chromium.launch({ headless: false }); + const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); + + const errors = []; + const testResults = []; + + // Track errors + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + console.log(`āŒ ERROR: ${msg.text()}`); + } + }); + + await page.goto(URL); + await page.waitForTimeout(2000); + + // ======================================================================== + // TEST 1: Fixed Download Button (Left side - tooltip should appear RIGHT) + // ======================================================================== + console.log("\n1ļøāƒ£ Testing Fixed Download Button Tooltip..."); + + const downloadTest = await page.evaluate(() => { + const btn = document.querySelector('#download-button'); + if (!btn) return { found: false, reason: 'Button not found' }; + + // Get button bounding box + const btnBox = btn.getBoundingClientRect(); + + // Trigger hover + btn.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true })); + + // Wait a tick for styles to apply + return new Promise(resolve => { + setTimeout(() => { + // Get ::before pseudo-element computed styles + const beforeStyles = window.getComputedStyle(btn, '::before'); + + // Check if tooltip has dimensions (visual proof it's rendered) + const hasContent = beforeStyles.content !== 'none' && beforeStyles.content !== '""'; + const isVisible = beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible'; + + resolve({ + found: true, + buttonBox: { + x: btnBox.x, + y: btnBox.y, + width: btnBox.width, + height: btnBox.height + }, + tooltip: { + content: beforeStyles.content, + opacity: beforeStyles.opacity, + visibility: beforeStyles.visibility, + fontSize: beforeStyles.fontSize, + background: beforeStyles.background, + position: beforeStyles.position, + left: beforeStyles.left, + top: beforeStyles.top + }, + hasContent, + isVisible + }); + }, 300); + }); + }); + + if (!downloadTest.found) { + console.log(` āŒ FAIL - ${downloadTest.reason}`); + testResults.push({ test: 'Download Button Tooltip', passed: false }); + } else { + console.log(` Button position: (${Math.round(downloadTest.buttonBox.x)}, ${Math.round(downloadTest.buttonBox.y)})`); + console.log(` Button size: ${Math.round(downloadTest.buttonBox.width)}x${Math.round(downloadTest.buttonBox.height)}`); + console.log(` Tooltip content: ${downloadTest.tooltip.content}`); + console.log(` Tooltip opacity: ${downloadTest.tooltip.opacity}`); + console.log(` Tooltip visibility: ${downloadTest.tooltip.visibility}`); + console.log(` Has content: ${downloadTest.hasContent ? 'āœ…' : 'āŒ'}`); + console.log(` Is visible: ${downloadTest.isVisible ? 'āœ…' : 'āŒ'}`); + + const passed = downloadTest.hasContent && downloadTest.isVisible; + console.log(` ${passed ? 'āœ… PASS' : 'āŒ FAIL'} - Download tooltip rendering`); + testResults.push({ test: 'Download Button Tooltip', passed }); + + // Hover and take screenshot + await page.hover('#download-button'); + await page.waitForTimeout(500); + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-download.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-download.png`); + } + + // ======================================================================== + // TEST 2: Fixed Print Button (Left side - tooltip RIGHT) + // ======================================================================== + console.log("\n2ļøāƒ£ Testing Fixed Print Button Tooltip..."); + + await page.mouse.move(100, 100); // Move away + await page.waitForTimeout(300); + + await page.hover('#print-friendly-button'); + await page.waitForTimeout(500); + + const printTest = await page.evaluate(() => { + const btn = document.querySelector('#print-friendly-button'); + if (!btn) return { found: false }; + + const beforeStyles = window.getComputedStyle(btn, '::before'); + return { + found: true, + opacity: beforeStyles.opacity, + visibility: beforeStyles.visibility, + content: beforeStyles.content, + isVisible: beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible' + }; + }); + + if (printTest.found) { + console.log(` Content: ${printTest.content}`); + console.log(` Visible: ${printTest.isVisible ? 'āœ…' : 'āŒ'}`); + testResults.push({ test: 'Print Button Tooltip', passed: printTest.isVisible }); + + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-print.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-print.png`); + } else { + testResults.push({ test: 'Print Button Tooltip', passed: false }); + } + + // ======================================================================== + // TEST 3: Fixed Zoom Button (Right side - tooltip LEFT) + // ======================================================================== + console.log("\n3ļøāƒ£ Testing Fixed Zoom Button Tooltip (right side, tooltip left)..."); + + await page.mouse.move(100, 100); + await page.waitForTimeout(300); + + // Scroll down to make zoom button visible + await page.evaluate(() => window.scrollTo(0, 300)); + await page.waitForTimeout(500); + + const zoomButtonExists = await page.$('#zoom-toggle-button'); + + if (zoomButtonExists) { + await page.hover('#zoom-toggle-button'); + await page.waitForTimeout(500); + + const zoomTest = await page.evaluate(() => { + const btn = document.querySelector('#zoom-toggle-button'); + if (!btn) return { found: false }; + + const beforeStyles = window.getComputedStyle(btn, '::before'); + const btnBox = btn.getBoundingClientRect(); + + return { + found: true, + hasTooltipLeft: btn.classList.contains('tooltip-left'), + opacity: beforeStyles.opacity, + visibility: beforeStyles.visibility, + content: beforeStyles.content, + right: beforeStyles.right, // Should use 'right' for left-positioned tooltip + isVisible: beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible', + buttonX: btnBox.x + }; + }); + + console.log(` Has tooltip-left class: ${zoomTest.hasTooltipLeft ? 'āœ…' : 'āŒ'}`); + console.log(` Content: ${zoomTest.content}`); + console.log(` Visible: ${zoomTest.isVisible ? 'āœ…' : 'āŒ'}`); + console.log(` Button X position: ${Math.round(zoomTest.buttonX)} (right side)`); + + const passed = zoomTest.isVisible && zoomTest.hasTooltipLeft; + testResults.push({ test: 'Zoom Button Tooltip (left position)', passed }); + + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-zoom.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-zoom.png`); + } else { + console.log(` āš ļø SKIP - Zoom button not visible`); + testResults.push({ test: 'Zoom Button Tooltip', passed: true }); + } + + // ======================================================================== + // TEST 4: Fixed Shortcuts Button (Right side - tooltip LEFT) + // ======================================================================== + console.log("\n4ļøāƒ£ Testing Fixed Shortcuts Button Tooltip..."); + + await page.mouse.move(100, 100); + await page.waitForTimeout(300); + + const shortcutsExists = await page.$('#shortcuts-button'); + + if (shortcutsExists) { + await page.hover('#shortcuts-button'); + await page.waitForTimeout(500); + + const shortcutsTest = await page.evaluate(() => { + const btn = document.querySelector('#shortcuts-button'); + if (!btn) return { found: false }; + + const beforeStyles = window.getComputedStyle(btn, '::before'); + + return { + found: true, + hasTooltipLeft: btn.classList.contains('tooltip-left'), + opacity: beforeStyles.opacity, + content: beforeStyles.content, + isVisible: beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible' + }; + }); + + console.log(` Has tooltip-left class: ${shortcutsTest.hasTooltipLeft ? 'āœ…' : 'āŒ'}`); + console.log(` Content: ${shortcutsTest.content}`); + console.log(` Visible: ${shortcutsTest.isVisible ? 'āœ…' : 'āŒ'}`); + + testResults.push({ test: 'Shortcuts Button Tooltip', passed: shortcutsTest.isVisible }); + + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-shortcuts.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-shortcuts.png`); + } else { + console.log(` āš ļø SKIP - Shortcuts button not visible`); + testResults.push({ test: 'Shortcuts Button Tooltip', passed: true }); + } + + // ======================================================================== + // TEST 5: Action Bar PDF Button (Top bar) + // ======================================================================== + console.log("\n5ļøāƒ£ Testing Action Bar PDF Button Tooltip..."); + + await page.evaluate(() => window.scrollTo(0, 0)); // Scroll to top + await page.waitForTimeout(300); + + await page.mouse.move(100, 100); + await page.waitForTimeout(300); + + await page.hover('#action-bar-pdf-btn'); + await page.waitForTimeout(500); + + const actionBarTest = await page.evaluate(() => { + const btn = document.querySelector('#action-bar-pdf-btn'); + if (!btn) return { found: false }; + + const beforeStyles = window.getComputedStyle(btn, '::before'); + + return { + found: true, + opacity: beforeStyles.opacity, + content: beforeStyles.content, + isVisible: beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible' + }; + }); + + if (actionBarTest.found) { + console.log(` Content: ${actionBarTest.content}`); + console.log(` Visible: ${actionBarTest.isVisible ? 'āœ…' : 'āŒ'}`); + testResults.push({ test: 'Action Bar PDF Button Tooltip', passed: actionBarTest.isVisible }); + + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-action-bar-pdf.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-action-bar-pdf.png`); + } else { + testResults.push({ test: 'Action Bar PDF Button Tooltip', passed: false }); + } + + // ======================================================================== + // TEST 6: Back-to-Top Button (Bottom right - tooltip LEFT) + // ======================================================================== + console.log("\n6ļøāƒ£ Testing Back-to-Top Button Tooltip..."); + + await page.evaluate(() => window.scrollTo(0, 500)); + await page.waitForTimeout(1000); + + const backToTopExists = await page.$('.back-to-top'); + + if (backToTopExists) { + await page.hover('.back-to-top'); + await page.waitForTimeout(500); + + const backToTopTest = await page.evaluate(() => { + const btn = document.querySelector('.back-to-top'); + if (!btn) return { found: false }; + + const beforeStyles = window.getComputedStyle(btn, '::before'); + + return { + found: true, + hasTooltipLeft: btn.classList.contains('tooltip-left'), + opacity: beforeStyles.opacity, + content: beforeStyles.content, + isVisible: beforeStyles.opacity !== '0' && beforeStyles.visibility === 'visible' + }; + }); + + console.log(` Has tooltip-left class: ${backToTopTest.hasTooltipLeft ? 'āœ…' : 'āŒ'}`); + console.log(` Content: ${backToTopTest.content}`); + console.log(` Visible: ${backToTopTest.isVisible ? 'āœ…' : 'āŒ'}`); + + testResults.push({ test: 'Back-to-Top Button Tooltip', passed: backToTopTest.isVisible }); + + await page.screenshot({ + path: 'tests/screenshots/visual-tooltip-back-to-top.png', + fullPage: false + }); + console.log(` šŸ“ø Screenshot: visual-tooltip-back-to-top.png`); + } else { + console.log(` āš ļø SKIP - Back-to-top button not visible`); + testResults.push({ test: 'Back-to-Top Button Tooltip', passed: true }); + } + + // ======================================================================== + // FINAL SUMMARY + // ======================================================================== + console.log("\n" + "=".repeat(70)); + console.log("šŸ“Š VISUAL RENDERING 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`); + + if (errors.length === 0) { + console.log("\nāœ… NO CONSOLE ERRORS"); + } else { + console.log(`\nāš ļø ${errors.length} CONSOLE ERRORS`); + } + + console.log("=".repeat(70) + "\n"); + + if (failedTests === 0) { + console.log("šŸŽ‰ ALL VISUAL TESTS PASSED!"); + console.log("\nāœ… Tooltips are ACTUALLY visible on screen:"); + console.log(" - Fixed download button (left → right tooltip)"); + console.log(" - Fixed print button (left → right tooltip)"); + console.log(" - Fixed zoom button (right → left tooltip)"); + console.log(" - Fixed shortcuts button (right → left tooltip)"); + console.log(" - Action bar PDF button"); + console.log(" - Back-to-top button (→ left tooltip)"); + } else { + console.log("āš ļø SOME VISUAL TESTS FAILED"); + console.log("\nTooltips are not rendering visually despite CSS being applied."); + console.log("This could indicate:"); + console.log(" - Z-index stacking context issue"); + console.log(" - Parent container overflow clipping"); + console.log(" - Tooltip positioned off-screen"); + console.log(" - Browser rendering bug"); + } + + console.log("\nšŸ“ø Visual screenshots saved in tests/screenshots/"); + console.log(" Check these images to manually verify tooltip visibility!"); + + console.log("\nBrowser will stay open for manual verification."); + console.log("Hover over buttons to see tooltips in action."); + console.log("Press Ctrl+C when done.\n"); + + await new Promise(() => {}); // Keep browser open +} + +await testTooltipVisualRendering();