From c541d51f33a636ef42156eb13fa7d8ab7e36adb8 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 11 May 2026 11:52:22 +0300 Subject: [PATCH] =?UTF-8?q?fix(web-test):=20resetState=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D0=B2=D0=B0=D0=BB=20form=200=20+?= =?UTF-8?q?=20error=20screenshot=20=D1=81=D0=BD=D0=B8=D0=BC=D0=B0=D0=BB?= =?UTF-8?q?=D1=81=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20reset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .claude/skills/web-test/scripts/run.mjs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.claude/skills/web-test/scripts/run.mjs b/.claude/skills/web-test/scripts/run.mjs index ee360d5d..a76aeaed 100644 --- a/.claude/skills/web-test/scripts/run.mjs +++ b/.claude/skills/web-test/scripts/run.mjs @@ -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; } }