feat(web-test): auto-suite + severity-резолвер для Allure

run.mjs:
- buildSeverityIndex(config) — валидация config.severity (inverted map
  «уровень → [теги]») при загрузке: ключи только из blocker|critical|
  normal|minor|trivial, теги не дублируются между bucket'ами,
  defaultSeverity тоже валидируется. fail-fast через die.
- resolveSeverity(t, severityIndex):
  1. mod.severity если задан и валидный — выигрывает.
  2. max-rank среди тегов (стандартные имена severity или маппинг).
  3. config.defaultSeverity или 'normal'.
  Rank: blocker(5) > critical(4) > normal(3) > minor(2) > trivial(1).
  Max-wins инвариантен к порядку тегов.
- writeAllure: добавлены labels suite (= dirname(t.file) или 'root') +
  severity. Тег `tag` остался как раньше.
- testResult пробрасывает t.severity (для passed/failed веток).
- SEVERITY_RANK/LEVELS объявлены в модульной шапке (top-level await на
  cmdTest начинается до конца тела модуля, TDZ-аккуратность).

webtest.config.mjs: severity policy для нашего сьюта (smoke +
multi-context → critical, recording → minor, defaultSeverity = normal).

spec.md §7: раздел про severity-policy в конфиге с валидацией.
spec.md §9: «Авто-эмиссия label-ов» — tag/suite/severity + правила резолва.

Регресс 19/19 ✓ (9m 7.6s). Распределение по уровням после исправления
'record' → 'recording' в маппинге: 13 critical / 5 normal / 1 minor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-05-13 18:37:58 +03:00
co-authored by Claude Opus 4.7
parent a55195ab66
commit fc76407877
3 changed files with 133 additions and 5 deletions
+32
View File
@@ -484,9 +484,26 @@ export default {
retries: 0,
screenshot: 'on-failure', // 'every-step' | 'off'
record: false,
// Allure severity policy (опционально). Inverted map: уровень → [теги].
// Резолв см. §9 «Severity».
severity: {
critical: ['smoke', 'multi-context'],
minor: ['recording'],
// blocker / trivial — необязательны, можно опустить
},
defaultSeverity: 'normal', // если ничего не подошло
};
```
`severity` валидируется при загрузке конфига:
- ключи — только из `blocker|critical|normal|minor|trivial`;
- значение каждого ключа — массив тегов;
- тег не может быть в двух bucket'ах одновременно (явная ошибка с указанием конфликта);
- `defaultSeverity` — из стандартного набора.
При нарушении любого правила раннер `die`-ает с понятным сообщением до запуска тестов.
Кириллица в ID контекстов работает, но смешанный регистр затрудняет ergonomics
(`testInfo.contexts.кладовщик.displayName` vs `testInfo.contexts.clerk.displayName`).
Рекомендуем разделять технический ID и человекочитаемое имя.
@@ -720,6 +737,21 @@ await step('Кладовщик проверяет статус', async () => {
Скриншоты/видео копируются в `allure-results/` с уникальными именами.
#### Авто-эмиссия label-ов
Раннер всегда заполняет следующие labels:
- **`tag`** — по одному label-у на каждый элемент `mod.tags[]`. Бесплатная фильтрация в Allure-дашборде.
- **`suite`** — `dirname(t.file)`. Тесты в корне `testDir` идут под `'root'`, тесты в подкаталоге `sales/` — под `'sales'`. Это даёт левую группировку отчёта без ручной разметки.
- **`severity`** — резолв в порядке приоритета:
1. `export const severity = 'critical'` в самом тесте (если задано и значение валидное);
2. иначе **max-rank** среди тегов теста (стандартные имена `blocker|critical|normal|minor|trivial` напрямую, либо через `config.severity`-маппинг);
3. иначе `config.defaultSeverity` или `'normal'`.
Rank: `blocker(5) > critical(4) > normal(3) > minor(2) > trivial(1)`. Max-wins инвариантен к порядку тегов в `mod.tags`.
Пример: `tags: ['smoke', 'recording']` + `severity: { critical: ['smoke'], minor: ['recording'] }` → severity = `critical` (5 > 2).
### JUnit XML (`--format=junit`)
```xml