mcpbeat

Percy Visual Regression Testing

pramoddutta/percy visual regression testing

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.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
195
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/PramodDutta/qaskills --skill Percy Visual Regression Testing

The instruction itself

10 sections, as written by the author

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=...            # project token from percy.io
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('cart-total').waitFor();
  await percySnapshot(page, 'Checkout - 3 items', { widths: [375, 768, 1280] });

  await page.getByRole('button', { name: 'Apply coupon' }).click();
  await page.getByRole('dialog').waitFor();
  await percySnapshot(page, 'Checkout - coupon modal');
});
npx percy exec -- npx playwright test e2e/visual.spec.ts

Cypress equivalent: @percy/cypress, cy.percySnapshot('name', { widths: [...] }). Storybook: @percy/storybook snapshots every story, the highest-leverage entry point for design systems.

Snapshot Strategy

| Layer | What to snapshot | Cadence |

|---|---|---|

| Design system | Every component story (via Storybook) | Every PR |

| Money-path pages | Each state of checkout, auth, pricing | Every PR |

| Marketing/content pages | Above-the-fold at 3 widths | Nightly |

| Full-page long-tail | Top 20 templates, not every URL | Nightly |

Name snapshots as stable identifiers ("Checkout - coupon modal"), never with timestamps or data values; renamed snapshots orphan their baselines. Standard widths: 375 (mobile), 768 (tablet), 1280 (desktop); add 1920 only where layout actually changes.

Stabilizing Dynamic Content

/* percy.css (applied only in Percy's rendering) */
.timestamp, .relative-time { visibility: hidden !important; }
.avatar-random { background: #ccc !important; }
* { animation: none !important; transition: none !important; caret-color: transparent !important; }
# .percy.yml
version: 2
snapshot:
  percy-css: |
    @import url("./percy.css");
  widths: [375, 1280]
discovery:
  network-idle-timeout: 500

Additional stabilizers: seed test data (fixed names, fixed prices); freeze the clock in the app under test where dates render; mock third-party embeds (maps, ads, chat widgets) at the network layer; wait for fonts (document.fonts.ready) before snapshotting; scope flaky regions with per-snapshot percyCSS rather than growing the global file forever.

Review Workflow Discipline

  • Percy posts a status check per PR: unreviewed diffs block merge (enable in project settings + branch protection)
  • WHO approves: the PR author never solo-approves their own visual changes on design-system components; route to a design owner
  • Approve-all is for intentional global changes (rebrand, font swap) only, and gets a PR comment explaining it
  • Auto-approve on main after merge keeps baselines current; feature branches compare against their base branch build
  • Weekly hygiene: any snapshot that needed approval 3 PRs in a row without a real UI change is noisy; stabilize or delete it

CI Wiring

# .github/workflows/visual.yml
- name: Percy visual tests
  env:
    PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
  run: npx percy exec -- npx playwright test e2e/visual/
# parallel shards: run percy exec on each shard with PERCY_PARALLEL_TOTAL set,
# so Percy assembles one build from all shards

Budget note: Percy prices by screenshots (snapshots x widths x browsers); the strategy table above exists to keep the count intentional. Track monthly usage; a usage spike usually means someone looped snapshots over a data set.

Percy vs Alternatives

Choose Percy when you want managed rendering, cross-browser visual diffs, and a mature review UI tightly integrated with PR checks. Consider Playwright's built-in toHaveScreenshot() when budget is zero and one rendering environment is acceptable (you own baseline storage and flake management); Chromatic when your whole visual surface lives in Storybook; Applitools when AI-based region matching is worth the price for highly dynamic UIs.

Common Mistakes

  • Snapshotting after networkidle alone; wait for the specific element/fonts, spinners pass networkidle
  • Baselines approved by whoever is unblocking their own PR; route design-system diffs to owners
  • One snapshot of a page's default state; the bug is in the error state you never captured
  • Global percy.css graveyard hiding half the page; prefer per-snapshot scoping and fix root causes
  • Treating a green Percy build as functional coverage; it only proves pixels matched

Checklist

  • [ ] Percy wired through the E2E runner (percy exec) with PR status checks blocking on unreviewed diffs
  • [ ] Snapshot set curated per the strategy table; states, not just pages; stable names
  • [ ] Dynamic content stabilized (percy-css, seeded data, frozen clock, font wait)
  • [ ] Review routing: design owner for system components; approve-all only with justification
  • [ ] Monthly screenshot-usage and noisy-snapshot review

How to use it

Copy the folder

Take pramoddutta/percy visual regression testing from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.

Install what it needs

The instructions reference npm, npx. Without those the skill loads but fails at the first command.