Visual Regression Testing

Visual Regression Testing — автоматическое обнаружение непреднамеренных визуальных изменений в UI путём попиксельного сравнения скриншотов с эталонными снимками.

Зачем нужно

CSS-изменения легко ломают другие компоненты. Unit и integration тесты не замечают визуальных регрессий — неправильный отступ, перекрытый текст, сломанный layout. Visual regression testing автоматически ловит эти изменения до того, как они попадут в production.

Где используется

  • Design system и UI-библиотеки компонентов
  • Критичные страницы: главная, checkout, формы авторизации
  • После обновления CSS-фреймворка или глобальных стилей
  • При рефакторинге компонентов

Основной контент

Playwright — встроенные visual comparisons

// tests/visual/button.spec.ts
import { test, expect } from '@playwright/test';

test('кнопка выглядит корректно', async ({ page }) => {
  await page.goto('/components/button');

  // Скриншот всей страницы
  await expect(page).toHaveScreenshot('button-page.png');

  // Скриншот конкретного элемента
  const button = page.getByRole('button', { name: 'Primary' });
  await expect(button).toHaveScreenshot('primary-button.png');
});

test('кнопка в состоянии hover', async ({ page }) => {
  await page.goto('/components/button');
  const button = page.getByRole('button', { name: 'Primary' });

  await button.hover;
  await expect(button).toHaveScreenshot('primary-button-hover.png');
});

Обновление baseline скриншотов

# Первый запуск — создаёт эталонные скриншоты
npx playwright test --update-snapshots

# Последующие запуски — сравнивают с эталоном
npx playwright test

Настройка порога различий

// playwright.config.ts
export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,        // допустимое кол-во отличающихся пикселей
      threshold: 0.2,            // допустимый % различий на пиксель (0-1)
      animations: 'disabled',    // отключаем анимации для стабильности
    },
  },
});

Storybook + Chromatic

npm install --save-dev chromatic
npx chromatic --project-token=YOUR_TOKEN

Chromatic автоматически делает скриншот каждой Storybook story и уведомляет при изменениях. В PR показывает visual diff с подсветкой изменений.

Jest + jest-image-snapshot

import { toMatchImageSnapshot } from 'jest-image-snapshot';
import puppeteer from 'puppeteer';

expect.extend({ toMatchImageSnapshot });

test('страница выглядит правильно', async () => {
  const browser = await puppeteer.launch;
  const page = await browser.newPage;
  await page.goto('http://localhost:3000');
  const screenshot = await page.screenshot;

  expect(screenshot).toMatchImageSnapshot({
    failureThreshold: 0.01,
    failureThresholdType: 'percent',
  });

  await browser.close();
});

Папка со скриншотами

tests/
  visual/
    button.spec.ts
    button.spec.ts-snapshots/
      primary-button-chromium-linux.png  ← baseline
      primary-button-hover-chromium-linux.png

Частые ошибки

  • Разные ОС/браузеры дают разные скриншоты — рендеринг шрифтов и antialiasing отличаются; запускай visual tests всегда на одном окружении (Docker в CI)
  • Скриншот с анимацией — CSS-анимация делает скриншоты нестабильными; отключай через animations: 'disabled' или CSS * { animation: none }
  • Commit baseline без проверки — при --update-snapshots в CI автоматически принимаются все изменения; требуй ручного апрува diff в PR
  • Слишком жёсткий threshold — 1 разный пиксель = падение; начинай с разумного порога и ужесточай постепенно

Связанные темы

Ресурсы