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 при падении и понятным владельцем.