fix(web-test): resetState не закрывал form 0 + error screenshot снимался после reset

run.mjs:

1. resetState проверял `if (!state.form) break`. form === 0 (фоновая
   форма 1С, которую detectForm может вернуть) рассматривался как
   "форм нет" → cleanup прерывался, форма оставалась → следующий тест
   получал грязное состояние. Замена на `state.form == null` корректно
   различает null (desktop) и 0 (реальная фоновая форма).

2. Error screenshot в catch-блоке cmdTest снимался ПОСЛЕ resetState,
   который уже закрывал все формы → скрин показывал пустой рабочий
   стол вместо места падения. Перенёс снимок в начало catch (до
   teardown/afterEach/resetState).

Эффекты:
- 15-multi-context-handover теперь стабильно проходит в полном прогоне
  (раньше падал когда предыдущий тест оставлял form=0).
- 04-selectvalue/direct-form остался pre-existing flake (история
  выбора 1С после 03 — отдельная задача в синтетике).
- Скриншоты падения теперь показывают реальный UI на момент исключения.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-05-11 11:52:22 +03:00
parent a650325baf
commit c541d51f33
+14 -11
View File
@@ -638,6 +638,17 @@ async function cmdTest(rawArgs) {
break;
} catch (e) {
// Screenshot on failure FIRST — before teardown/afterEach/resetState reset the UI.
// Otherwise the shot captures an empty desktop instead of the failure context.
let shotFile = e.onecError?.screenshot;
if (!shotFile && opts.screenshot !== 'off') {
try {
const png = await browser.screenshot();
shotFile = resolve(reportDir, `error-${testIdx}-${slugify(t.file.replace(/\.test\.mjs$/, ''))}.png`);
writeFileSync(shotFile, png);
} catch {}
}
// per-test teardown (always)
if (t.teardown) try { await t.teardown(ctx); } catch {}
// afterEach (always)
@@ -648,16 +659,6 @@ async function cmdTest(rawArgs) {
}
for (const k of scopedKeys) delete ctx[k];
// Screenshot on failure (skip if strategy is 'off')
let shotFile = e.onecError?.screenshot;
if (!shotFile && opts.screenshot !== 'off') {
try {
const png = await browser.screenshot();
shotFile = resolve(reportDir, `error-${testIdx}-${slugify(t.file.replace(/\.test\.mjs$/, ''))}.png`);
writeFileSync(shotFile, png);
} catch {}
}
if (videoFile) {
try { await browser.stopRecording(); } catch {}
}
@@ -817,7 +818,9 @@ async function resetState(ctx) {
for (let i = 0; i < 10; i++) {
try {
const state = await ctx.getFormState();
if (!state.form) break;
// form === null means no form open (desktop). form === 0 is a real background form
// 1C exposes in some states — must still close it to fully reset.
if (state.form == null) break;
await ctx.closeForm({ save: false });
} catch { break; }
}