import { chromium } from '@playwright/test'; import fs from 'fs'; import path from 'path'; const screenshotsDir = '/tmp/screenshots/final-visual-test'; fs.mkdirSync(screenshotsDir, { recursive: true }); async function runTests() { console.log('========================================'); console.log(' Novalon Homepage Visual Test'); console.log('========================================'); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1080 }, }); const page = await context.newPage(); console.log('\n1. Loading homepage...'); await page.goto('http://localhost:3000', { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(2000); console.log(' ✓ Page loaded'); console.log('\n2. Capturing full page screenshot...'); await page.screenshot({ path: path.join(screenshotsDir, '01-home-full.png'), fullPage: true, }); console.log(' ✓ Full page captured'); const sections = [ { name: '02-hero', scrollY: 0, desc: 'Hero Section' }, { name: '03-stats', scrollY: 900, desc: 'Stats Section (可衡量成果)' }, { name: '04-services', scrollY: 1800, desc: 'Services Matrix' }, { name: '05-industries', scrollY: 2800, desc: 'Industries Section' }, { name: '06-cases', scrollY: 3600, desc: 'Case Studies' }, { name: '07-approach', scrollY: 4500, desc: 'Approach Section' }, { name: '08-insights', scrollY: 5400, desc: 'Insights Section' }, { name: '09-cta', scrollY: 6300, desc: 'CTA Section' }, ]; console.log('\n3. Capturing section screenshots...'); for (const section of sections) { await page.evaluate((scrollY) => { window.scrollTo(0, scrollY); }, section.scrollY); await page.waitForTimeout(800); await page.screenshot({ path: path.join(screenshotsDir, `${section.name}.png`), }); console.log(` ✓ ${section.desc}`); } console.log('\n4. Checking text readability...'); const textIssues = await page.evaluate(() => { const issues = []; const allElements = document.querySelectorAll('*'); allElements.forEach((el) => { const style = window.getComputedStyle(el); const opacity = parseFloat(style.opacity); const mixBlendMode = style.mixBlendMode; if (mixBlendMode && mixBlendMode !== 'normal') { const rect = el.getBoundingClientRect(); if (rect.width > 50 && rect.height > 20) { issues.push({ type: 'mix-blend-mode', tag: el.tagName, className: el.className.substring(0, 80), blendMode: mixBlendMode, text: el.textContent?.trim().substring(0, 40) || '', }); } } if (opacity < 0.5 && opacity > 0) { const rect = el.getBoundingClientRect(); if (rect.width > 100 && rect.height > 30 && el.textContent?.trim().length > 10) { issues.push({ type: 'low-opacity-text', tag: el.tagName, className: el.className.substring(0, 80), opacity: opacity.toFixed(2), text: el.textContent.trim().substring(0, 40), }); } } }); return issues.slice(0, 20); }); if (textIssues.length > 0) { console.log(` ⚠ Found ${textIssues.length} potential issues:`); textIssues.forEach((issue, i) => { console.log(` ${i + 1}. [${issue.type}] ${issue.tag}: "${issue.text}"`); if (issue.blendMode) console.log(` blendMode: ${issue.blendMode}`); if (issue.opacity) console.log(` opacity: ${issue.opacity}`); }); } else { console.log(' ✓ No major text readability issues'); } console.log('\n5. Checking glow/blur elements...'); const glowElements = await page.evaluate(() => { const elements = []; const allElements = document.querySelectorAll('*'); allElements.forEach((el) => { const style = window.getComputedStyle(el); const hasBlur = style.filter.includes('blur'); const hasRadialGradient = style.backgroundImage.includes('radial-gradient'); const opacity = parseFloat(style.opacity); if ((hasBlur || hasRadialGradient) && opacity > 0.03) { const rect = el.getBoundingClientRect(); if (rect.width > 150 && rect.height > 150) { elements.push({ tag: el.tagName, className: el.className.substring(0, 100), opacity: opacity.toFixed(3), hasBlur, hasRadialGradient, size: `${Math.round(rect.width)}x${Math.round(rect.height)}`, position: style.position, zIndex: style.zIndex || 'auto', pointerEvents: style.pointerEvents, }); } } }); return elements.sort((a, b) => parseFloat(b.opacity) - parseFloat(a.opacity)).slice(0, 12); }); console.log(` Found ${glowElements.length} significant glow/blur elements:`); glowElements.forEach((el, i) => { const type = el.hasBlur && el.hasRadialGradient ? 'blur+gradient' : el.hasBlur ? 'blur' : 'gradient'; console.log(` ${i + 1}. ${el.tag} (${el.size}, ${type}, opacity: ${el.opacity})`); console.log(` position: ${el.position}, zIndex: ${el.zIndex}, pointerEvents: ${el.pointerEvents}`); console.log(` class: ${el.className.substring(0, 60)}`); }); await browser.close(); console.log('\n========================================'); console.log(' Test Complete'); console.log('========================================'); console.log(` Screenshots saved to: ${screenshotsDir}`); console.log('\n To view screenshots:'); console.log(` open ${screenshotsDir}`); console.log('========================================'); } runTests().catch(console.error);