Modern QA2026Коммерческие инструменты визуальной регрессии
Join

Course10 Visual & Accessibility Testing

Cutting-edge · Chapter 10

Коммерческие инструменты визуальной регрессии

Updated Jul 2026

Percy (BrowserStack)

Percy делает скриншоты в разных браузерах и viewport, затем подсвечивает визуальные изменения с интеллектуальным сравнением. Он интегрируется со всеми основными фреймворками тестирования и предоставляет веб-дашборд для ревью и одобрения изменений.

// tests/visual/homepage.spec.ts
import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';

test('homepage visual regression', async ({ page }) => {
    await page.goto('/');
    await page.waitForLoadState('networkidle');

    // Capture at multiple widths
    await percySnapshot(page, 'Homepage', {
        widths: [375, 768, 1280],
        minHeight: 1024,
        percyCSS: `
            /* Stabilize dynamic content for consistent screenshots */
            [data-testid="timestamp"] { visibility: hidden; }
            [data-testid="avatar"] { visibility: hidden; }
            .animated { animation: none !important; }
        `,
    });
});

test('product card visual regression', async ({ page }) => {
    await page.goto('/products');

    // Wait for images to load
    await page.waitForFunction(() => {
        const images = document.querySelectorAll('img');
        return Array.from(images).every(img => img.complete);
    });

    await percySnapshot(page, 'Product Listing', {
        widths: [375, 1280],
    });
});

test('checkout flow visual regression', async ({ page }) => {
    // Navigate through checkout steps
    await page.goto('/cart');
    await percySnapshot(page, 'Cart Page', { widths: [375, 1280] });

    await page.click('[data-testid="proceed-to-checkout"]');
    await percySnapshot(page, 'Checkout - Shipping', { widths: [375, 1280] });

    await page.fill('[data-testid="shipping-name"]', 'John Doe');
    await page.fill('[data-testid="shipping-address"]', '123 Main St');
    await page.click('[data-testid="continue-to-payment"]');
    await percySnapshot(page, 'Checkout - Payment', { widths: [375, 1280] });
});

Конфигурация Percy для CI

# .github/workflows/percy.yml
name: Percy Visual Tests
on: [pull_request]

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

      - name: Run Percy snapshots
        env:
          PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
        run: npx percy exec -- npx playwright test tests/visual/

Рабочий процесс одобрения Percy: скриншоты из PR-ветки сравниваются с базовой веткой. Неизменённые скриншоты одобряются автоматически. Изменённые скриншоты появляются в дашборде Percy для ревью. Одобренные изменения становятся новым базовым уровнем при слиянии PR.

Chromatic (Storybook)

Chromatic интегрируется напрямую со Storybook для тестирования компонентов в изоляции. Это ловит визуальные регрессии на уровне компонентов до того, как они накопятся в проблемы на уровне страницы.

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
    title: 'Components/Button',
    component: Button,
    parameters: {
        // Chromatic-specific configuration
        chromatic: {
            viewports: [320, 768, 1200],
            delay: 300, // Wait for animations
            diffThreshold: 0.063, // Sensitivity (default is 0.063)
        },
    },
};
export default meta;

type Story = StoryObj<typeof Button>;

export const Primary: Story = {
    args: { variant: 'primary', children: 'Click me' },
};

export const Disabled: Story = {
    args: { variant: 'primary', children: 'Disabled', disabled: true },
};

export const Loading: Story = {
    args: { variant: 'primary', children: 'Loading', loading: true },
    parameters: {
        chromatic: {
            // Capture after loading spinner appears
            delay: 500,
        },
    },
};

export const AllVariants: Story = {
    render: () => (
        <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
            <Button variant="primary">Primary</Button>
            <Button variant="secondary">Secondary</Button>
            <Button variant="danger">Danger</Button>
            <Button variant="ghost">Ghost</Button>
            <Button size="sm">Small</Button>
            <Button size="lg">Large</Button>
        </div>
    ),
};

Интеграция Chromatic с CI

# In your CI pipeline
- name: Run Chromatic
  uses: chromaui/action@latest
  with:
    projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
    exitOnceUploaded: true  # Don't wait for review
    onlyChanged: true       # Only snapshot changed stories
    traceChanged: true      # Detect which stories are affected by code changes

Applitools Eyes

Applitools использует проприетарный AI-движок («Visual AI»), который обеспечивает наиболее продвинутое интеллектуальное сравнение. Он может тестировать одновременно в нескольких браузерах и на нескольких устройствах из одного тестового прогона.

// tests/visual/applitools-checkout.spec.ts
import { test } from '@playwright/test';
import {
    Eyes, Target, BatchInfo, Configuration,
    BrowserType, DeviceName, ScreenOrientation
} from '@applitools/eyes-playwright';

test.describe('Checkout Flow Visual Tests', () => {
    let eyes: Eyes;

    test.beforeEach(async () => {
        eyes = new Eyes();
        const config = new Configuration();
        config.setBatch(new BatchInfo('Checkout Flow'));

        // Test across multiple browsers and devices simultaneously
        config.addBrowser(1200, 800, BrowserType.CHROME);
        config.addBrowser(1200, 800, BrowserType.FIREFOX);
        config.addBrowser(1200, 800, BrowserType.SAFARI);
        config.addDeviceEmulation(DeviceName.iPhone_15, ScreenOrientation.PORTRAIT);
        config.addDeviceEmulation(DeviceName.Pixel_7, ScreenOrientation.PORTRAIT);

        eyes.setConfiguration(config);
    });

    test('cart page visual check', async ({ page }) => {
        await eyes.open(page, 'E-Commerce', 'Cart Page');
        await page.goto('/cart');

        // Full page check with layout matching (ignores text content changes)
        await eyes.check('Cart with items', Target.window()
            .fully()
            .layout()  // Match layout, not exact content
            .ignoreDisplacements()  // Ignore minor position shifts
        );

        // Specific region check with strict matching
        await eyes.check('Price total', Target.region('#order-summary')
            .strict()  // Exact match for financial data
        );

        await eyes.close();
    });

    test.afterEach(async () => {
        await eyes.abort();
    });
});

Уровни сопоставления Applitools

Уровень сопоставления Что сравнивает Сценарий использования
Strict На уровне пикселей, с AI-фильтрацией Финансовые данные, юридические тексты, точный контент
Layout Позиции элементов, размеры, взаимосвязи Страницы с динамическим контентом (имена пользователей, даты)
Content Текстовый контент и структура Страницы, где стили могут варьироваться, но контент важен
Ignore colors Layout + контент, игнорируя изменения цвета Изменения темы, тестирование тёмного режима

Матрица сравнения инструментов

Функция Percy Chromatic Applitools
AI-сравнение Базовое Базовое Продвинутое (Visual AI)
Кроссбраузерный рендеринг Да (облачный рендеринг) Да (облачный рендеринг) Да (Ultrafast Grid)
Интеграция со Storybook Через Percy CLI Нативная (создан для Storybook) Через SDK
Интеграция с Playwright Нативная Через Storybook Нативный SDK
Контроль чувствительности Процент порога Процент порога Несколько уровней сопоставления
Рабочий процесс одобрения Веб-дашборд Веб-дашборд Веб-дашборд
Бесплатный уровень 5 000 снимков/месяц 5 000 снимков/месяц Ограниченный
Лучше всего для Тестирование полных страниц, множество фреймворков Рабочие процессы вокруг Storybook Корпоративное, кроссбраузерное в масштабе
Ценообразование За снимок За снимок За контрольную точку

Выбирайте на основе вашего рабочего процесса: Chromatic, если вы активно используете Storybook, Percy, если нужна широкая поддержка фреймворков, и Applitools, если нужно наиболее продвинутое AI-сравнение и кроссбраузерный рендеринг.