fix(ci): stabilize visual regression environment

This commit is contained in:
jeffusion
2026-03-24 10:55:18 +08:00
committed by 路遥知码力
parent bd8235c70f
commit 98875044d6
4 changed files with 40 additions and 5 deletions

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -15,7 +15,7 @@ jobs:
- name: Set up Bun - name: Set up Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: latest bun-version: 1.3.10
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
@@ -35,6 +35,12 @@ jobs:
- name: Install Playwright Chromium - name: Install Playwright Chromium
run: cd frontend && bunx playwright install --with-deps chromium run: cd frontend && bunx playwright install --with-deps chromium
- name: Print visual stack versions
run: cd frontend && bun --version && bunx playwright --version && bunx playwright install --list
- name: Install deterministic CJK fonts for visual snapshots
run: sudo apt-get update && sudo apt-get install -y fonts-noto-cjk fonts-noto-color-emoji fonts-liberation
- name: Run visual regression - name: Run visual regression
run: bun run ui:visual run: bun run ui:visual

View File

@@ -1,4 +1,4 @@
import { defineConfig, devices } from '@playwright/test'; import { defineConfig } from '@playwright/test';
const port = Number(process.env.PW_PORT ?? 4173); const port = Number(process.env.PW_PORT ?? 4173);
const baseURL = process.env.PW_BASE_URL ?? `http://127.0.0.1:${port}`; const baseURL = process.env.PW_BASE_URL ?? `http://127.0.0.1:${port}`;
@@ -13,7 +13,7 @@ export default defineConfig({
animations: 'disabled', animations: 'disabled',
caret: 'hide', caret: 'hide',
scale: 'css', scale: 'css',
maxDiffPixels: 30, maxDiffPixelRatio: 0.012,
stylePath: './tests/visual/fixtures/screenshot.css', stylePath: './tests/visual/fixtures/screenshot.css',
}, },
}, },
@@ -23,11 +23,24 @@ export default defineConfig({
], ],
fullyParallel: false, fullyParallel: false,
use: { use: {
...devices['Desktop Chrome'],
baseURL, baseURL,
deviceScaleFactor: 1,
hasTouch: false,
isMobile: false,
locale: 'zh-CN', locale: 'zh-CN',
timezoneId: 'Asia/Shanghai', timezoneId: 'Asia/Shanghai',
viewport: { width: 1440, height: 900 }, viewport: { width: 1440, height: 900 },
launchOptions: {
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-lcd-text',
'--disable-font-subpixel-positioning',
'--font-render-hinting=none',
'--force-color-profile=srgb',
'--hide-scrollbars',
],
},
trace: 'retain-on-failure', trace: 'retain-on-failure',
screenshot: 'only-on-failure', screenshot: 'only-on-failure',
video: 'retain-on-failure', video: 'retain-on-failure',

View File

@@ -11,6 +11,9 @@ input,
textarea, textarea,
select { select {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Helvetica Neue', Arial, sans-serif !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Helvetica Neue', Arial, sans-serif !important;
text-rendering: geometricPrecision !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale !important;
} }
code, code,
@@ -22,3 +25,8 @@ pre,
.bg-grid-pattern { .bg-grid-pattern {
opacity: 0.04 !important; opacity: 0.04 !important;
} }
::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
}

View File

@@ -14,9 +14,17 @@ const STABILIZE_STYLE = `
export async function stabilizeVisualState(page: Page) { export async function stabilizeVisualState(page: Page) {
await page.addStyleTag({ content: STABILIZE_STYLE }); await page.addStyleTag({ content: STABILIZE_STYLE });
await page.waitForLoadState('networkidle');
await page.evaluate(() => { await page.evaluate(() => {
window.scrollTo(0, 0); window.scrollTo(0, 0);
}); });
await page.evaluate(async () => {
if (document.fonts) {
await document.fonts.ready;
}
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
});
} }
export async function installVisualNetworkGuards(page: Page) { export async function installVisualNetworkGuards(page: Page) {