Files
novalon-website/e2e/playwright.config.ts
T
zhangxiang 6bb7c557ee test(qa): e2e 装配修正 + 集成/门禁测试与工具链重构
- e2e:修死选择器与蜜罐误命中、补断言、GA4/security-headers 用例诚实标注
  skip 边界;新增 assert-helpers/fixtures/hydrated/primary-nav/touch-targets
  等复用桩。
- 集成层:config/test/jest.integration.config.js + tests-integration/ 真库
  一次性 SQLite 用例,teardown 守卫开发库指纹。
- 门禁工具链:jest.setup 归位到根、scripts/accessibility 全站 axe 节点计数、
  check-brand-text-token/check-motion-constraints 机械守卫、stryker/lighthouse
  配置收敛。
2026-09-28 10:48:08 +08:00

147 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineConfig, devices } from '@playwright/test';
import { config as dotenvConfig } from 'dotenv';
import path from 'path';
// 加载项目根目录的 .env.local,使 Playwright webServer 与测试进程使用相同的 CMS_REVALIDATE_SECRET
// 若 .env.local 不存在则回退到默认测试 secret
dotenvConfig({ path: path.resolve(__dirname, '../.env.local') });
process.env.CMS_REVALIDATE_SECRET = process.env.CMS_REVALIDATE_SECRET || 'e2e-test-secret';
export default defineConfig({
testDir: './',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { open: 'never' }],
['list', { printSteps: true }],
['allure-playwright', { outputFolder: 'allure-results' }],
],
snapshotDir: './visual-snapshots',
snapshotPathTemplate: '{snapshotDir}/{projectName}/{testFilePath}/{arg}-{projectName}{ext}',
// 显式 outputDir 绝对路径(e2e/test-results),避免 playwright 在项目根创建 test-results
// 触发 WorkBuddy 沙箱 safe-delete BULK_GUARD(node fs.rm ≥ 50 文件被拦,e2e 直接退出)
outputDir: path.join(__dirname, 'test-results'),
use: {
baseURL: process.env.E2E_BASE_URL || 'http://localhost:3000',
// 路径以配置文件所在目录为基准,兼容 `cd e2e` 与从项目根目录直接调用两种执行方式
storageState: path.resolve(__dirname, 'storageState.json'),
trace: 'on-first-retry',
screenshot: 'only-on-failure',
ignoreHTTPSErrors: true,
actionTimeout: 15000,
navigationTimeout: 30000,
},
expect: {
toHaveScreenshot: {
maxDiffPixels: 200,
maxDiffPixelRatio: 0.005,
threshold: 0.3,
animations: 'disabled',
caret: 'hide',
scale: 'device',
timeout: 20000,
},
timeout: 15000,
},
projects: [
{
name: 'visual-chromium-desktop',
use: {
...devices['Desktop Chrome'],
viewport: { width: 1280, height: 800 },
// 视觉回归确定性:reducedMotion 让 ScrollReveal/StaggerReveal 走 initial={} 分支
// 默认可见(不残留 opacity:0),避免全页截图时 below-the-fold 模块因 whileInView
// 未触发而空白。同时使 Hero 粒子场走固定种子的静态降级帧,快照零漂移。
reducedMotion: 'reduce',
},
testMatch: 'visual-regression.spec.ts',
},
{
name: 'visual-chromium-tablet',
use: {
...devices['iPad Pro 11'],
viewport: { width: 834, height: 1194 },
reducedMotion: 'reduce',
},
testMatch: 'visual-regression.spec.ts',
},
{
name: 'visual-chromium-mobile',
use: {
...devices['iPhone 14'],
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
reducedMotion: 'reduce',
},
testMatch: 'visual-regression.spec.ts',
},
{
name: 'visual-firefox-desktop',
use: {
...devices['Desktop Firefox'],
viewport: { width: 1280, height: 800 },
storageState: './storageState.firefox.json',
reducedMotion: 'reduce',
},
testMatch: 'visual-regression.spec.ts',
},
{
name: 'visual-webkit-desktop',
use: {
...devices['Desktop Safari'],
viewport: { width: 1280, height: 800 },
storageState: './storageState.firefox.json',
reducedMotion: 'reduce',
},
testMatch: 'visual-regression.spec.ts',
},
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
testIgnore: 'visual-regression.spec.ts',
},
{
name: 'chromium-mobile',
use: {
...devices['iPhone 14'],
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
},
testIgnore: 'visual-regression.spec.ts',
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'], storageState: './storageState.firefox.json' },
testIgnore: 'visual-regression.spec.ts',
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'], storageState: './storageState.firefox.json' },
testIgnore: 'visual-regression.spec.ts',
},
],
webServer: {
// E2E_TARGET=production 时打构建产物(验收 A-10:dev server 下预渲染、ISR、生产响应头
// 与构建期才暴露的问题永不被测)。dev 仍是默认目标,因为多数用例依赖热更新提速。
// 产物是 output:'standalone',Next 16 明确告警 `"next start" does not work with
// "output: standalone"` ⇒ 生产目标必须直接起 dist/standalone/server.js,否则测的是不受支持的降级路径。
command:
process.env.E2E_TARGET === 'production'
? 'HOSTNAME=localhost PORT=3000 node dist/standalone/server.js'
: 'npm run dev',
url: 'http://localhost:3000',
// 复用已有服务会让「测的是当前代码」这一前提失效(门禁脚本曾把 next-server 留在 :3000 达
// 39 分钟),故 CI 与 production 目标一律新起服务。
reuseExistingServer: !process.env.CI && process.env.E2E_TARGET !== 'production',
timeout: 180000,
cwd: '..',
env: {
CMS_REVALIDATE_SECRET: process.env.CMS_REVALIDATE_SECRET || 'e2e-test-secret',
},
},
});