diff --git a/tests/web-test/README.md b/tests/web-test/README.md index 1d88b9ea..ad682022 100644 --- a/tests/web-test/README.md +++ b/tests/web-test/README.md @@ -5,7 +5,7 @@ E2E-тесты движка `web-test` (Playwright + изолированная ## Запуск ```bash -# Полный регресс (все 21 тестов) +# Полный регресс (все 25 тестов; фикстура _hang/ в него не входит — см. ниже) node .claude/skills/web-test/scripts/run.mjs test tests/web-test/ # Один файл @@ -23,7 +23,7 @@ node .claude/skills/web-test/scripts/run.mjs test tests/web-test/ --grep=multi URL не передаём позиционно — берётся из `webtest.config.mjs` (`contexts.a.url` = `http://localhost:9191/webtest-runner/ru_RU`). Переопределить можно флагом `--url=`. -Exit code: 0 = все прошли, 1 = есть падения. +Exit code: 0 = все прошли, 1 = есть падения, 2 = сработал `--global-timeout` (отчёт записан, сеансы освобождены), 3 = зависло само сворачивание. ## CLI флаги runner'а @@ -95,6 +95,49 @@ node .claude/skills/web-test/scripts/run.mjs test tests/web-test/ -- --rebuild-e | `WEB_TEST_PRESERVE_CLIPBOARD=0` | Отключить save/restore буфера обмена вокруг `pasteText` | | `WEBTEST_HOOKS_RUNTIME=python` | Использовать py-версии скиллов вместо ps1 (для не-Windows) | +## Фикстура зависания — `_hang/` + +Не часть регресса, а **ручная проверка прерывания зависшего теста**. `_`-префикс держит её вне обычного +прогона: `walk()` в `discover.mjs` пропускает `_`-записи внутри каталога, но явно переданный корневой путь +не фильтрует — поэтому `test tests/web-test/` её не видит, а `test tests/web-test/_hang` находит. + +```bash +# стенд должен быть уже опубликован — своих _hooks.mjs у фикстуры нет +node .claude/skills/web-test/scripts/run.mjs test tests/web-test/_hang +``` + +Состав: `01-renderer-block` вешает JS-поток рендерера (`page.evaluate(() => { while(true) {} })`) — +до появления abort-механики такой тест вешал прогон навсегда; `02-survivor` идёт следом и доказывает, +что прогон поехал дальше, а контекст пересоздан со свежим сеансом (то есть лицензия вернулась). + +**Ожидаемый результат — `1 passed, 1 failed` и exit code 1. Красный `01` здесь означает успех.** +Читать глазами два условия: + +1. `01` упал со строкой `verdict: hang (renderer unresponsive, browser alive)` и + `recovery: context aborted (logout: node, closed: page)` — а не завис; +2. `02` зелёный — прогон продолжился и лицензия освободилась. + +Автоматической обвязки (spawn раннера + проверка этих условий → 0/1) пока нет, поэтому в CI фикстуру +как обычный набор класть нельзя — код выхода 1 тут штатный. + +**Когда гонять:** +- правки пути очистки в `cli/commands/test.mjs` (дедлайны, ветка таймаута) и жизненного цикла в + `engine/core/session.mjs` (`abortContext`, `probeContext`, `disconnect`, `createContext`); +- **обновление Playwright** — механика стоит на замеренном поведении библиотеки: `page.close({runBeforeUnload:false})` + возвращается поверх зависшего `evaluate`, тот отваливается «Target closed», а `cookies()` отвечает при + мёртвом рендерере (пробник). Новая версия может это поменять; +- перед вливанием движковых правок web-test в `dev`. + +**Цена:** ~18 с чистого таймаута, намеренное уничтожение контекста, а в `tab`-режиме — ещё и перезапуск +браузера (закрывается последняя вкладка persistent-контекста). + +**Слепая зона:** каскад logout считает успехом ответ 2xx. Если 1С однажды начнёт держать сеанс на куке, +а не на `seanceId` в URL, Node-logout продолжит получать 200, фикстура покажет `logout: node` — и поломка +всплывёт только когда утёкшие сеансы упрутся в лимит лицензий. Фикстура этого не ловит. + +**Воспроизводит один режим** — заблокированный рендерер. Полумёртвый CDP (сокет жив, браузер не отвечает) +ловится дедлайнами, а не `abortContext`, и здесь не покрыт. + ## Артефакты - `tests/web-test/error-*.png` — скриншоты упавших шагов (auto на `--screenshot=on-failure`)