Визуальное QA как этап конвейера CI

Месяц назад я смержил PR. Code review выглядел хорошо. Тесты прошли. Потом я открыл сайт на своём телефоне, и боковая панель была полностью сломана.

Исправление было тривиальным — отсутствующая media query. Баг был очевиден, как только ты действительно посмотрел на мобильный вид. Никто не посмотрел.

Поэтому я добавил этап конвейера, который смотрит.

Я открываю issue на GitHub, которое говорит:

Реализовать опцию Ручного Ввода для добавления клиентов. При клике открывается широкий выдвижной ящик с формой для создания нового клиента.

Один коммит спустя, PR прилетает с 30+ скриншотами, доказывающими, что каждое состояние работает на каждом viewport. Нулевое ручное тестирование. Единственное усилие — написать описание функции.

Концепция

При каждом push в PR, workflow GitHub Actions:

  1. Запускает Docker Compose стек (Django + Postgres) из ветки PR
  2. Выполняет миграции и создаёт тестового пользователя
  3. Передаёт локальный URL Claude Code с Playwright MCP (headless Chromium)
  4. Тестирует интерактивные элементы на основе PR diff
  5. Делает скриншоты каждого состояния на трёх viewport’ах
  6. Публикует результаты как комментарий к PR

Claude Code управляет браузером. Docker Compose предоставляет приложение. GitHub Actions связывает всё вместе. Ревьюеры видят доказательство функциональности без локального запуска чего-либо.

%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 3, "bottom": 10}}} }%%
graph TD
    A["Push в PR"] --> B

    subgraph GHA["GitHub Actions Runner"]
        B["Checkout + Docker Compose"] --> C["Миграции + Тестовые Данные"]
        C --> Agent

        subgraph Agent["Claude Code + Playwright"]
            direction LR
            D["Читать PR Diff"] --> E["Сопоставить Файлы → URLs"]
            E --> F["Вход + Навигация"]
            F --> G["Скриншот ×3 Viewport'а"]
        end
    end

    Agent --> H["Комментарий к PR"]

    style GHA fill:transparent,stroke:#888
    style Agent fill:transparent,stroke:#888

Сам workflow прямолинеен:

name: UI Screenshots
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  screenshots:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      # ... Настройка Docker Compose, миграции, создание тестового пользователя ...

      - name: Start services
        run: |
          docker compose -f docker-compose.local.yml up -d --wait

      - name: Install Playwright
        run: npx playwright install --with-deps chromium

      - name: Run Claude for Screenshots
        uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          claude_args: >-
            --allowed-tools
            "mcp__playwright__*,
            Bash(gh pr diff:*),
            Bash(gh pr comment:*),
            Read,Glob,Grep"
          prompt: |
            You are a visual QA agent. Your job is to visually
            verify a UI deployment.

            PR: ${{ github.event.pull_request.number }}
            The Django app is running at http://localhost:8001

            1. Run `gh pr diff` to find changed templates/views
            2. Map changed files to URLs — only test pages
               affected by this push
            3. Login, then screenshot each page at three viewports:
               1440x900, 1024x768, 640x1136
            4. Save screenshots with descriptive filenames
            5. Post a PR comment with:
               - Each test performed with pass/fail
               - Any visual issues found
               - Total screenshots captured

Матрица Viewport’ов

УстройствоРазмерПочему
Десктоп1440×900Стандартный ноутбук
Планшет1024×768iPad портретная ориентация
Мобильный640×1136iPhone SE

Каждое интерактивное состояние захватывается на всех трёх. Компонент боковой панели генерирует 12+ скриншотов:

Умножьте на светлую/тёмную темы, и вы смотрите на 24 скриншота для одного компонента. В этом суть. Исчерпывающее визуальное доказательство как артефакт CI.

Что Это Реально Выдаёт

Вот реальный вывод из PR, который добавил страницу ручного ввода. Агент публикует комментарий к PR со структурированным резюме, скриншотами на каждом viewport и чек-листом тестов:

GitHub issue, созданный агентом визуального QA

Под этим резюме агент добавляет скриншоты каждого протестированного состояния. Вот десктопный вид — список клиентов с данными, плюс открытый ящик Добавить Клиента:

Десктопный вид: список клиентов и ящик Добавить Клиента на 1440x900

Агент протестировал:

Я пропустил индивидуальные состояния фокуса полей для этого PR — убывающая отдача на простой форме. Агент может это сделать, но 50 скриншотов текстовых полей, получающих фокус, не является полезным сигналом.

Комментарий к PR становится визуальным changelog’ом. Каждый запуск теста заканчивается чек-листом:

Чек-лист Выполненного Тестирования со всеми пройденными пунктами

Через шесть месяцев я могу посмотреть на любой PR и увидеть точно, как выглядел UI при отправке.

Манифест Тестов

Промпт в workflow выше — общий, он работает для любого PR. Специфичность приходит из самого diff. Агент читает gh pr diff, выясняет, какие шаблоны и представления изменились, сопоставляет их с URL и решает, что тестировать.

Для компонента формы это означает, что он будет тестировать:

component: ClientForm
states:
  - empty form
  - filled form (valid data)
  - validation error (submit without name)
  - submitting (loading state)
  - success (confirmation)
interactions:
  - submit empty form (trigger validation)
  - fill all fields, submit
  - verify client appears in list after submit

Ручной план тестирования не нужен. Агент выводит тестовую поверхность из изменений кода.

Почему Скриншоты в Комментариях к PR

Встроенные скриншоты в комментарии к PR имеют несколько преимуществ:

  1. Видимые — ревьюеры видят их без клика на артефакты
  2. Контекстуальные — прямо рядом с diff, который они ревьюят
  3. Сравнимые — каждый push обновляет комментарий, так что вы видите до/после
  4. Проверяемые — история комментариев показывает, что было протестировано на каждом коммите

Что Это Ловит

Реальные баги, пойманные в первую неделю:

Каждый из них прошёл code review. Каждый был очевиден на скриншотах.

Цена

Запуск Claude с Playwright MCP в CI занимает 2-4 минуты в зависимости от того, сколько состояний нужно протестировать. Для типичного PR, затрагивающего один компонент, это около 90 секунд.

Сравните с: развёртыванием в продакшн и узнаванием от пользователей, что мобильный layout сломан. Бесценно.

Сдвиг

Визуальное QA всегда было узким местом. Вы можете автоматизировать unit-тесты, интеграционные тесты, даже end-to-end потоки — но кто-то всё ещё должен посмотреть на UI. Это было правдой десятилетиями.

Это больше не правда. Агенты с браузерами не просто запускают скрипты. Они интерпретируют, навигируют, взаимодействуют и судят. Тестовая поверхность не захардкожена — она выводится из изменения. Каждый PR получает исчерпывающее визуальное покрытие, которое не может сравниться ни с каким ручным процессом.

Это меняет QE с ворот на гарантию. Не “кто-то это проверил”, а “конвейер это проверил, вот доказательство”. Каждый PR, каждый push, каждый viewport. Роль QE не исчезает. Она движется вверх по потоку. Вместо выполнения планов тестирования вы определяете, о чём должен заботиться агент. Вместо ловли багов вы проектируете систему, которая их ловит.