mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-18 16:49:41 +03:00
037d000f20
Прогон вставал намертво на зависшем 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>
60 lines
3.4 KiB
JavaScript
60 lines
3.4 KiB
JavaScript
// web-test browser v1.19 — engine facade: re-exports the public API from engine/*
|
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
|
/**
|
|
* Public API of the web-test engine. Pure re-export facade — no logic here.
|
|
* Implementation lives in `./engine/*`. External callers (run.mjs, exec scripts,
|
|
* tests) import from this file; engine internals import each other directly.
|
|
*/
|
|
|
|
// ── core ──────────────────────────────────────────────────────────────────
|
|
export {
|
|
isConnected, getPage, ensureConnected, setPreserveClipboard,
|
|
} from './engine/core/state.mjs';
|
|
export {
|
|
pasteText, saveClipboard, restoreClipboard,
|
|
} from './engine/core/clipboard.mjs';
|
|
export { getFormState } from './engine/forms/state.mjs';
|
|
export { fetchErrorStack } from './engine/core/errors.mjs';
|
|
export { clickElement } from './engine/core/click.mjs';
|
|
|
|
// ── session ───────────────────────────────────────────────────────────────
|
|
export {
|
|
connect, disconnect, attach, detach, getSession,
|
|
createContext, setActiveContext, listContexts, getActiveContext,
|
|
hasContext, closeContext,
|
|
// Unresponsive-context handling (test runner). abortContext is the sanctioned way to
|
|
// mutate the registry from outside — the `contexts` Map itself stays private.
|
|
abortContext, probeContext, getContextDiagnostics,
|
|
} from './engine/core/session.mjs';
|
|
|
|
// ── navigation ────────────────────────────────────────────────────────────
|
|
export {
|
|
getPageState, getSections, navigateSection, getCommands,
|
|
openCommand, switchTab, openFile, navigateLink,
|
|
} from './engine/nav/navigation.mjs';
|
|
|
|
// ── forms ─────────────────────────────────────────────────────────────────
|
|
export { selectValue } from './engine/forms/select-value.mjs';
|
|
export { fillFields, fillField } from './engine/forms/fill.mjs';
|
|
export { closeForm } from './engine/forms/close.mjs';
|
|
|
|
// ── tables ────────────────────────────────────────────────────────────────
|
|
export { readTable, deleteTableRow } from './engine/table/grid.mjs';
|
|
export { readSpreadsheet } from './engine/spreadsheet/spreadsheet.mjs';
|
|
export { fillTableRow } from './engine/table/row-fill.mjs';
|
|
export { filterList, unfilterList } from './engine/table/filter.mjs';
|
|
|
|
// ── recording / overlays ──────────────────────────────────────────────────
|
|
export {
|
|
screenshot, wait, isRecording, startRecording, stopRecording,
|
|
} from './engine/recording/capture.mjs';
|
|
export {
|
|
showCaption, hideCaption, getCaptions,
|
|
showTitleSlide, hideTitleSlide,
|
|
showImage, hideImage,
|
|
} from './engine/recording/captions.mjs';
|
|
export {
|
|
highlight, unhighlight, setHighlight, isHighlightMode,
|
|
} from './engine/recording/highlight.mjs';
|
|
export { addNarration } from './engine/recording/narration.mjs';
|