PRACTICAL GUIDE / percy visual testing
Percy Visual Testing: Screenshot Review Workflow
Percy visual testing guide for QA and frontend teams: learn snapshots, baselines, pull request reviews, CI setup, stable data, and best practices.
In this guide8 sections
- One component change produced 180 unexplained diffs
- Design a snapshot inventory reviewers can reason about
- Stabilize the DOM and its assets
- Create the project and handle credentials safely
- Add Percy to a focused Playwright test
- Make the CI build reflect source-control history
- Review a build as a set of product claims
- Manage review cost as a quality metric
What you will learn
- One component change produced 180 unexplained diffs
- Design a snapshot inventory reviewers can reason about
- Stabilize the DOM and its assets
- Create the project and handle credentials safely
One component change produced 180 unexplained diffs
A frontend team added Percy to its full end-to-end suite and captured every page at three widths. The first pull request after launch changed a shared button token. Percy reported 180 snapshots with differences. Reviewers sampled five, assumed the rest were the same, and approved the build. One unreviewed checkout snapshot also contained a clipped tax label.
The integration worked. The review design did not.
Percy is most useful when a snapshot represents a state someone is responsible for judging. Before adding one, write its review question: “Does the order summary preserve amount hierarchy and a usable submit action at the mobile width?” That question determines the state, width, scope, and owner. It also makes removal possible when the snapshot no longer protects a decision.
Begin with one product surface and one pull-request workflow. Expansion should follow evidence that the team can stabilize and review the existing set.
Design a snapshot inventory reviewers can reason about
Inventory visual risks by component and state, not by URL count. A product page might need default, unavailable, discounted, and long-title states. Capturing the default page four times through different navigation tests adds little.
Use a worksheet like this:
| Snapshot name | Prepared state | Widths | Review concern | Owner |
|---|---|---|---|---|
| Product, long title | fixed product fixture | 390, 1280 | wrapping cannot displace purchase action | catalog team |
| Cart, promotion applied | seeded cart | 390, 768 | total hierarchy and discount placement | checkout team |
| Address, invalid postal code | submitted invalid form | 390 | error association and recovery | design system |
Stable names matter because comparisons and history need a durable identity. Include the meaningful state in the name. Do not embed a random user ID, timestamp, or CI job number.
Select widths at actual layout boundaries and supported devices. More widths create more renderings and more decisions. A width should exist because a component rearranges there or because product support requires it.
Prefer component or region snapshots for isolated design-system behavior, and page snapshots where composition is the risk. A full page can expose unexpected interactions between header, content, sticky controls, and footer. It can also create noise from unrelated modules. Choose deliberately.
Stabilize the DOM and its assets
Percy SDKs capture page state and the assets needed for remote rendering. That makes application readiness and asset discovery part of the test.
Control these inputs before capture:
- fixture records, sorting, personalization, and feature flags
- locale, time zone, theme, authentication role, and consent state
- font files, icons, stylesheets, and images reachable during discovery
- animation, video, cursor, and skeleton-loading behavior
- lazy content that appears only after scrolling
- third-party frames, advertisements, and unavailable hosts
Wait for a business condition such as “six catalog cards are loaded” rather than network quiet alone. Modern applications may keep background connections open or finish network activity before a framework has painted its final state.
When a volatile area has no visual contract, exclude or neutralize the smallest possible region. Percy-specific CSS can be useful for disabling motion or hiding a third-party widget in the Percy rendering. Keep those overrides in version control and add a reason. An unexplained hidden selector becomes a blind spot.
Run the same prepared state twice before calling it stable. If the second build differs without a product change, investigate fonts, data, timing, and assets first.
Create the project and handle credentials safely
Create a Percy project that maps to one codebase or independently reviewed product surface. Connect it to source control when branch and pull-request comparison are part of the workflow. Choose the baseline management approach that matches how the team branches and releases.
The project token identifies where snapshots are sent. Store PERCY_TOKEN in the CI secret mechanism and inject it only into the visual job. Do not place it in the repository, a sample environment file with a real value, or test output.
Decide these operating settings before the first team build:
- which branch supplies the normal baseline
- how release branches and long-running branches compare
- who can approve visual changes
- whether an unresolved Percy build blocks merge
- which CI events send snapshots
- how forked or untrusted contributions run without exposing secrets
Run local Percy builds only when they add value. A developer may need them while changing responsive CSS, but sending every save creates clutter. The pull-request build should remain the shared review record.
Add Percy to a focused Playwright test
Install the current Percy Playwright package and call percySnapshot after the page reaches the intended state. Run the test through Percy’s execution wrapper so asset discovery and build finalization occur around the test command.
const { test, expect } = require("@playwright/test");
const percySnapshot = require("@percy/playwright");
test("discounted cart remains readable", async ({ page }) => {
await page.goto("/cart?fixture=promotion-applied");
await expect(page.getByTestId("order-total")).toHaveText("$81.00");
await expect(page.getByRole("button", { name: "Checkout" }))
.toBeEnabled();
await percySnapshot(page, "Cart, promotion applied", {
widths: [390, 768, 1280],
});
});The functional assertions protect the exact calculation and action state. The snapshot protects their presentation. Keeping both gives a reviewer clearer failure evidence.
An example CI-facing command is:
npx percy exec -- npx playwright test tests/visual/cart.spec.tsUse a dedicated script in the project configuration so local and CI commands stay consistent. Fail clearly when the token is expected but absent. If the ordinary functional suite should run without Percy, make that behavior explicit rather than silently losing visual coverage in CI.
Do not snapshot while an interaction is in flight. Prepare the page, assert readiness, then capture. If several states matter in one test, give each snapshot a unique state name and reset any data that could leak between them.
Make the CI build reflect source-control history
Percy needs branch and commit context to choose a meaningful comparison and group snapshots into a build. Configure the CI provider’s recommended environment integration, especially for merge commits, rebases, and parallel jobs. A visually clean build against the wrong ancestor is misleading.
If tests are sharded, ensure all snapshot uploads contribute to the intended Percy build and that finalization waits for every shard. Do not let the first finishing job announce completion while other snapshots are still arriving.
Separate infrastructure failure from visual difference in pipeline reporting. Reviewers need to know whether:
- no snapshots were uploaded
- the build is incomplete
- rendering or asset discovery failed
- comparison completed with changes
- all changes have been reviewed
Keep the visual job scoped during adoption. Run it on changes to relevant frontend paths plus a scheduled broader build if that suits the architecture. Path filters should be conservative because shared tokens, dependencies, and server data can affect many pages.
Review a build as a set of product claims
Open the pull-request result and first verify build completeness: expected snapshots, widths, and state names are present. An all-green build with half the inventory missing is not success.
Review changed snapshots in this order:
- unexpected additions, deletions, or missing snapshots
- high-impact checkout, authentication, and recovery states
- shared layout and design-system changes
- feature-specific pages
- lower-risk responsive variants
For each difference, inspect the baseline and current rendering, then classify it:
- intended change supported by the work item
- unintended product regression
- fixture or readiness defect
- asset-discovery or rendering defect
- outdated snapshot that should be redesigned or removed
Approval should identify the reason, especially for broad baseline updates. If a token change affects many snapshots, verify representative uses by component category and still inspect outliers. Do not assume identical-looking highlight areas have identical business impact.
When a defect is found, link the snapshot and describe the affected state and width. After the correction, the next build should show both the resolved visual result and unchanged neighboring snapshots.
Manage review cost as a quality metric
A visual suite consumes reviewer attention. Measure that resource directly.
Track snapshots per pull request, changed snapshots, unstable reruns, time to first review, unresolved build age, and genuine regressions found. Segment instability by snapshot so a few volatile pages do not discredit the entire project.
Set a maintenance response:
- first unexplained change: classify and record the source
- repeated environmental change: repair fixture, waits, or assets
- recurring irrelevant difference: narrow scope or add a justified override
- no longer meaningful state: delete the snapshot and its inventory entry
- repeated missed defect: add or reshape a state around that failure
Review ownership should follow the UI. A design-system maintainer can approve a deliberate token update, while a checkout owner should judge whether the new hierarchy still protects purchase accuracy. Requiring one QA engineer to interpret every product change creates a bottleneck and weak approvals.
The Percy workflow is healthy when a pull request produces a small, complete, branch-correct set of comparisons; each change has a clear reviewer; and noisy inputs are repaired instead of normalized. The objective is not a gallery of screenshots. It is a dependable visual review gate that makes a hidden layout regression hard to merge.
// FIELD DISPATCH
Get the QA Field Notes
Weekly QA battles, AI testing guides, and interview drills. Free on Substack.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01
FAQ / QUICK ANSWERS
Questions testers ask
Which Percy snapshots deserve a place in a pull-request gate?
Keep snapshots that ask a specific review question about a prepared state, supported width, and owned product risk. Prefer state coverage over repeated URL coverage. Start with one product surface, use durable names, and expand only after the team proves it can stabilize inputs and review the existing inventory completely.
What should a team fix first when the same Percy snapshot changes twice without code changes?
Treat the snapshot as unstable and investigate fixture data, sorting, fonts, assets, locale, time zone, animation, lazy content, and readiness conditions. Wait for a business state rather than network quiet. Hide only the smallest region that has no visual contract, record the reason, and rerun the prepared state twice.
Can an all-green Percy result still be unsafe to merge?
Yes. First confirm every expected state, width, and snapshot name arrived and that the build compared against the correct branch and commit. Missing uploads, incomplete sharded builds, or wrong source-control ancestry can look clean while removing coverage. Build completeness is evidence that must be checked before reviewing pixel differences.
How should reviewers handle a shared design-token change that produces many diffs?
Verify representative uses by component category, then inspect outliers and high-impact checkout, authentication, and recovery states. Do not infer that identical highlight regions have equal business impact. The design-system owner can confirm the intended token change, while each product owner judges whether composition and hierarchy remain usable in their surface.
What must be coordinated when Percy tests run in parallel CI shards?
All shards must upload to one intended Percy build with accurate branch and commit context, and finalization must wait for every shard. Pipeline reporting should distinguish no uploads, incomplete collection, render or asset failure, unresolved visual changes, and reviewed completion. The first shard finishing is not evidence that the visual inventory is complete.
RELATED GUIDES
Continue the learning route
GUIDE 01
Visual Regression Testing Guide: Catch UI Changes
Visual regression testing guide for QA and automation teams: learn snapshots, baselines, thresholds, tools, workflows, reviews, and mistakes.
GUIDE 02
Applitools Tutorial: Visual AI Testing for QA Teams
Applitools tutorial for QA teams: learn Visual AI checkpoints, baselines, batches, match levels, integrations, CI review, and visual testing tips.
GUIDE 03
Playwright Tutorial: End-to-End Testing from Scratch
Playwright tutorial for beginners: install, write your first test, TypeScript setup, codegen, fixtures, API testing, debugging, and CI tips in one guide.
GUIDE 04
Cypress Best Practices
Cypress best practices for stable E2E tests: selectors, waits, isolation, Page Objects, network stubs, CI tips, and common mistakes to avoid in real projects.