Sprint 16 — постоянный regression-контур: flows + visual + nightly +
CI workflow + README badges.
Ключевые цифры:
- 35 flow-тестов: 35/35 ✓ за ~30 секунд (workers=2 локально).
- 60 visual snapshot'ов (15 страниц × 2 темы × 2 viewport'a):
60/60 ✓ за ~4 минуты с retries=1.
- Полный регресс прогон: ~5 минут (цель была < 15).
Что сделано:
1. tests/regression/ — Playwright + factories + 8 spec-файлов.
OrgFactory builder создаёт org через API за O(N) HTTP вызовов
(signup → token → refs → products → counterparties → posted supplies).
Каждый flow независим, использует свой fresh-org.
2. tests/regression/visual/ — 15 страниц × 2 темы × 2 viewport'a.
Маски на динамический контент (артикулы с Date.now, KPI'ы,
delta-стрелки) чтобы 0.2% threshold не флакал. snapshotPathTemplate
c {projectName} — desktop+mobile не затирают друг друга.
3. tests/regression/factories/OrgFactory.ts — builder с .withProducts
.withCounterparties .withSupplies. Retry signup'a на 429.
4. .forgejo/workflows/regression.yml — on workflow_run после
Docker API/Web; cache на pnpm-store + Playwright-browsers;
артефакты при failure; Telegram-уведомление в обоих случаях.
5. ~/nightly-verify.sh + cron `0 4 * * *`: health → redeploy если
нужно → smoke flows; в воскресенье полный flows+visual. Логи с
ротацией 14 дней. Telegram на провал (~/.fm-watchdog/telegram-*).
6. scripts/generate-badges.sh — coverage из cobertura.xml в SVG через
shields.io (offline fallback). 4 CI-status badge + coverage badge в
README; CI step «Update coverage badge» авто-коммитит обновлённый
SVG на push в main.
Локальное число flake'ов: 1/60 visual на retry=1 (product-new light) —
случайная гонка маски, retry'ит и проходит.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
import { defineConfig, devices } from '@playwright/test'
|
||
|
||
/**
|
||
* Sprint 16: regression suite playwright config.
|
||
*
|
||
* Запуск:
|
||
* pnpm test # все: flows + visual
|
||
* pnpm test:flows # только flows
|
||
* pnpm test:visual # только visual
|
||
* pnpm test:smoke # tagged @smoke (быстрый прогон)
|
||
* pnpm test:update-snapshots # обновить visual baseline
|
||
*
|
||
* Env:
|
||
* E2E_ADMIN_URL — base URL (default https://test.admin.food-market.kz)
|
||
* CI=1 — retries=1, fail-fast=false
|
||
* WORKERS — override default workers (4 на CI, 2 локально)
|
||
*/
|
||
const baseURL = process.env.E2E_ADMIN_URL ?? 'https://test.admin.food-market.kz'
|
||
const isCI = !!process.env.CI
|
||
const workers = process.env.WORKERS ? Number(process.env.WORKERS) : (isCI ? 4 : 2)
|
||
|
||
export default defineConfig({
|
||
testDir: '.',
|
||
testMatch: /(flows|visual)\/.*\.spec\.ts$/,
|
||
// Параллелизм — каждый flow создаёт свою org через factory, поэтому
|
||
// shared state нет. Workers ограничены чтобы не перегрузить stage'е
|
||
// signup rate-limit.
|
||
fullyParallel: true,
|
||
forbidOnly: isCI,
|
||
retries: isCI ? 1 : 0,
|
||
workers,
|
||
timeout: 60_000,
|
||
expect: {
|
||
timeout: 10_000,
|
||
// Sprint 16: visual diff threshold — 0.2% pixel-level. Font-rendering
|
||
// antialiasing чуть «гуляет» между виртуалками; 0.2% даёт устойчивость
|
||
// и ловит реальные визуальные изменения. Animations отключаем на снапшоте.
|
||
toHaveScreenshot: {
|
||
maxDiffPixelRatio: 0.002,
|
||
animations: 'disabled',
|
||
},
|
||
},
|
||
reporter: [
|
||
['list'],
|
||
['json', { outputFile: 'reports/results.json' }],
|
||
['html', { outputFolder: 'reports/playwright-html', open: 'never' }],
|
||
],
|
||
use: {
|
||
baseURL,
|
||
headless: true,
|
||
ignoreHTTPSErrors: true,
|
||
locale: 'ru-RU',
|
||
viewport: { width: 1280, height: 800 },
|
||
actionTimeout: 15_000,
|
||
navigationTimeout: 30_000,
|
||
screenshot: 'only-on-failure',
|
||
trace: 'retain-on-failure',
|
||
video: 'retain-on-failure',
|
||
},
|
||
outputDir: 'reports/playwright-artifacts',
|
||
// {projectName} разделяет desktop и mobile snapshot'ы в подкаталогах,
|
||
// иначе обновление одного проекта затирало бы другой.
|
||
snapshotPathTemplate: '{testDir}/__screenshots__/{projectName}/{testFileName}/{arg}{ext}',
|
||
projects: [
|
||
{
|
||
name: 'desktop-chromium',
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1280, height: 800 },
|
||
},
|
||
},
|
||
{
|
||
name: 'mobile-chromium',
|
||
testMatch: /visual\/.*\.spec\.ts$/, // mobile только для visual
|
||
use: {
|
||
...devices['Pixel 5'],
|
||
viewport: { width: 375, height: 667 },
|
||
},
|
||
},
|
||
],
|
||
})
|