Learnly

Production: CI/CD, sharding, отчёты, flaky-tests

Production: CI/CD, sharding, отчёты, flaky tests, Docker, cloud-сетки

Финальный урок, что отделяет «локально работает» от «надёжно гоняется в CI на каждом PR на 4 браузерах за 6 минут». CI-конфиги для GitHub Actions / GitLab / CircleCI, sharding, отчёты, Docker-образы, BrowserStack/Sauce, борьба с flaky-тестами.

Что мы разобрали в курсе

Глава Главное
1.1 Что такое Playwright архитектура CDP, 5 ключевых решений (auto-wait, multi-context, trace viewer, codegen, multi-browser)
1.2 Когда использовать тестовая пирамида, 9 правильных кейсов, 7 антипаттернов; vs Cypress / Selenium / Cucumber / WebdriverIO
2.1 Архитектура Browser → Context → Page; locator-стратегии (getByRole > Label > TestId > CSS); web-first assertions; network interception
2.2 POM и fixtures Page Object Model, custom fixtures, storageState для авторизации, конфигурация по окружениям, sharding
3.1 Реальный тест codegen → ручная допилка → UI mode → trace viewer; полный e-commerce flow

CI/CD: GitHub Actions (рекомендованный)

.github/workflows/playwright.yml:

name: Playwright Tests
on:
  push:    { branches: [main] }
  pull_request: { branches: [main] }

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1/4, 2/4, 3/4, 4/4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: pnpm }
      - run: pnpm install --frozen-lockfile

      # Установка браузеров с кешем
      - name: Cache Playwright browsers
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: pw-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
      - run: pnpm exec playwright install --with-deps

      # Запуск с шардированием
      - run: pnpm exec playwright test --shard=${{ matrix.shard }}

      # Артефакты
      - if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-${{ strategy.job-index }}
          path: playwright-report/
          retention-days: 14
      - if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-traces-${{ strategy.job-index }}
          path: test-results/
          retention-days: 7

  merge-reports:
    if: always()
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: pnpm install
      - uses: actions/download-artifact@v4
        with: { pattern: playwright-report-*, path: all-reports, merge-multiple: true }
      - run: pnpm exec playwright merge-reports ./all-reports --reporter html
      - uses: actions/upload-artifact@v4
        with: { name: html-report, path: playwright-report/, retention-days: 14 }

Что важно:

  • matrix.shard × --shard=N/M, параллелит на 4 runner'а; ×4 быстрее.
  • actions/cache для ~/.cache/ms-playwright, браузеры (~500MB) скачиваются один раз.
  • --with-deps ставит system-deps (нужно на Ubuntu для WebKit).
  • Artifacts с traces, необходимы для дебага CI-only падений.
  • merge-reports собирает шарды в один HTML-report.

GitLab CI

playwright:
  image: mcr.microsoft.com/playwright:v1.60.0-jammy
  parallel: 4
  script:
    - pnpm install --frozen-lockfile
    - pnpm exec playwright test --shard=${CI_NODE_INDEX}/${CI_NODE_TOTAL}
  artifacts:
    when: always
    paths: [playwright-report/, test-results/]
    expire_in: 14 days

CircleCI / Bitbucket

Все используют тот же principal: matrix split + cache + artifacts. Microsoft публикует официальные Docker-образы для всех CI: mcr.microsoft.com/playwright:v1.60.0-jammy (Ubuntu 22), браузеры предустановлены.

Docker

Локально или в self-hosted CI:

FROM mcr.microsoft.com/playwright:v1.60.0-jammy
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
CMD ["pnpm", "exec", "playwright", "test"]
docker run --rm --ipc=host -v "$PWD":/app -w /app \
  mcr.microsoft.com/playwright:v1.60.0-jammy \
  npx playwright test

--ipc=host обязателен для Chromium/Firefox в Docker (иначе сегфолты).

Cloud-сетки браузеров

Для проверки в реальных мобильных девайсах (а не эмуляторах) и редких версиях браузеров, BrowserStack, SauceLabs, LambdaTest:

// playwright.config.ts
projects: [{
  name: 'browserstack-iphone15',
  use: {
    connectOptions: {
      wsEndpoint: `wss://cdp.browserstack.com/playwright?caps=${
        encodeURIComponent(JSON.stringify({
          'browserstack.username': process.env.BS_USER,
          'browserstack.accessKey': process.env.BS_KEY,
          'browser': 'safari',
          'os': 'ios', 'os_version': '17',
          'device': 'iPhone 15',
        }))
      }`,
    },
  },
}]

Аналогичные интеграции у Sauce Labs и LambdaTest.

Reporters

Reporter Зачем
list дефолт для local, построчно в терминал
html интерактивный отчёт + trace + screenshots
json для пост-обработки (custom dashboard)
junit для CI-систем, которые понимают JUnit XML (Jenkins, TeamCity, Azure DevOps)
github annotations прямо на PR в GitHub
blob бинарный, для merge-reports между шардами
dot минималистичный для long runs
line компактный построчный

В CI обычно ставят несколько:

reporter: process.env.CI
  ? [['blob'], ['github'], ['junit', { outputFile: 'junit.xml' }]]
  : 'list',

Sharding под нагрузку

Эмпирика для middle-size приложения:

  • 50 e2e тестов × 1 worker × 1 shard = 8–15 минут.
  • 200 тестов × 4 workers × 4 shards = 6–10 минут (×8 параллелизм).
  • 500 тестов × 4 workers × 8 shards = 8–15 минут.

Sharding линейно даёт ускорение до железа CI, потом упирается в setup-стоимость каждого runner'а.

Борьба с flaky tests

Flaky test, тот, который иногда падает, иногда нет, без видимых причин в коде продукта.

Built-in retry

// playwright.config.ts
retries: process.env.CI ? 2 : 0,

retries: 2 означает: если тест упал, попробовать ещё дважды. Если хотя бы одна попытка зелёная, тест считается пройденным, но в отчёте помечен как flaky (Playwright различает passed / failed / flaky).

Trace on first retry

use: { trace: 'on-first-retry' }

Trace создаётся только на упавший прогон, не раздувая artifacts.

Стандартные источники flakiness и фиксы

Симптом Причина Фикс
Timed out waiting for selector Selector хрупкий, элемент не появился Заменить на getByRole, увеличить timeout, дождаться waitForResponse
Element is not stable Анимация не закончилась Дать expect(...).toBeVisible() (он сам подождёт) или disable animations в test-mode
Element is outside of the viewport Не проскролилось await locator.scrollIntoViewIfNeeded()
Click was intercepted Поверх есть overlay/модалка Закрыть/дождаться её исчезновения
Race condition с network Тест начал ассерт раньше, чем UI обновился await page.waitForResponse() перед ассертом
Flaky из-за random data Конфликт с реальной БД / параллельным тестом Изоляция через unique-prefix в имени данных
Flaky только в CI Производительность runner Увеличить timeouts, retry, проверить ресурсы

Disable animations в тестовом режиме

// playwright.config.ts
use: {
  reducedMotion: 'reduce',
}

Или CSS-инъекция:

await page.addStyleTag({ content: `
  *, *::before, *::after { transition: none !important; animation: none !important; }
`});

Мониторинг flaky-rate

Долгосрочно, собирайте метрику flaky-rate (% тестов прошедших с retry). > 5%, звонок плохого кода тестов или нестабильного окружения. Готовые сервисы: Currents.dev, Trunk.io, Datadog Test Visibility, Buildkite Test Engine.

Авторизация в CI: secrets и rate limits

  • Test-аккаунт с учёткой через GitHub Secrets / Vault.
  • Не используйте production-аккаунты (rate limits, real money).
  • На каждый shard, свой test-tenant если возможно (избежать data conflicts).
  • Для massive parallel: pre-warm storage state снимком БД и подсоской cookie напрямую.

Performance budget

test('homepage performance', async ({ page }) => {
  const start = Date.now();
  await page.goto('/');
  await expect(page.getByRole('heading')).toBeVisible();
  expect(Date.now() - start).toBeLessThan(2000);

  // Web Vitals
  const lcp = await page.evaluate(() => new Promise(r => {
    new PerformanceObserver(l => r(l.getEntries().pop()?.startTime)).observe({type:'largest-contentful-paint', buffered:true});
  }));
  expect(lcp).toBeLessThan(2500);
});

Не замена Lighthouse CI, но базовая защита от регрессий.

Visual regression в CI

Для рекомендуемого workflow с PR-preview, approve, диффом:

  • Argos CI, open-source-friendly, простой setup.
  • Lost Pixel, open-source.
  • Percy (BrowserStack), managed.
  • Chromatic, Storybook-друг.
  • Applitools Eyes, enterprise, AI-driven.
// Argos пример
import { argosScreenshot } from '@argos-ci/playwright';
test('home', async ({ page }) => {
  await page.goto('/');
  await argosScreenshot(page, 'homepage');
});

В PR появляется превью diff'а, можно approve.

Accessibility в CI

import AxeBuilder from '@axe-core/playwright';

test('homepage a11y', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa'])
    .analyze();
  expect(results.violations).toEqual([]);
});

Сразу ловите regressions accessibility, критично для public-продуктов.

Reference workflows

Smoke (на каждый деплой, ≤ 5 мин)

npx playwright test --grep @smoke --project=chromium

5–15 критических happy-path. Не блокируют PR, блокируют деплой.

Full PR (на каждый PR, ≤ 15 мин)

Всё, кроме @slow. Параллелится 4 шардами.

Nightly (раз в сутки, можно 60 мин)

Все тесты × все браузеры (chromium / firefox / webkit / mobile-safari / mobile-chrome) × visual regression.

On-demand (manual)

Полный visual diff, performance regression, full accessibility scan.

Cost-калькуляция

GitHub Actions free для public repos; для private, Linux 2-core $0.008/min:

  • 4 shards × 8 min × $0.008 × 60 PRs/month = ~$15/месяц.
  • Аналогично nightly full-suite ×3 браузера на 60 мин = $15 + storage.

Cloud-сетки (BrowserStack/Sauce):

  • $99–$199/месяц base plan, далее $$$. Окупается, когда нужно тестировать на реальных iOS/Android.

5 типичных грабель в production

1. Тесты делятся state'ом. Один тест ломает другой. Решение: каждый тест в свой context, ноль shared mutable state, unique data per test.

2. Hard-coded timeouts (page.waitForTimeout(5000)). Главный источник flakiness. Замена: expect(...).toBeVisible(), waitForResponse, waitForURL.

3. Слишком много тестов. > 500 e2e, поддержка дороже бизнес-ценности. Перенесите часть в component/unit.

4. Storage state не обновляется. При смене формы логина старые auth.json ломают CI. Регенерируйте автоматически в setup.

5. Нет trace в CI. Нельзя дебажить CI-only падения. Включить trace: 'on-first-retry' + upload artifacts.

Команда для проекта

  • 1 QA-engineer / SDET (full-time для команды 5–15 разработчиков), pipeline, fixtures, support flaky-tests.
  • Frontend-разработчики пишут тесты сами, это норма в современных командах. SDET, фасилитатор и owner pipeline'а, не главный автор.
  • Platform/DevOps (part-time), CI-инфра, Docker, secrets, scaling.

Что почитать дальше

  • playwright.dev, документация, обновляется к каждой версии.
  • playwright.dev/blog, releases с примерами.
  • github.com/microsoft/playwright/discussions, community Q&A.
  • Test Automation University (Applitools), бесплатные курсы.
  • JS in Plain English / Web.dev articles, современные практики.
  • YouTube: Playwright Tooling, Bryan Ehrhardt, Stefan Judis, практика.

Главное

Playwright в проде, это не «запустил тесты, надеемся на зелёный». Это дисциплинированный pipeline: правильный config, sharding, retries, traces, artifacts, мониторинг flaky-rate. Когда это всё на месте, e2e становятся надёжным защитным слоем, а не источником раздражения.

Главное правило: e2e дороги, поэтому строги к качеству. Меньше тестов, но каждый, стабильный, с понятным trace при падении и понятным владельцем.

AI-тест
1 / 10

Какое основное преимущество систематической проверки веб-приложений?