Files
novalon-website/scripts/final-visual-test.mjs
T
张翔 636bc4ecde docs(test): 添加设计文档、测试规范与 E2E 测试套件
- 新增 ADR 架构决策记录 (Design DNA 集成与深化)
- 新增 CMS 系统设计文档
- 新增实施计划文档 (Phase1-3)
- 新增 Bain 品牌升级设计规格
- 新增 E2E 分层测试套件 (P1-P4)
- 新增视觉回归测试配置
- 新增光效分析、视觉验证等辅助脚本
- 更新验收测试报告
2026-07-07 06:54:25 +08:00

158 lines
5.7 KiB
JavaScript

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);