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>
32 lines
1.9 KiB
TypeScript
32 lines
1.9 KiB
TypeScript
/**
|
||
* Sprint 16 — visual regression helper.
|
||
*
|
||
* Подготавливает одинаковый кеш + локаль для всех snapshot-тестов.
|
||
* Маскирует «гуляющие» области (badges с временем, live-clock, тут
|
||
* стоит расти со временем).
|
||
*
|
||
* `applyTheme(page, 'dark')` — переключает тему через localStorage и
|
||
* reload. Sprint 14 ввёл lazy-chunk'и, страница может «доезжать»
|
||
* после loadstate=networkidle (тёмная вспышка через 50мс при первом
|
||
* рендере dashboard'a) — добавляем page.waitForTimeout(300).
|
||
*/
|
||
import type { Page } from '@playwright/test'
|
||
|
||
export async function applyTheme(page: Page, theme: 'light' | 'dark'): Promise<void> {
|
||
await page.evaluate((t) => localStorage.setItem('fm.theme', t), theme)
|
||
await page.reload({ waitUntil: 'networkidle' })
|
||
// Дать React закончить mount lazy-чанков (Recharts, DashboardWidgets).
|
||
await page.waitForTimeout(300)
|
||
}
|
||
|
||
/** Стандартные маски для всех страниц: live-clock, текущая дата в title
|
||
* страницы, аватар с инициалами от случайного email'а (digits меняются от
|
||
* прогона к прогону). Локаторы — `.text-slate-500` это часто
|
||
* «timestamp»-подписи; маска без выбора нужного фрагмента слишком грубая,
|
||
* лучше маскировать только конкретный CSS-селектор. */
|
||
export const TIMESTAMP_MASK_SELECTORS: string[] = [
|
||
'[data-live-clock]', // компонент Wifi/WifiOff в sidebar показывает текущее время
|
||
'[data-user-initials]', // аватар инициалов (зависит от email)
|
||
'.dashboard-stat-delta', // KPI delta — текущая дата
|
||
]
|