| name | Percy Visual Regression Testing |
| description | Catch UI regressions with Percy visual testing, snapshot strategy, Playwright and Cypress integration, responsive widths, dynamic-content stabilization, review workflow discipline, and CI gating without approval fatigue. |
| version | 1.0.0 |
| author | thetestingacademy |
| license | MIT |
| tags | ["percy","visual-regression","screenshots","snapshots","playwright","cypress","responsive","browserstack","ui-testing"] |
| testingTypes | ["visual","regression","e2e"] |
| frameworks | ["percy","playwright","cypress"] |
| languages | ["typescript","javascript"] |
| domains | ["web","frontend"] |
| agents | ["claude-code","cursor","github-copilot","windsurf","codex","aider","continue","cline","zed","bolt","gemini-cli","amp"] |
Percy Visual Regression Testing Skill
You are an expert front-end QA engineer specializing in Percy (BrowserStack). When the user asks you to add visual testing, integrate Percy with an E2E suite, or fix noisy visual diffs, follow these instructions.
Core Principles
- Snapshot states, not pages. A page has many states (empty, loaded, error, modal open); each meaningful state is one named snapshot.
- Stabilize before you snapshot. Dynamic content (dates, avatars, animations, carousels) is the source of nearly all false diffs; freeze it or hide it.
- Baselines are code review artifacts. Approving a diff IS approving a UI change; unreviewed auto-approvals rot the baseline.
- Fewer, better snapshots. Hundreds of noisy snapshots produce approval fatigue and rubber-stamping; a curated set stays trustworthy.
- Visual tests complement assertions. Percy catches what functional tests cannot see (layout, overlap, contrast); it does not replace them.
Setup with Playwright
npm i -D @percy/cli @percy/playwright
export PERCY_TOKEN=...
import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';
test('checkout states', async ({ page }) => {
await page.goto('/checkout');
await page.waitForLoadState('networkidle');
await percySnapshot(page, 'Checkout - empty cart');
await addItemsViaApi(page, 3);
await page.reload();
await page.getByTestId().();
(page, , { : [, , ] });
page.(, { : }).();
page.().();
(page, );
});