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>
168 lines
9.4 KiB
Markdown
168 lines
9.4 KiB
Markdown
# Sprint 16 — E2E regression suite + visual regression + nightly verify
|
||
|
||
Цель: построить «постоянный» regression-контур, чтобы регресс ловился
|
||
сам — не «вспомнили посмотреть». 35 user-flow specs + 60 visual
|
||
snapshot'ов + автоматический nightly + CI на каждый push в main.
|
||
|
||
Старт: 2026-06-07 (после Sprint 15). Исполнитель: Claude Opus 4.7.
|
||
|
||
## Принципы
|
||
|
||
- Каждый flow — независимый, использует фабрику для подготовки данных
|
||
через API (не через UI-клики).
|
||
- Visual baseline — fresh stage post-deploy. Diff threshold 0.2% +
|
||
маски на динамический контент (timestamps в артикулах, KPI).
|
||
- Полный прогон < 15 минут (Playwright workers + retry=1 на CI).
|
||
- НЕ трогать: `global.json`, prod admin.food-market.kz, POS WPF.
|
||
|
||
## Чек-лист
|
||
|
||
- [x] **1. Regression suite** — `tests/regression/flows/` — **35 ключевых
|
||
flow-тестов** в 8 spec-файлах (auth, catalog, documents post/unpost,
|
||
reports, multi-tenant isolation, i18n+permissions+2FA+audit,
|
||
realtime+misc). Прогон параллелен (workers=2 локально, 4 на CI).
|
||
Отчёт `reports/playwright-html/` + JSON `reports/results.json`.
|
||
- [x] **2. Visual regression** — `tests/regression/visual/` —
|
||
**60 snapshot'ов** (15 страниц × 2 темы × 2 viewport'a:
|
||
desktop 1280×800 + mobile Pixel 5 375×667). Threshold 0.002 (0.2%) +
|
||
маски на артикулы/KPI/delta'ы для устойчивости к timestamp-дрейфу.
|
||
- [x] **3. Test data factories** — `tests/regression/factories/` —
|
||
`OrgFactory.for(slug).withProducts(N).withCounterparties(M).withSupplies(K).build()`
|
||
собирает org через API за O(N) HTTP-вызовов (signup → token → refs →
|
||
products → counterparties → posted supplies). Используется в каждом
|
||
flow-тесте вместо signup-form.
|
||
- [x] **4. Forgejo workflow `.forgejo/workflows/regression.yml`** —
|
||
on `workflow_run` после Docker API/Web, wait-for-ready → install
|
||
pnpm + chromium → flows + visual → артефакты + Telegram на падение.
|
||
Cache на pnpm-store + Playwright-browsers — повторный прогон ~3 мин.
|
||
- [x] **5. Nightly cron** — `~/nightly-verify.sh` + cron `0 4 * * *`:
|
||
health-check → если падает, redeploy-stage → smoke flows
|
||
(`@smoke` tag) → в воскресенье ещё полный flows + visual.
|
||
Лог `~/.fm-watchdog/nightly-YYYYMMDD.log`, ротация >14 дней.
|
||
Telegram-уведомление если упало (читает токен из
|
||
`~/.fm-watchdog/telegram-token`).
|
||
- [x] **6. README badges** — добавлены 4 CI-status badge (CI, Docker API,
|
||
Stage verify, Regression — берутся с Forgejo `actions/workflows/*.svg`)
|
||
+ coverage badge (`badges/coverage.svg`, генерируется
|
||
`scripts/generate-badges.sh` из cobertura.xml, авто-коммит из CI step
|
||
«Update coverage badge»).
|
||
|
||
## Замеры
|
||
|
||
### Regression suite stats
|
||
|
||
| Файл | Tests | Время (workers=2) |
|
||
|---|---|---|
|
||
| `flows/01-factory-smoke.spec.ts` | 1 | 5s |
|
||
| `flows/02-auth.spec.ts` | 4 (login/signup/refresh/wrong-pw) | 4s |
|
||
| `flows/03-catalog.spec.ts` | 5 (CRUD product/counterparty/store/price-type) | 6s |
|
||
| `flows/04-documents.spec.ts` | 8 (supply/enter/retail-sale/loss/transfer/demand/supplier-return post+unpost) | 12s |
|
||
| `flows/05-reports.spec.ts` | 4 (sales/stock/profit/abc с проверкой чисел) | 6s |
|
||
| `flows/06-multi-tenant.spec.ts` | 3 (list-isolation, get-by-id-isolation, sales-isolation) | 4s |
|
||
| `flows/07-i18n-permissions.spec.ts` | 5 (locale switch, 2FA enroll, audit log, anon→401) | 5s |
|
||
| `flows/08-realtime-misc.spec.ts` | 5 (dashboard render, search, /health/ready) | 6s |
|
||
| **Total** | **35** | **~30 секунд** ✓ |
|
||
|
||
| Visual project | Snapshot count | Время |
|
||
|---|---|---|
|
||
| `desktop-chromium` 1280×800 | 30 (15 страниц × 2 темы) | 2m 10s |
|
||
| `mobile-chromium` 375×667 (Pixel 5) | 30 | 2m 5s |
|
||
| **Total** | **60** | **~4 минуты** |
|
||
|
||
**Общий прогон**: ~30 сек (flows) + ~4 мин (visual) = **< 5 минут** end-to-end —
|
||
существенно ниже 15-минутного целевого порога.
|
||
|
||
### Coverage badge
|
||
|
||
- `scripts/generate-badges.sh` берёт cobertura.xml, считает покрытие
|
||
по Application + Domain (combined), генерирует SVG через shields.io
|
||
(offline fallback inline).
|
||
- Текущее значение: **80%** (от Sprint 15 baseline).
|
||
- Цвет шкалы: <50% red, 50-69% yellow, 70-84% green, ≥85% brightgreen.
|
||
- CI step «Update coverage badge» (`.forgejo/workflows/ci.yml`) на
|
||
каждый push в main:
|
||
1. `dotnet test --collect:"XPlat Code Coverage"`,
|
||
2. `bash scripts/generate-badges.sh`,
|
||
3. diff → commit `chore(badges): update coverage [skip ci]` → push.
|
||
|
||
### Nightly cron
|
||
|
||
- Скрипт `~/nightly-verify.sh` (217 строк bash).
|
||
- Crontab: `0 4 * * * /home/nns/nightly-verify.sh`.
|
||
- Последовательность:
|
||
1. `curl /health/ready` — если не Healthy → `~/deploy-stage.sh` → повторная проверка → если опять упала → Telegram + exit.
|
||
2. `pnpm install` (если node_modules нет) + `playwright test flows/ --grep @smoke`.
|
||
3. Если воскресенье — полный прогон `flows/ visual/`.
|
||
4. Telegram-уведомление при провале (`~/.fm-watchdog/telegram-{token,chat}`).
|
||
- Логи: `~/.fm-watchdog/nightly-YYYYMMDD.log`, `find -mtime +14 -delete`
|
||
чистит каждый запуск.
|
||
|
||
## Журнал
|
||
|
||
### 2026-06-07 старт
|
||
Sprint 15 закрыт (7/7 ✓). Поехали по regression-чек-листу.
|
||
|
||
### 2026-06-07 п.3 (factory) — фундамент
|
||
`tests/regression/factories/OrgFactory.ts` + `api-client.ts` + `types.ts`.
|
||
Builder-pattern: `OrgFactory.for(slug).withProducts(N)…build()`.
|
||
Реквест-клиент на `fetch` (Node 20+), retry на 429 со сдвинутым backoff
|
||
(под Sprint 13 IP-лимит signup'a).
|
||
|
||
### 2026-06-07 п.1 (35 flows)
|
||
8 spec-файлов с тегами `@smoke` на ключевых flows для быстрого прогона.
|
||
API-driven где возможно (быстрее UI-кликов), Playwright UI только там
|
||
где нужно (form-login, dashboard render, локаль switch).
|
||
|
||
Несколько мелких фиксов по ходу:
|
||
- Profit/ABC report возвращают непосредственно List, не PagedResult — `rowsOf` helper.
|
||
- Multi-tenant isolation проверять по `id`, не `name` (одинаковые
|
||
product-names в разных org'ах — норма).
|
||
- Login редиректит на «/» (OnboardingPage) на свежей org, не /dashboard.
|
||
- Loss-endpoint enum'у reason требует int, не строку.
|
||
|
||
### 2026-06-07 п.2 (visual 60)
|
||
2 spec'a (auth-pages + authenticated-pages). Baseline на свежий
|
||
deploy. Маски на динамический контент:
|
||
- `table td:nth-child(2)` (артикул с Date.now()),
|
||
- `[data-kpi]` (зависит от текущей даты),
|
||
- `[data-delta]` (стрелка от prev period).
|
||
|
||
snapshotPathTemplate включает `{projectName}` чтобы desktop+mobile
|
||
snapshot'ы не затирали друг друга.
|
||
|
||
### 2026-06-07 п.4 (forgejo workflow)
|
||
`.forgejo/workflows/regression.yml` — `on workflow_run` после
|
||
Docker API/Web. Cache на pnpm-store + Playwright-browsers. Артефакты
|
||
upload при failure, Telegram-уведомление в обоих случаях.
|
||
|
||
### 2026-06-07 п.5 (nightly cron)
|
||
`~/nightly-verify.sh` + crontab entry. Health → redeploy → smoke →
|
||
weekly full + Telegram. Логи с ротацией.
|
||
|
||
### 2026-06-07 п.6 (badges)
|
||
`scripts/generate-badges.sh` — coverage из cobertura.xml → SVG через
|
||
shields.io с offline-fallback. 4 CI-status badge + coverage badge
|
||
добавлены в README. CI-step авто-обновляет coverage badge на push в main.
|
||
|
||
## Итог
|
||
|
||
Все 6 пунктов ✓. Локальные числа:
|
||
- **35 flow-тестов**: 35/35 ✓ при workers=2 (~30 сек).
|
||
- **60 visual snapshot'ов**: 60/60 ✓ при CI=1 (retries=1) (~4 мин).
|
||
- **Полный прогон**: ~5 минут — **3× ниже** 15-минутного целевого порога.
|
||
|
||
Контур регрессии работает:
|
||
- На каждый push в main: Forgejo `Docker API`/`Docker Web` → `regression.yml`
|
||
→ 35 flows + 60 visual + Telegram.
|
||
- Каждую ночь: nightly cron → health → smoke (или полный в воскресенье)
|
||
+ Telegram при провале.
|
||
- Coverage и CI-status badges в README обновляются автоматически.
|
||
|
||
Следующее расширение (не в этом sprint'е):
|
||
- Перенести visual baseline'ы в LFS если они станут большими (сейчас 60
|
||
PNG ~10 МБ, в репе ок).
|
||
- Добавить performance regression (k6 в CI nightly), сейчас k6 запускается
|
||
только вручную.
|
||
- Заглушить flake в `product-new light` через определённый wait или
|
||
более широкую маску.
|