Прогон вставал намертво на зависшем Playwright-действии (~29 мин, без движения),
при --format=allure отчёт терялся целиком — результаты писались только в конце.
Promise.race с таймером был и раньше, но не лечил: race не отменяет t.fn (промис
отменить нельзя), а весь путь после него шёл без единого таймаута. Паттерн
`try { await x } catch {}` ловит reject, но не «никогда не завершится» — а
page.evaluate не имеет таймаута в принципе, поэтому заблокированный JS-поток
рендерера вешал раннер навсегда.
Что сделано:
1. Прерывание. При таймауте движок опрашивает контекст (probeContext) и при
вердикте hang/browser-dead уничтожает зависшее: abortContext закрывает
страницу с runBeforeUnload:false, повисший await отваливается «Target closed»,
следующий тест поднимает контекст лениво. Зависший тест не ретраится.
2. Диагноз в отчёте. hang (браузер жив, рендерер не отвечает) против slow
(просто не уложился → поднять timeout). Разделитель — асимметрия пробников:
вызов в browser-процесс отвечает за 1 мс при мёртвом рендерере, evaluate — нет.
3. Лицензии. Штатный logout идёт fetch-ем изнутри страницы и на зависшей
странице невозможен. abortContext шлёт POST /e1cib/logout из Node: замерено —
сеанс опознаётся seanceId в URL, кук у клиента нет вообще, после запроса
клиент пишет «сеанс был завершен». Каскад: node → page → соседняя страница.
4. Дедлайны на весь путь очистки (deadline.mjs) — пробой печатается строкой,
молча зависнуть больше нельзя. Попутно: disconnect слал logout по зависшей
странице дважды (модульный `page` после multi-context ветки).
5. Инкрементальный Allure: результат теста пишется сразу по его завершении —
зависание больше не уничтожает уже собранное.
6. --global-timeout: потолок на прогон, работает и внутри зависшего теста
(неразрешённый промис не блокирует event loop). Коды выхода: 2 — потолок
сработал, 3 — зависло само сворачивание. Внешний watchdog больше не нужен.
Проверено вживую на стенде: фикстура tests/web-test/_hang (заблокированный
JS-поток) — падение за 17.6с с verdict: hang вместо вечного зависания,
следующий тест зелёный, оба результата в allure-results; --global-timeout
срабатывает посреди зависания и сохраняет отчёт. Полный регресс 25 тестов:
пробоев дедлайнов ноль.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Вложение «Screenshot on failure» весило 0 B у всех красных тестов —
две независимых причины, маскирующие друг друга.
1. slugify сохранял кириллицу в именах артефактов. Allure CLI молча не
находит вложение с не-ASCII именем: пишет "size": 0 без ссылки на файл
(JAVA_OPTS с file.encoding/sun.jnu.encoding не помогает). Теперь slugify
транслитерирует кириллицу и схлопывает остальное не-ASCII в дефис.
Чинит и видео — оно использовало то же имя.
2. Скриншот 1С-ошибки писался в фиксированный <навык>/error-shot.png:
вне reportDir (репортер аттачит по basename → мёртвая ссылка) и одним
именем на весь прогон (каждый следующий тест перетирал предыдущий).
exec-context получил setErrorShotDir + уникальные имена; раннер
направляет их в reportDir. Дефолт для интерактивных exec/run не менялся.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Привод контракта `run.mjs test` к общей практике (pytest/jest/playwright):
- позиционные аргументы = пути к тестам, можно несколько
(`test a.test.mjs b.test.mjs dir/`); discoverTests объединяет, дедуплицирует,
сортирует (порядок по числовым префиксам сохраняется);
- URL переехал из первого позиционного во флаг --url= (по умолчанию из
webtest.config.mjs) — раньше url-первым путал: второй путь уходил в page.goto()
и падал с криптовым 'Cannot navigate to invalid URL';
- fail-fast: несуществующий путь → понятная ошибка вместо падения goto;
аргумент, похожий на URL, подсказывает про --url=.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Команда `test` приведена к поведению тест-раннеров (jest/pytest/playwright):
человеческий отчёт со сводкой в последней строке идёт в stdout, а машинный
JSON/JUnit — опционально через `--report=-` (Unix-конвенция `-` = stdout),
при этом прогресс уезжает в stderr. Убран безусловный дамп JSON в stdout,
из-за которого `test … | tail` хоронил сводку под отчётом.
- test.mjs: writer выбирается по режиму (--report=- → stderr-прогресс);
развилка `-` в обеих ветках записи (json и junit), чтобы не плодить файл "-";
валидация: --report=- несовместимо с --format=allure (каталог, не поток).
- util.mjs: строка --report=- в справке.
- Документация (spec/guide/regress/README) приведена к фактическому
английскому выводу и описывает матрицу потоков stdout/stderr.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>