Skip to content
Visual Regression Testing: Percy, Applitools, and Snapshot Testing Guide

Visual Regression Testing: Percy, Applitools, and Snapshot Testing Guide

DodaTech Updated Jun 20, 2026 7 min read

Visual regression testing compares screenshots of your application across different states, browsers, and time — catching unintended visual changes that functional tests would miss.

What You’ll Learn

  • What visual regression testing is and why functional tests miss visual bugs
  • How pixel-diff, snapshot, and structural comparison approaches work
  • Setting up Percy for automated visual testing
  • Applitools Eyes for AI-powered visual validation
  • Integrating visual tests into CI/CD pipelines
  • Managing baselines and handling dynamic content

Why Visual Regression Testing Matters

Functional tests verify that elements exist, have correct text, and respond to events. But they don’t see what the user sees — a button that’s 2 pixels too far right, text that overflows its container, or a missing icon. These visual bugs degrade user experience and brand trust. Research shows that 38% of users will stop engaging with a website if the layout is unattractive. Visual regression testing catches these issues automatically.

Doda Browser uses visual regression testing across all platforms and screen sizes — a CSS change that shifts the toolbar on Windows but not macOS must be caught before release.

Learning Path

    flowchart LR
  A[Functional Testing] --> B[Visual Regression<br/>You are here]
  B --> C[Percy Integration]
  C --> D[CI/CD Pipeline]
  D --> E[Cross-Browser Testing]
  style B fill:#f90,color:#fff
  

Approaches to Visual Testing

Pixel-by-Pixel Comparison

Compares every pixel in two screenshots.

Pros: Catches any visible difference. Cons: Extremely sensitive — anti-aliasing, font rendering, and sub-pixel differences cause false positives.

Structural Comparison

Compares the DOM structure and CSS properties rather than rendered pixels.

Pros: Resistant to rendering differences. Cons: Misses visual issues that don’t change the DOM.

Snapshot (Component-Level)

Captures individual components in isolation rather than full pages.

Pros: Targeted, stable, fast. Cons: Doesn’t catch layout interactions between components.

AI-Powered Comparison

AI algorithms identify meaningful visual differences while ignoring anti-aliasing, sub-pixel shifts, and expected animations.

Pros: Fewer false positives, better at identifying real bugs. Cons: Requires cloud service (Applitools).

Percy

Percy is a visual testing platform that integrates with your existing test framework.

Setup

npm install --save-dev @percy/cli @percy/playwright

Percy with Playwright

const { percySnapshot } = require('@percy/playwright');

test('homepage visual regression', async ({ page }) => {
  await page.goto('https://example.com');
  await percySnapshot(page, 'Homepage');
});

test('login page visual regression', async ({ page }) => {
  await page.goto('https://example.com/login');
  await percySnapshot(page, 'Login Page');
});

test('dashboard with data', async ({ page }) => {
  // Set up test data via API
  await page.goto('https://example.com/dashboard');
  await page.waitForSelector('.data-table');
  await percySnapshot(page, 'Dashboard with data');
});

Configuration

# .percy.yml
version: 2
snapshot:
  widths:
    - 375    # Mobile
    - 1280  # Desktop
    - 1440  # Wide desktop
  minHeight: 1024
  enableJavaScript: true
  cli_options:
    - --wait-for-timeout=2000

Running

PERCY_TOKEN=your_token npx percy exec -- npx playwright test

CI Integration

name: Visual Tests
on: [pull_request]

jobs:
  visual:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install --with-deps

      - name: Visual Regression Tests
        env:
          PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
        run: npx percy exec -- npx playwright test --config visual.config.js

      - name: Percy Build Status
        if: always()
        uses: percy/status-action@v1
        with:
          percy-token: ${{ secrets.PERCY_TOKEN }}

Applitools Eyes

Applitools uses AI-powered visual comparison for more intelligent testing:

Setup

npm install --save-dev @applitools/eyes-playwright

Example

const { Eyes, Target } = require('@applitools/eyes-playwright');

test('checkout flow visual test', async ({ page }) => {
  const eyes = new Eyes();
  await eyes.open(page, 'MyApp', 'Checkout Flow');

  await page.goto('https://example.com/checkout');

  // Full page screenshot
  await eyes.check('Checkout page', Target.window().fully());

  // Region-specific check
  await eyes.check(
    'Price summary',
    Target.region('.price-summary')
  );

  // Fill form and check again
  await page.fill('#email', 'user@example.com');
  await eyes.check('Checkout with data', Target.window());

  await eyes.close();
});

Key Features

  • Universal comparison: Works across browsers, devices, and operating systems
  • Automatic baseline management: New snapshots become baselines, changes flag for review
  • Content-aware matching: Ignores irrelevant changes (animations, dynamic data)
  • Layout regions: Define areas where only layout matters, not content

Snapshot Testing in Jest

Jest snapshot testing is a lightweight alternative for component-level visual testing:

// Button.test.js
import { render } from '@testing-library/react';
import Button from './Button';

test('button matches snapshot', () => {
  const { container } = render(
    <Button variant="primary" size="large" disabled={false}>
      Submit
    </Button>
  );
  expect(container).toMatchSnapshot();
});

test('disabled button matches snapshot', () => {
  const { container } = render(
    <Button variant="secondary" size="small" disabled={true}>
      Cancel
    </Button>
  );
  expect(container).toMatchSnapshot();
});
// __snapshots__/Button.test.js.snap
exports[`button matches snapshot 1`] = `
<button
  class="btn btn-primary btn-large"
  type="button"
>
  Submit
</button>
`;

Updating Snapshots

# Update all snapshots
jest --updateSnapshot

# Interactive mode
jest --watch
# Press 'u' to update failing snapshots

Managing Baselines

Visual test baselines are the “correct” reference images:

Workflow

  1. When a PR creates new snapshots, they become baselines
  2. When snapshots differ from baselines, the build is flagged
  3. A reviewer visually inspects the diff and approves or rejects
  4. Approved diffs become new baselines

Best Practices

  • Review every diff before merging — what looks like an unintended change might be the intended fix
  • Know your false positive sources: Font rendering, OS-level anti-aliasing, time-based content
  • Use dedicated snapshot names that describe the state being captured
  • Keep snapshots focused on one component or page section at a time

Handling Dynamic Content

Dynamic content (dates, user names, random data) causes false failures:

/* Percy CSS — hide dynamic elements during capture */
#current-time,
.user-avatar,
.random-banner {
  visibility: hidden;
}
// Dynamic content regions for Applitools
await eyes.check('Dashboard', Target.window()
  .ignoreRegions(
    Target.region('.clock'),
    Target.region('.user-greeting')
  )
  .layoutRegions(
    Target.region('.news-feed')  // Check layout, not content
  )
);

Common Visual Testing Mistakes

1. Testing Full Pages Instead of Components

Full-page snapshots break on any change anywhere on the page.

Fix: Test individual components or page sections. Use Percy’s per-element snapshots.

2. Not Managing Baselines

Snapshots that aren’t reviewed and approved accumulate stale baselines.

Fix: Review visual diffs before merging. Update baselines intentionally.

3. Ignoring Dynamic Content

Dates, timers, live data, and random elements cause constant false failures.

Fix: Mock dynamic content, freeze dates, or use ignore regions.

4. Too Many Snapshots

Hundreds of snapshots per PR make review overwhelming. Each diff requires human judgment.

Fix: Be selective. Test critical pages and components. Delete unused snapshots.

5. Testing Only Default State

Testing only the initial page load misses interactive state changes.

Fix: Capture snapshots after interactions — form filled, dropdown open, error state, loading state.

6. No Cross-Browser Visual Testing

A page that looks perfect in Chrome might be broken in Firefox or Safari.

Fix: Run visual tests on at least Chrome, Firefox, and Safari.

7. Noisy CI Output

Hundreds of snapshot diff images in CI output make it hard to find real issues.

Fix: Use a visual testing service (Percy, Applitools) with a web interface for reviewing diffs.

Practice Questions

1. What is the difference between pixel-diff and AI-powered visual comparison?

Pixel-diff compares every pixel and flags any difference. AI-powered comparison identifies meaningful visual changes while ignoring anti-aliasing, sub-pixel shifts, and expected animations.

2. What is a visual test baseline?

A reference screenshot that represents the expected correct appearance. New snapshots are compared against baselines to detect visual changes.

3. How does Percy integrate with existing test frameworks?

Percy wraps your test runner (percy exec -- npx playwright test) and automatically captures snapshots when percySnapshot() is called.

4. How do you handle dynamic content in visual tests?

Use CSS to hide dynamic elements, ignore regions (Applitools), or mock the data source to return consistent values.

5. What is the recommended approach for reviewing visual diffs?

Use a visual testing service with a web interface. Each diff should be reviewed by a human before merging. Approved diffs become new baselines.

Challenge: Set up Percy for an existing project with 5 critical pages/components. Run visual tests in CI, create a PR with a visual change, and practice reviewing and approving diffs.

FAQ

Do visual regression tests replace functional tests?
No. Visual tests catch visual bugs. Functional tests catch logical bugs. You need both.
How often should I update visual baselines?
Update when you intentionally change the UI. Review every PR that touches CSS, templates, or components.
Are visual tests slow?
They’re slower than unit tests but faster than manual visual inspection. Percy and Applitools optimize by uploading screenshots for cloud-based comparison.
Can visual tests run on real mobile devices?
Yes. Services like Percy and Applitools support mobile viewports and real device testing through integrations with BrowserStack and Sauce Labs.
How do I prevent false positives from font rendering differences?
Use CI environments with consistent fonts (Docker), or use AI-powered comparison that ignores font rendering differences.

What’s Next

TutorialWhat You’ll Learn
Percy Advanced ConfigurationPercy CLI, parallel builds, and integrations
Applitools Eyes GuideAI-powered visual testing with Applitools
Cross-Browser Testing StrategiesTesting across browsers and devices

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro