import { chromium } from '@playwright/test'; import fs from 'fs'; import path from 'path'; const screenshotsDir = '/tmp/screenshots/full-visual-test'; fs.mkdirSync(screenshotsDir, { recursive: true }); const pages = [ { name: 'home', url: 'http://localhost:3000', sections: ['hero', 'stats', 'services', 'industries', 'cases', 'approach', 'insights', 'cta'] }, { name: 'products', url: 'http://localhost:3000/products', sections: ['hero', 'product-grid'] }, { name: 'solutions', url: 'http://localhost:3000/solutions', sections: ['hero', 'solutions-grid'] }, { name: 'services', url: 'http://localhost:3000/services', sections: ['hero', 'services-grid'] }, { name: 'about', url: 'http://localhost:3000/about', sections: ['hero', 'about-content'] }, ]; async function capturePage(page, pageInfo) { console.log(`\n=== Testing ${pageInfo.name} page ===`); await page.goto(pageInfo.url, { waitUntil: 'networkidle' }); await page.waitForTimeout(1500); await page.screenshot({ path: path.join(screenshotsDir, `${pageInfo.name}-full.png`), fullPage: true, }); console.log(` ✓ Full page screenshot captured`); const viewportHeight = 1080; let currentScroll = 0; let sectionIndex = 0; while (sectionIndex < 5) { await page.evaluate((scroll) => { window.scrollTo(0, scroll); }, currentScroll); await page.waitForTimeout(800); await page.screenshot({ path: path.join(screenshotsDir, `${pageInfo.name}-section-${sectionIndex + 1}.png`), }); currentScroll += viewportHeight * 0.8; sectionIndex++; } console.log(` ✓ Section screenshots captured`); const cards = await page.$$('a[href], .card, [class*="group"]'); if (cards.length > 0) { const firstCard = cards[0]; await firstCard.scrollIntoViewIfNeeded(); await page.waitForTimeout(500); const box = await firstCard.boundingBox(); if (box) { await page.screenshot({ path: path.join(screenshotsDir, `${pageInfo.name}-card-before.png`), clip: { x: Math.max(0, box.x - 20), y: Math.max(0, box.y - 20), width: Math.min(box.width + 40, 800), height: Math.min(box.height + 40, 600), }, }); await firstCard.hover(); await page.waitForTimeout(600); await page.screenshot({ path: path.join(screenshotsDir, `${pageInfo.name}-card-hover.png`), clip: { x: Math.max(0, box.x - 20), y: Math.max(0, box.y - 20), width: Math.min(box.width + 40, 800), height: Math.min(box.height + 40, 600), }, }); console.log(` ✓ Card hover states captured`); } } await page.evaluate(() => { window.scrollTo(0, 0); }); await page.waitForTimeout(500); } async function checkTextReadability(page, pageName) { console.log(`\n Checking text readability...`); const issues = await page.evaluate(() => { const problems = []; const textElements = document.querySelectorAll('h1, h2, h3, h4, p, span, a'); textElements.forEach((el, index) => { const style = window.getComputedStyle(el); const opacity = parseFloat(style.opacity); const color = style.color; const bgColor = style.backgroundColor; const fontSize = parseFloat(style.fontSize); const zIndex = parseInt(style.zIndex) || 0; const rect = el.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) return; if (opacity < 0.4 && opacity > 0 && el.textContent.trim().length > 5) { problems.push({ type: 'low-opacity', text: el.textContent.trim().substring(0, 50), opacity: opacity.toFixed(2), tag: el.tagName, }); } if (style.mixBlendMode && style.mixBlendMode !== 'normal') { problems.push({ type: 'mix-blend-mode', text: el.textContent.trim().substring(0, 50), blendMode: style.mixBlendMode, tag: el.tagName, }); } }); return problems.slice(0, 10); }); if (issues.length > 0) { console.log(` ⚠ Found ${issues.length} potential readability issues:`); issues.forEach((issue, i) => { console.log(` ${i + 1}. [${issue.type}] ${issue.tag}: "${issue.text}"`); }); } else { console.log(` ✓ No major readability issues found`); } return issues; } async function checkGlowElements(page, pageName) { console.log(`\n Checking glow/blur elements...`); const glowInfo = await page.evaluate(() => { const allElements = document.querySelectorAll('*'); const glowElements = []; 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.05) { const rect = el.getBoundingClientRect(); if (rect.width > 100 && rect.height > 100) { glowElements.push({ tag: el.tagName, className: el.className.substring(0, 100), opacity: opacity.toFixed(3), hasBlur, hasRadialGradient, width: rect.width, height: rect.height, zIndex: style.zIndex || 'auto', position: style.position, }); } } }); return glowElements.slice(0, 15); }); console.log(` Found ${glowInfo.length} glow/blur elements (>100px, opacity>0.05)`); glowInfo.forEach((el, i) => { console.log(` ${i + 1}. ${el.tag} (${Math.round(el.width)}x${Math.round(el.height)}, opacity: ${el.opacity})`); console.log(` position: ${el.position}, zIndex: ${el.zIndex}`); }); return glowInfo; } async function runTests() { console.log('========================================'); console.log(' Novalon Website Visual Test Suite'); console.log('========================================'); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1080 }, }); const page = await context.newPage(); let totalIssues = 0; for (const pageInfo of pages.slice(0, 3)) { try { await capturePage(page, pageInfo); const readabilityIssues = await checkTextReadability(page, pageInfo.name); const glowElements = await checkGlowElements(page, pageInfo.name); totalIssues += readabilityIssues.length; } catch (e) { console.log(` ✗ Error testing ${pageInfo.name}: ${e.message}`); } } await browser.close(); console.log('\n========================================'); console.log(' Test Summary'); console.log('========================================'); console.log(` Screenshots saved to: ${screenshotsDir}`); console.log(` Total readability issues found: ${totalIssues}`); console.log('\n Check the screenshots for visual verification.'); console.log('========================================'); } runTests().catch(console.error);