visual-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<objective> Catch visual regressions that functional tests miss. A button that works perfectly but renders at 2px height passes `toBeVisible()` and `click()` — visual testing compares screenshots against approved baselines and flags pixel-level differences. This skill covers Playwright's built-in `toHaveScreenshot`, dedicated tools (Chromatic, Percy, Argos CI), and the baseline-management workflows around them. </objective>
<objective> 捕捉功能测试遗漏的视觉回归问题。一个功能完全正常但渲染高度仅为2px的按钮会通过`toBeVisible()`和`click()`测试——而视觉测试会将截图与已批准的基线进行对比,标记出像素级的差异。本技能涵盖Playwright内置的`toHaveScreenshot`方法、专用工具(Chromatic、Percy、Argos CI)以及围绕它们的基线管理工作流。 </objective>

Quick Route

快速指引

SituationGo to
Just need pixel diffs in CI, no review SaaSPlaywright Visual Comparisons (below)
Storybook in the repoDedicated Tools → Chromatic, or recipes.md "Storybook without Chromatic"
Need a hosted review/approval UI + AI diff triageDedicated Visual Testing Tools
Screenshots flake on timestamps/avatars/adsMasking & Freezing Dynamic Content
Baselines bloating git historyBaseline Management → Git LFS

场景参考内容
仅需在CI中进行像素差异对比,无需SaaS审核服务下方的Playwright视觉对比
仓库中包含Storybook专用工具 → Chromatic,或查看recipes.md中的「无Chromatic的Storybook」方案
需要托管式审核/批准UI + AI差异分类专用视觉测试工具
截图因时间戳/头像/广告出现不稳定问题遮蔽与冻结动态内容
基线导致Git历史记录臃肿基线管理 → Git LFS

Discovery Questions

调研问题

Check
.agents/qa-project-context.md
first — if it exists, use it and skip anything answered there.
首先查看
.agents/qa-project-context.md
——如果存在,请使用其中信息并跳过已解答的问题。

Tool selection

工具选择

  • Playwright built-in or dedicated tool?
    toHaveScreenshot
    is free, stores baselines in-repo (git/LFS cost is on you). Dedicated tools (Chromatic, Percy, Argos) store artifacts off-repo, add review workflows, browser farms, historical tracking, and — since late 2025 — AI diff triage. Choose on team size, review needs, and whether you want artifacts in your repo or theirs.
  • Storybook in the project? If yes, Chromatic is the natural fit (every story becomes a visual test) — but you can also drive stories with Playwright without paying (see recipes.md). If no Storybook, Playwright built-in or Percy.
  • CI platform and artifact budget? Visual tests generate large screenshot/diff artifacts. Built-in baselines live in git (repo bloat, LFS); hosted tools keep retention off-repo at a subscription cost. Confirm CI has the storage and time before scaling up.
  • 使用Playwright内置功能还是专用工具?
    toHaveScreenshot
    是免费的,基线存储在仓库内(Git/LFS成本由您承担)。专用工具(Chromatic、Percy、Argos)将工件存储在仓库外,提供审核工作流、浏览器集群、历史追踪功能,并且自2025年末起支持AI差异分类。根据团队规模、审核需求以及是否希望工件存储在自己或服务商的仓库中来选择。
  • 项目中是否使用Storybook? 如果是,Chromatic是自然选择(每个story都会成为一个视觉测试)——但您也可以无需付费,使用Playwright驱动story(见recipes.md)。如果没有Storybook,可选择Playwright内置功能或Percy。
  • CI平台及工件存储预算? 视觉测试会生成大量截图/差异工件。内置基线存储在Git中(会导致仓库臃肿,需使用LFS);托管工具将工件保留在仓库外,但需要订阅费用。在扩大规模前,请确认CI具备足够的存储和时间资源。

Scope

测试范围

  • Full-page or component screenshots? Full-page catches layout issues but is sensitive to unrelated changes. Component-level screenshots are more stable and focused.
  • Which pages/components are visually critical? Not everything needs it. Focus on user-facing pages, marketing pages, design-system components, and complex layouts.
  • Which viewports? Desktop, tablet, mobile — define the viewport matrix upfront from analytics, not every possible width.
  • 全页面截图还是组件截图? 全页面截图能捕捉布局问题,但对无关变更较为敏感。组件级截图更稳定且聚焦。
  • 哪些页面/组件是视觉关键项? 并非所有内容都需要视觉测试。重点关注用户面向页面、营销页面、设计系统组件以及复杂布局。
  • 需要测试哪些视口? 桌面端、平板端、移动端——根据分析数据预先定义视口矩阵,无需覆盖所有可能的宽度。

Dynamic content

动态内容

  • What changes between runs? Dates, timestamps, user-generated content, analytics IDs, randomized content, ads, avatars — all must be masked or frozen.
  • Animations or transitions? They cause false positives if not disabled or finished before capture.
  • External resources? Fonts, CDN images, third-party widgets can vary between runs.

  • 哪些内容会在每次运行时变化? 日期、时间戳、用户生成内容、分析ID、随机内容、广告、头像——所有这些都必须被遮蔽或冻结。
  • 是否存在动画或过渡效果? 如果未禁用或未等待其完成就进行截图,会导致误报。
  • 是否依赖外部资源? 字体、CDN图片、第三方组件在每次运行时可能存在差异。

Core Principles

核心原则

1. Visual tests catch what functional tests miss

1. 视觉测试捕捉功能测试遗漏的问题

Functional tests assert behavior ("clicking Submit shows a success message"). Visual tests assert appearance ("the success message is green, correctly positioned, and does not overlap the form"). Both are needed. Visual tests complement functional tests; they do not replace them.
功能测试断言行为(如「点击提交按钮显示成功消息」)。视觉测试断言外观(如「成功消息为绿色,位置正确,且不与表单重叠」)。两者都是必需的。视觉测试是功能测试的补充,而非替代。

2. Baseline management is the hard part

2. 基线管理是难点

Taking screenshots is easy. Managing baselines — updating them when design changes intentionally, reviewing diffs, coordinating approvals across a team — is the real challenge. Invest in the review workflow early.
截图很容易。管理基线——在设计有意变更时更新基线、审核差异、协调团队内的批准流程——才是真正的挑战。尽早投入精力搭建审核工作流。

3. Dynamic content causes false positives

3. 动态内容会导致误报

Any content that changes between runs (timestamps, avatars, ads, random IDs) produces pixel differences that are not real regressions. Aggressively mask or freeze it. A suite with a 10% false-positive rate gets ignored within a month. Hosted tools (Percy's Visual Review Agent, Chromatic AI triage) now auto-filter a large share of these — but masking and freezing remain your first line of defense regardless of tool.
任何在每次运行时变化的内容(时间戳、头像、广告、随机ID)都会产生并非真正回归的像素差异。要主动遮蔽或冻结这些内容。误报率达10%的测试套件会在一个月内被团队忽略。托管工具(Percy的Visual Review Agent、Chromatic的AI分类)现在可以自动过滤大部分此类误报——但无论使用何种工具,遮蔽和冻结始终是您的第一道防线。

4. Threshold tuning is iterative

4. 阈值调整是迭代过程

The right diff threshold depends on the component, rendering engine, and what counts as "visually different." Start strict (zero tolerance), observe false positives, loosen per-component. Document why each threshold was chosen.
合适的差异阈值取决于组件、渲染引擎以及何为「视觉差异」。从严格标准(零容忍)开始,观察误报情况,针对单个组件放宽阈值。记录每个阈值的选择原因。

5. Screenshots are artifacts, not test results

5. 截图是工件,而非测试结果

The screenshot file is the evidence. Store it, version it, make it accessible for review. A test that says "visual diff detected" without showing the diff is useless — always upload expected/actual/diff images as CI artifacts.

截图文件是证据。要存储、版本化并使其便于审核。仅提示「检测到视觉差异」但不展示差异的测试毫无用处——务必将预期/实际/差异图片作为CI工件上传。

Playwright Visual Comparisons

Playwright视觉对比

Playwright's built-in
toHaveScreenshot
and
toMatchSnapshot
provide visual regression testing without an external service.
Playwright内置的
toHaveScreenshot
toMatchSnapshot
无需外部服务即可实现视觉回归测试。

Basic screenshot comparison

基础截图对比

typescript
import { test, expect } from '@playwright/test';

test('dashboard matches baseline', async ({ page }) => {
  await page.goto('/dashboard');
  // Wait for data to load before capturing — never waitForTimeout
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await expect(page.getByTestId('chart-container')).toBeVisible();

  await expect(page).toHaveScreenshot('dashboard.png', { animations: 'disabled' });
});
First run creates the baseline. Subsequent runs compare and fail if pixels differ beyond the threshold.
typescript
import { test, expect } from '@playwright/test';

test('dashboard matches baseline', async ({ page }) => {
  await page.goto('/dashboard');
  // 等待数据加载完成后再截图——绝不要使用waitForTimeout
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await expect(page.getByTestId('chart-container')).toBeVisible();

  await expect(page).toHaveScreenshot('dashboard.png', { animations: 'disabled' });
});
首次运行会创建基线。后续运行会进行对比,如果像素差异超过阈值则测试失败。

Configuration options

配置选项

typescript
await expect(page).toHaveScreenshot('dashboard.png', {
  maxDiffPixels: 100,          // Allow up to 100 pixels to differ
  // OR
  maxDiffPixelRatio: 0.01,     // Allow up to 1% of pixels to differ
  threshold: 0.2,              // Per-pixel color difference tolerance (0-1, YIQ space)
  animations: 'disabled',      // Freeze CSS animations and transitions
  caret: 'hide',               // Hide blinking cursor
  stylePath: './screenshot.css', // Inject a stylesheet at capture to hide dynamic chrome
  timeout: 15000,              // Wait up to 15s for a stable screenshot
});
The default comparator is
pixelmatch
(YIQ color space). The relevant knobs are
comparator: 'pixelmatch'
plus
threshold
/
maxDiffPixel*
— there is no stable perceptual-color mode key, so do not set
mode:
(perceptual SSIM is an internal/experimental underscore API, not stable config).
When to use which threshold:
OptionUse when
maxDiffPixels: 0
Pixel-perfect components (icons, logos, design-system atoms)
maxDiffPixels: 50-100
Full-page layouts where antialiasing varies slightly
maxDiffPixelRatio: 0.01
Full-page screenshots where absolute pixel count varies with viewport
threshold: 0.2
Cross-browser testing where color rendering differs slightly
typescript
await expect(page).toHaveScreenshot('dashboard.png', {
  maxDiffPixels: 100,          // 允许最多100个像素存在差异
  // 或者
  maxDiffPixelRatio: 0.01,     // 允许最多1%的像素存在差异
  threshold: 0.2,              // 单像素颜色差异容忍度(0-1,YIQ色彩空间)
  animations: 'disabled',      // 冻结CSS动画和过渡效果
  caret: 'hide',               // 隐藏闪烁光标
  stylePath: './screenshot.css', // 截图时注入样式表以隐藏动态元素
  timeout: 15000,              // 等待稳定截图的最长时间为15秒
});
默认比较器是
pixelmatch
(YIQ色彩空间)。相关配置项为
comparator: 'pixelmatch'
加上
threshold
/
maxDiffPixel*
——没有稳定的感知色彩模式配置项,因此请勿设置
mode:
(感知SSIM是内部/实验性下划线API,并非稳定配置)。
何时使用何种阈值:
选项使用场景
maxDiffPixels: 0
像素级完美的组件(图标、Logo、设计系统原子组件)
maxDiffPixels: 50-100
抗锯齿效果略有差异的全页面布局
maxDiffPixelRatio: 0.01
视口变化会导致绝对像素数变化的全页面截图
threshold: 0.2
颜色渲染略有差异的跨浏览器测试

playwright.config.ts visual settings

playwright.config.ts视觉设置

typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      comparator: 'pixelmatch',    // Default comparator (YIQ color space)
      maxDiffPixelRatio: 0.005,    // Global default: 0.5% tolerance
      animations: 'disabled',
      caret: 'hide',
    },
    toMatchSnapshot: {
      maxDiffPixelRatio: 0.005,
    },
  },
  projects: [
    {
      name: 'visual-desktop',
      use: { viewport: { width: 1280, height: 720 }, colorScheme: 'light' },
      testMatch: /.*visual.*\.spec\.ts/,
    },
    {
      name: 'visual-mobile',
      use: { viewport: { width: 375, height: 667 }, colorScheme: 'light', isMobile: true },
      testMatch: /.*visual.*\.spec\.ts/,
    },
  ],
});
typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      comparator: 'pixelmatch',    // 默认比较器(YIQ色彩空间)
      maxDiffPixelRatio: 0.005,    // 全局默认:0.5%的容忍度
      animations: 'disabled',
      caret: 'hide',
    },
    toMatchSnapshot: {
      maxDiffPixelRatio: 0.005,
    },
  },
  projects: [
    {
      name: 'visual-desktop',
      use: { viewport: { width: 1280, height: 720 }, colorScheme: 'light' },
      testMatch: /.*visual.*\.spec\.ts/,
    },
    {
      name: 'visual-mobile',
      use: { viewport: { width: 375, height: 667 }, colorScheme: 'light', isMobile: true },
      testMatch: /.*visual.*\.spec\.ts/,
    },
  ],
});

Masking & freezing dynamic content

遮蔽与冻结动态内容

Two complementary tactics. Mask blanks specific elements;
stylePath
/ freezing kills time- and animation-driven noise.
typescript
test('profile page visual test', async ({ page }) => {
  await page.goto('/profile');
  await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();

  await expect(page).toHaveScreenshot('profile.png', {
    mask: [
      page.getByTestId('user-avatar'),       // User-specific image
      page.getByTestId('last-login-time'),    // Timestamp
      page.getByTestId('activity-feed'),      // Dynamic content
    ],
    maskColor: '#FF00FF',                      // Visible mask color for debugging
  });
});
For cursors, animations, and dynamic chrome, prefer
stylePath
(a stylesheet injected at capture time) over per-element masks — it is declarative and survives DOM changes. To eliminate timestamps and live data, pin the clock with
page.clock.setFixedTime
(holds the clock dead-still — best for screenshot determinism;
page.clock.install
lets it tick from a seed) and stub the API with
page.route
+
route.fulfill
.
See
references/recipes.md
for the full frozen-data recipe (clock + route + font-abort +
getAnimations().finish()
), the
stylePath
example, and component-state screenshots.
两种互补策略。遮蔽会模糊特定元素;
stylePath
/冻结会消除时间和动画驱动的干扰。
typescript
test('profile page visual test', async ({ page }) => {
  await page.goto('/profile');
  await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();

  await expect(page).toHaveScreenshot('profile.png', {
    mask: [
      page.getByTestId('user-avatar'),       // 用户专属图片
      page.getByTestId('last-login-time'),    // 时间戳
      page.getByTestId('activity-feed'),      // 动态内容
    ],
    maskColor: '#FF00FF',                      // 用于调试的可见遮蔽颜色
  });
});
对于光标、动画和动态元素,优先使用
stylePath
(截图时注入的样式表)而非逐元素遮蔽——它是声明式的,且能在DOM变化时保持有效。要消除时间戳和实时数据,可使用**
page.clock.setFixedTime
**固定时钟(将时钟完全静止——最适合确保截图的确定性;
page.clock.install
允许从种子值开始计时),并通过
page.route
+
route.fulfill
存根API。
查看
references/recipes.md
获取完整的冻结数据方案(时钟 + 路由 + 字体中止 +
getAnimations().finish()
)、
stylePath
示例以及组件状态截图方案。

Updating baselines

更新基线

bash
undefined
bash
undefined

Update all baselines (when design intentionally changes)

更新所有基线(当设计有意变更时)

npx playwright test --update-snapshots
npx playwright test --update-snapshots

Update baselines for specific tests only

仅更新特定测试的基线

npx playwright test visual-dashboard --update-snapshots
npx playwright test visual-dashboard --update-snapshots

Review what changed before committing

提交前查看变更内容

git diff --stat # See which baseline files changed npx playwright show-report # Visually review expected/actual/diff for each

**Baseline update workflow:**

1. Design change is implemented.
2. Run visual tests — they fail with expected diffs.
3. Review each diff in `show-report`: is the change intentional?
4. Update baselines: `npx playwright test --update-snapshots`.
5. Commit updated baselines with a message referencing the design change.
6. PR reviewers verify the baseline images look correct — not just the file diff.
git diff --stat # 查看哪些基线文件发生了变化 npx playwright show-report # 可视化审核每个测试的预期/实际/差异截图

**基线更新工作流:**

1. 设计变更已实现。
2. 运行视觉测试——测试会因预期差异而失败。
3. 在`show-report`中审核每个差异:变更是否为有意的?
4. 更新基线:`npx playwright test --update-snapshots`。
5. 提交更新后的基线,并附上引用设计变更的提交信息。
6. PR审核人员验证基线图片是否正确——不仅要查看文件差异。

Component-level & responsive testing

组件级与响应式测试

Screenshot the component (e.g.
getByRole('table')
), not the full page — more stable, more focused. Drive empty/error/normal states by stubbing the API per test. For responsive coverage, loop a
VISUAL_VIEWPORTS
array (mobile/tablet/desktop with
isMobile
flags) or define one Playwright project per viewport. See
references/recipes.md
for the component-state and responsive-loop recipes.

对组件(如
getByRole('table')
)截图,而非全页面——更稳定、更聚焦。通过为每个测试存根API来驱动空状态/错误状态/正常状态。对于响应式覆盖,可循环遍历
VISUAL_VIEWPORTS
数组(包含
isMobile
标志的移动端/平板端/桌面端),或为每个视口定义一个Playwright项目。查看
references/recipes.md
获取组件状态和响应式循环方案。

Dedicated Visual Testing Tools

专用视觉测试工具

Reach for these when you want a hosted review/approval UI, a cross-browser rendering farm, historical tracking, or AI-assisted diff triage — and you are fine with artifacts living off-repo on a subscription.
ToolBest whenIntegrationKey feature
ChromaticProject uses StorybookEvery story = a visual testReview/approval UI, cross-browser, TurboSnap + AI triage
PercyNo Storybook, need multi-browserAny framework via SDKMulti-width captures, CSS overrides, AI Visual Review Agent (auto-filters ~40% of false positives), BrowserStack Test Observability
Argos CIOpen-source preference, budget-consciousPlaywright reporterSelf-hosted tier; generous cloud free tier
AI diff triage (the material 2026 shift): Percy's Visual Review Agent and Chromatic's AI triage auto-classify a large share of diffs as noise vs. real change, cutting review fatigue. This is the strongest reason to pay for a hosted tool over built-in baselines — but it reduces, not removes, the need to mask/freeze dynamic content.
Avoid: Lost Pixel — repo archived 22 April 2026 (read-only). Use Argos, Chromatic, or Playwright's built-in
toHaveScreenshot
instead.
See
references/recipes.md
for the Chromatic GitHub Action, Percy
percySnapshot
, Argos
argosScreenshot
, and the Storybook-without-Chromatic snippets.

当您需要托管式审核/批准UI、跨浏览器渲染集群、历史追踪或AI辅助差异分类,且接受工件存储在服务商仓库(需订阅)时,可选择这些工具。
工具最佳适用场景集成方式核心功能
Chromatic项目使用Storybook每个story = 一个视觉测试审核/批准UI、跨浏览器测试、TurboSnap + AI分类
Percy无Storybook,需要多浏览器支持通过SDK适配任意框架多宽度截图、CSS覆盖、AI视觉审核代理(自动过滤约40%的误报)、BrowserStack测试可观测性
Argos CI偏好开源工具,预算有限Playwright报告器自托管版本;慷慨的云服务免费额度
AI差异分类(2026年重要变革): Percy的Visual Review Agent和Chromatic的AI分类可自动将大部分差异分类为干扰项或真实变更,减少审核疲劳。这是选择托管工具而非内置基线的最有力理由——但它只是减少而非消除了遮蔽/冻结动态内容的需求。
注意: Lost Pixel已于2026年4月22日归档(只读)。请改用Argos、Chromatic或Playwright内置的
toHaveScreenshot
查看
references/recipes.md
获取Chromatic GitHub Action、Percy
percySnapshot
、Argos
argosScreenshot
以及无Chromatic的Storybook代码片段。

Baseline Management

基线管理

Git-stored baselines, LFS, and platform tags

Git存储的基线、LFS与平台标签

Playwright stores baselines alongside test files, tagged per platform because rendering differs across operating systems:
e2e/tests/visual/
  dashboard.visual.spec.ts
  dashboard.visual.spec.ts-snapshots/
    dashboard-chromium-linux.png     # Platform-specific baselines
    dashboard-chromium-darwin.png
    dashboard-firefox-linux.png
Pros: versioned with the code, reviewed in PRs, available offline. Cons: repo size grows; large PNGs bloat git history.
Use Git LFS to prevent bloat, and customize layout with
snapshotPathTemplate
:
undefined
Playwright将基线与测试文件存储在一起,并按平台标记,因为不同操作系统的渲染效果存在差异:
e2e/tests/visual/
  dashboard.visual.spec.ts
  dashboard.visual.spec.ts-snapshots/
    dashboard-chromium-linux.png     # 平台专属基线
    dashboard-chromium-darwin.png
    dashboard-firefox-linux.png
优点: 与代码版本化,在PR中审核,离线可用。缺点: 仓库大小增长;大型PNG文件会臃肿Git历史记录。
使用Git LFS防止仓库臃肿,并通过
snapshotPathTemplate
自定义布局:
undefined

.gitattributes

.gitattributes

*.png filter=lfs diff=lfs merge=lfs -text

```typescript
// playwright.config.ts
export default defineConfig({
  snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}-{projectName}{ext}',
});
Because baselines are platform-tagged, generate them in the same Docker image CI uses so they always match the CI rendering environment — never commit baselines captured on a developer laptop. See
references/recipes.md
for the Docker CI job (
mcr.microsoft.com/playwright:v1.60.0-noble
, matched to your
@playwright/test
version).
*.png filter=lfs diff=lfs merge=lfs -text

```typescript
// playwright.config.ts
export default defineConfig({
  snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}-{projectName}{ext}',
});
由于基线是按平台标记的,请在CI使用的相同Docker镜像中生成基线,以确保始终匹配CI的渲染环境——绝不要提交在开发人员笔记本上捕获的基线。查看
references/recipes.md
获取Docker CI作业(
mcr.microsoft.com/playwright:v1.60.0-noble
,需与您的
@playwright/test
版本匹配)。

Review and approval workflow

审核与批准工作流

  1. CI detects a visual diff, uploads expected/actual/diff images as artifacts.
  2. PR reviewer examines the diffs in
    show-report
    (or the hosted tool's UI).
  3. Intentional change: update baselines (
    --update-snapshots
    ), re-commit.
  4. Unintentional regression: fix the code, re-run tests.

  1. CI检测到视觉差异,将预期/实际/差异图片作为工件上传。
  2. PR审核人员在
    show-report
    (或托管工具的UI)中检查差异。
  3. 若为有意变更:更新基线(
    --update-snapshots
    ),重新提交。
  4. 若为意外回归:修复代码,重新运行测试。

Anti-Patterns

反模式

1. Full-page screenshots without masking

1. 未遮蔽动态内容的全页面截图

Capturing entire pages without masking dynamic content (timestamps, avatars, live data) produces diffs every run. The team stops trusting visual tests. Always mask dynamic regions and freeze time-dependent content.
在未遮蔽动态内容(时间戳、头像、实时数据)的情况下捕获全页面截图,每次运行都会产生差异。团队会不再信任视觉测试。务必遮蔽动态区域并冻结时间相关内容。

2. No artifact storage in CI

2. CI中未存储工件

Running visual tests without uploading screenshot artifacts means a failure has no expected/actual/diff to inspect, forcing local repro that may render differently. Always upload screenshots, diffs, and the report as CI artifacts.
运行视觉测试但不上传截图工件,意味着测试失败时无法查看预期/实际/差异截图,只能在本地重现,而本地渲染效果可能不同。务必将截图、差异和报告作为CI工件上传。

3. No review process for baseline updates

3. 基线更新无审核流程

Running
--update-snapshots
and committing without reviewing the change bakes regressions into baselines, making them invisible. Every update goes through review of the before/after images, not just the file diff.
运行
--update-snapshots
并直接提交而不审核变更,会将回归问题纳入基线,使其变得不可见。每次更新都必须审核前后图片,而非仅查看文件差异。

4. Visual-testing unstable components

4. 对不稳定组件进行视觉测试

Visual tests for components that change by design (A/B tests, personalized content, frequently rotated banners) fail constantly with intentional changes. Exclude them or stub their content.
对设计上会频繁变化的组件(A/B测试、个性化内容、频繁轮换的横幅)进行视觉测试,会因有意变更而持续失败。请排除这些组件或存根其内容。

5. Pixel-perfect thresholds on full pages

5. 全页面截图使用像素完美阈值

maxDiffPixels: 0
on full-page screenshots flags sub-pixel rendering differences from browser/OS/font updates as failures. Use
maxDiffPixelRatio: 0.005
for full pages; reserve zero tolerance for small critical components (logos, icons).
对全页面截图设置
maxDiffPixels: 0
会将浏览器/操作系统/字体更新导致的亚像素渲染差异标记为失败。全页面截图请使用
maxDiffPixelRatio: 0.005
;仅对小型关键组件(Logo、图标)保留零容忍阈值。

6. No consistent rendering environment

6. 无一致的渲染环境

Running visual tests on assorted developer machines and expecting baselines to match — font rendering, antialiasing, and scaling differ across platforms. Run in a consistent CI Docker environment and generate baselines there.
在各种开发人员机器上运行视觉测试并期望基线匹配——不同平台的字体渲染、抗锯齿和缩放效果存在差异。请在一致的CI Docker环境中运行测试并生成基线。

7. Skipping animation handling

7. 未处理动画

Not disabling animations captures transitions mid-frame, producing random diffs. Use
animations: 'disabled'
,
stylePath
to zero out durations, or
getAnimations().finish()
before capture.

未禁用动画会捕获过渡中的帧,产生随机差异。请使用
animations: 'disabled'
stylePath
将动画时长设为0,或在截图前调用
getAnimations().finish()

Verification

验证

Prove baselines generate and the comparator runs before calling it done, smallest first:
bash
npx playwright test --grep @visual          # baselines generate (first run) / compare (later)
npx playwright test --grep @visual --update-snapshots  # regenerate intentionally
npx playwright show-report                  # diff artifacts (expected/actual/diff) render
git check-attr filter -- "e2e/**/*.png"     # prints "filter: lfs" if LFS is wired
A clean first run that writes
*-snapshots/*.png
files, plus a second run that passes against them, confirms the pipeline works end to end.

在完成前,先证明基线可生成且比较器能运行,从最小测试开始:
bash
npx playwright test --grep @visual          # 首次运行生成基线/后续运行进行对比
npx playwright test --grep @visual --update-snapshots  # 有意重新生成基线
npx playwright show-report                  # 差异工件(预期/实际/差异)可渲染
git check-attr filter -- "e2e/**/*.png"     # 若LFS已配置,会输出"filter: lfs"
首次干净运行生成
*-snapshots/*.png
文件,且第二次运行能通过对比,即可确认流水线端到端正常工作。

Done When

完成标准

  • Baseline screenshots captured in a consistent CI Docker environment (not locally) and committed.
  • playwright.config.ts
    sets a global
    maxDiffPixelRatio
    AND at least one icon/logo test overrides to
    maxDiffPixels: 0
    .
  • Dynamic content masked or frozen before capture (timestamps, avatars, live API data) via
    mask
    ,
    stylePath
    , or clock/route stubbing.
  • CI pipeline blocks merge when a visual diff exceeds the configured threshold.
  • Baselines tracked via Git LFS (
    .gitattributes
    has
    *.png filter=lfs
    ) OR an off-repo hosted tool, so the repo does not bloat.
  • Review workflow defined: who reviews diffs, how intentional changes get baseline updates, and PR reviewers sign off on the baseline images.
  • 基线截图在一致的CI Docker环境(而非本地)中捕获并提交。
  • playwright.config.ts
    设置了全局
    maxDiffPixelRatio
    ,且至少有一个图标/Logo测试覆盖为
    maxDiffPixels: 0
  • 动态内容在截图前已被遮蔽或冻结(时间戳、头像、实时API数据),方式包括
    mask
    stylePath
    或时钟/路由存根。
  • 当视觉差异超过配置阈值时,CI流水线会阻止合并。
  • 基线通过Git LFS跟踪(
    .gitattributes
    包含
    *.png filter=lfs
    )或存储在托管工具中,避免仓库臃肿。
  • 已定义审核工作流:谁审核差异、有意变更如何更新基线、PR审核人员需签署确认基线图片正确。

Reference Files (in
references/
)

参考文件(位于
references/
目录)

  • recipes.md — frozen-data capture (clock + route + font-abort),
    stylePath
    masking, component-state screenshots, the responsive-viewport loop, Chromatic/Percy/Argos snippets, Storybook-without-Chromatic, and the Docker CI job.
  • recipes.md — 冻结数据捕获(时钟 + 路由 + 字体中止)、
    stylePath
    遮蔽、组件状态截图、响应式视口循环、Chromatic/Percy/Argos代码片段、无Chromatic的Storybook方案以及Docker CI作业。

Related Skills

相关技能

  • playwright-automation — foundation for Playwright-based visual tests; Page Object Model, fixtures, and test structure apply here too.
  • ci-cd-integration — pipeline config for running visual tests, uploading artifacts, and wiring review workflows.
  • cross-browser-testing — when the goal is a rendering matrix across browsers rather than baseline diffs of one render; viewport/project config overlaps.
  • selector-drift-recovery — when a redesign broke many baselines at once and you need bulk regeneration, not per-test visual diffs.
  • qa-project-context — captures which pages are visually critical and what dynamic content exists.
  • playwright-automation — 基于Playwright的视觉测试基础;Page Object Model、fixtures和测试结构同样适用于此处。
  • ci-cd-integration — 运行视觉测试、上传工件以及搭建审核工作流的流水线配置。
  • cross-browser-testing — 当目标是跨浏览器渲染矩阵而非单一渲染的基线差异时;视口/项目配置存在重叠。
  • selector-drift-recovery — 当重新设计导致大量基线失效,需要批量再生而非逐测试视觉差异对比时。
  • qa-project-context — 记录哪些页面是视觉关键项以及存在哪些动态内容。