Инструменты визуальной регрессии с открытым исходным кодом
Updated Jul 2026
Обзор инструментов
| Инструмент | Подход | Интеграция | Готов к CI | Лучше всего для |
|---|---|---|---|---|
| Playwright (встроенный) | toHaveScreenshot() + попиксельный diff |
Нативная поддержка Playwright | Да | Команды, уже использующие Playwright |
| BackstopJS | Скриншоты Puppeteer + попиксельный diff | Конфигурационный файл + CLI | Да | Простые проекты, быстрая настройка |
| reg-suit | Хранение в S3/GCS + сравнение изображений | На основе плагинов | Да | Кастомные пайплайны, self-hosted |
| Loki | Скриншоты Storybook + diff | Аддон Storybook | Да | Тестирование компонентов (пользователи Storybook) |
| Lost Pixel | Скриншоты Next.js/Storybook | GitHub Action | Да | Проекты на Next.js и Storybook |
Встроенное визуальное тестирование Playwright
Playwright включает сравнение скриншотов из коробки без дополнительных зависимостей. Это рекомендуемая отправная точка для большинства команд.
// tests/visual/playwright-native.spec.ts
import { test, expect } from '@playwright/test';
test('login page matches baseline', async ({ page }) => {
await page.goto('/login');
// Full page screenshot comparison
await expect(page).toHaveScreenshot('login-page.png', {
fullPage: true,
maxDiffPixelRatio: 0.01, // Allow 1% pixel difference
threshold: 0.2, // Per-pixel color threshold (0-1)
animations: 'disabled', // Freeze animations for consistency
});
});
test('navigation component matches baseline', async ({ page }) => {
await page.goto('/');
// Component-level screenshot
const nav = page.locator('nav[data-testid="main-nav"]');
await expect(nav).toHaveScreenshot('main-navigation.png', {
maxDiffPixelRatio: 0.005,
});
});
test('modal dialog matches baseline', async ({ page }) => {
await page.goto('/');
await page.click('[data-testid="open-modal"]');
// Mask dynamic content before capturing
await expect(page).toHaveScreenshot('confirmation-modal.png', {
mask: [
page.locator('[data-testid="timestamp"]'),
page.locator('[data-testid="user-avatar"]'),
page.locator('[data-testid="order-id"]'),
],
});
});
Конфигурация скриншотов Playwright
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
toHaveScreenshot: {
// Default comparison settings
maxDiffPixelRatio: 0.01,
threshold: 0.2,
animations: 'disabled',
},
},
// Store baselines in a dedicated directory
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
});
Обновление базовых скриншотов
# Update all baselines (after intentional changes)
npx playwright test --update-snapshots
# Update baselines for specific tests
npx playwright test tests/visual/homepage.spec.ts --update-snapshots
# Review changes in a side-by-side report
npx playwright show-report
Обработка кроссплатформенных различий
Скриншоты Playwright различаются на разных операционных системах из-за рендеринга шрифтов. Обрабатывайте это с помощью платформо-специфичных базовых скриншотов:
// Playwright automatically stores OS-specific baselines:
// __screenshots__/login-page-chromium-linux.png
// __screenshots__/login-page-chromium-darwin.png
// __screenshots__/login-page-chromium-win32.png
// Or use a Docker container for consistent rendering:
// docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.48.0-noble \
// npx playwright test --update-snapshots
BackstopJS
BackstopJS предоставляет подход к визуальному тестированию на основе конфигурации. Определите сценарии в JSON-файле, и BackstopJS возьмёт на себя захват скриншотов, сравнение и отчётность.
{
"id": "my-app-visual-tests",
"viewports": [
{ "label": "phone", "width": 375, "height": 812 },
{ "label": "tablet", "width": 768, "height": 1024 },
{ "label": "desktop", "width": 1440, "height": 900 }
],
"scenarios": [
{
"label": "Homepage",
"url": "http://localhost:3000/",
"delay": 1000,
"misMatchThreshold": 0.1,
"requireSameDimensions": true,
"hideSelectors": [".dynamic-timestamp", ".user-avatar"],
"removeSelectors": [".cookie-banner"]
},
{
"label": "Login Form",
"url": "http://localhost:3000/login",
"delay": 500,
"selectors": ["[data-testid='login-form']"],
"selectorExpansion": true,
"misMatchThreshold": 0.05
},
{
"label": "Dashboard - After Login",
"url": "http://localhost:3000/dashboard",
"delay": 2000,
"cookiePath": "test-data/auth-cookies.json",
"hideSelectors": [".chart-animation", ".live-counter"]
}
],
"engine": "playwright",
"report": ["browser", "CI"],
"ci": {
"format": "junit",
"testReportFileName": "backstop-results",
"testSuiteName": "Visual Regression"
}
}
Команды BackstopJS
# Create initial baselines
npx backstop reference
# Run comparison against baselines
npx backstop test
# Approve current screenshots as new baselines
npx backstop approve
# Open the visual report in a browser
npx backstop openReport
Продвинутые функции BackstopJS
{
"label": "Product Detail - Hover State",
"url": "http://localhost:3000/products/1",
"hoverSelector": ".add-to-cart-btn",
"postInteractionWait": 500,
"misMatchThreshold": 0.1
},
{
"label": "Mobile Menu Open",
"url": "http://localhost:3000/",
"viewports": [{ "label": "mobile", "width": 375, "height": 812 }],
"clickSelector": ".hamburger-menu",
"postInteractionWait": 500,
"scrollToSelector": ".menu-drawer"
},
{
"label": "Form Validation Errors",
"url": "http://localhost:3000/register",
"onReadyScript": "scripts/submit-empty-form.js",
"delay": 1000,
"selectors": ["[data-testid='registration-form']"]
}
Lost Pixel
Lost Pixel разработан специально для проектов на Next.js и Storybook, с GitHub Action для бесшовной интеграции в CI.
# .github/workflows/lost-pixel.yml
name: Lost Pixel
on: [pull_request]
jobs:
visual:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build-storybook
- name: Lost Pixel
uses: lost-pixel/lost-pixel@v3
env:
LOST_PIXEL_API_KEY: ${{ secrets.LOST_PIXEL_API_KEY }}
// lostpixel.config.ts
import { CustomShot } from 'lost-pixel';
export const config = {
storybookShots: {
storybookUrl: './storybook-static',
},
pageShots: {
pages: [
{ path: '/', name: 'homepage' },
{ path: '/products', name: 'products' },
{ path: '/login', name: 'login' },
],
baseUrl: 'http://localhost:3000',
},
threshold: 0.05,
generateOnly: false,
};
Выбор подходящего инструмента
| Сценарий | Рекомендуемый инструмент | Обоснование |
|---|---|---|
| Уже используете Playwright | Playwright (встроенный) | Нулевые дополнительные зависимости |
| Рабочий процесс вокруг Storybook | Chromatic (коммерческий) или Loki (OSS) | Тестирование на уровне компонентов |
| Простой проект, быстрая настройка | BackstopJS | На основе конфигурации, без кода |
| Проект на Next.js | Lost Pixel | Создан для Next.js + Storybook |
| Кастомный пайплайн, self-hosted | reg-suit | Гибкие бэкенды хранения |
| Нужен AI-diff | Chromatic или Percy (коммерческие) | OSS-инструменты используют только попиксельный diff |
Все инструменты с открытым исходным кодом имеют одно фундаментальное ограничение: попиксельное сравнение. Они будут давать ложные срабатывания на различиях рендеринга шрифтов, сглаживании и субпиксельном позиционировании. Коммерческие инструменты (Percy, Chromatic, Applitools) добавляют AI-слой для снижения этого шума. Для большинства команд начать со встроенных скриншотов Playwright и перейти на коммерческий инструмент, когда ложные срабатывания станут болезненными — правильная траектория развития.