JS Guide
HomeQuestionsSearchResources
Search

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeQuestionstesting
PrevNext
testing
senior
visual

What is visual regression testing and how do you implement it?

visual-regression
screenshot-testing
chromatic
percy
Quick Answer

Visual regression testing captures screenshots and compares them against baseline images to detect unintended UI changes. Tools like Percy, Chromatic, or Playwright screenshots help catch CSS regressions. Best for component libraries and design systems.

Detailed Explanation

How It Works:

  1. Capture baseline screenshots
  2. Run tests and capture new screenshots
  3. Compare pixel-by-pixel
  4. Flag differences for review
  5. Update baselines if intentional

Popular Tools:

  • Percy: Cloud-based, CI integration
  • Chromatic: Built for Storybook
  • Playwright: Built-in screenshot comparison
  • jest-image-snapshot: Local screenshots

Best Practices:

  • Test at multiple viewports
  • Exclude dynamic content (dates, animations)
  • Review changes carefully
  • Keep baselines in version control
  • Run in consistent environment

Code Examples

Playwright visual testing
// visual.spec.js
import { test, expect } from '@playwright/test';

test.describe('Visual Regression', () => {
  test('homepage matches snapshot', async ({ page }) => {
    await page.goto('/');
    
    // Wait for content to load
    await page.waitForLoadState('networkidle');
    
    // Full page screenshot comparison
    await expect(page).toHaveScreenshot('homepage.png', {
      fullPage: true,
      maxDiffPixels: 100, // Allow small differences
    });
  });

  test('button component states', async ({ page }) => {
    await page.goto('/components/button');
    
    // Screenshot specific element
    const button = page.locator('.primary-button');
    await expect(button).toHaveScreenshot('button-default.png');
    
    // Hover state
    await button.hover();
    await expect(button).toHaveScreenshot('button-hover.png');
    
    // Focus state
    await button.focus();
    await expect(button).toHaveScreenshot('button-focus.png');
  });

  test('responsive layouts', async ({ page }) => {
    await page.goto('/products');
    
    // Test multiple viewports
    const viewports = [
      { width: 1920, height: 1080, name: 'desktop' },
      { width: 768, height: 1024, name: 'tablet' },
      { width: 375, height: 667, name: 'mobile' },
    ];
    
    for (const viewport of viewports) {
      await page.setViewportSize({ width: viewport.width, height: viewport.height });
      await expect(page).toHaveScreenshot(`products-${viewport.name}.png`);
    }
  });
});

Resources

Playwright - Visual Comparisons

docs

Chromatic Documentation

docs

Related Questions

What are E2E tests and when should you use Cypress vs Playwright?

senior
e2e

How do you design a testing strategy for a large application?

senior
strategy
Previous
How do you test application performance and prevent regressions?
Next
What is TDD and how does it compare to BDD?