feat(web-test): _allure/ конвенция + categories.json для триажа падений

run.mjs:
- syncAllureExtras(testDir, reportDir) копирует все файлы из
  <testDir>/_allure/ в reportDir перед генерацией отчёта. Underscore
  в имени параллелен _hooks.mjs (инфра, не тест) — discovery его
  пропускает.
- Вызов после writeAllure при --format=allure.

tests/web-test/_allure/categories.json — 7 правил классификации падений
по нашему 1С-домену:
  1. License pool exhausted (1C) — известный multi-context flake.
  2. 1C application error (modal) — exception modal через fetchErrorStack.
  3. Section panel icon-only — деградация состояния стенда.
  4. Navigation lookup miss — navigateSection/openCommand/navigateLink/switchTab.
  5. Element not found — clickElement/fillField/selectValue/closeForm/fillTableRow/deleteTableRow.
  6. Test timeout — Timeout (Nms) от раннера.
  7. Assertion failure — наши createAssertions + 1С-specific (formHasField/tableHasRow/noErrors).

spec §9: раздел «Доп. файлы Allure через <testDir>/_allure/» с таблицей
поддерживаемых типов (categories.json / environment.properties /
executor.json) и минимальным примером.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-05-13 18:53:09 +03:00
co-authored by Claude Opus 4.7
parent fc76407877
commit b992cd11c5
3 changed files with 87 additions and 1 deletions
+24 -1
View File
@@ -18,7 +18,7 @@
*/
import http from 'http';
import * as browser from './browser.mjs';
import { readFileSync, writeFileSync, unlinkSync, existsSync, readdirSync, mkdirSync } from 'fs';
import { readFileSync, writeFileSync, unlinkSync, existsSync, readdirSync, mkdirSync, copyFileSync, statSync } from 'fs';
import { resolve, dirname, basename, relative } from 'path';
import { fileURLToPath } from 'url';
import { randomUUID } from 'crypto';
@@ -821,6 +821,7 @@ async function cmdTest(rawArgs) {
if (opts.format === 'allure') {
writeAllure(results, reportDir, severityIndex);
syncAllureExtras(testDir, reportDir);
} else if (opts.format === 'junit') {
writeFileSync(resolve(opts.report), buildJUnit(report, testDir));
} else if (opts.report) {
@@ -830,6 +831,28 @@ async function cmdTest(rawArgs) {
if (failCount > 0) process.exit(1);
}
/**
* Copy any files from `<testDir>/_allure/` into `reportDir`. Convention for
* Allure customization that doesn't fit inside per-test JSON:
* - `categories.json` — failure classification (regex → bucket)
* - `environment.properties` — values shown in the Environment widget
* - `executor.json` — CI/CD metadata
* Underscored folder mirrors `_hooks.mjs` convention (infra, not a test).
* Silent if folder absent.
*/
function syncAllureExtras(testDir, reportDir) {
const extrasDir = resolve(testDir, '_allure');
if (!existsSync(extrasDir)) return;
try {
if (!statSync(extrasDir).isDirectory()) return;
} catch { return; }
for (const entry of readdirSync(extrasDir, { withFileTypes: true })) {
if (!entry.isFile()) continue;
try { copyFileSync(resolve(extrasDir, entry.name), resolve(reportDir, entry.name)); }
catch { /* best-effort */ }
}
}
function writeAllure(results, reportDir, severityIndex) {
for (const tr of results) {
if (tr.status === 'skipped') continue; // Allure ignores skipped without start/stop