From e93185c18b4f83ea5f02aeb7bf34ea4aa4fd88f1 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 18 May 2026 23:01:25 +0300 Subject: [PATCH] =?UTF-8?q?fix(web-test):=20=D1=81=D0=B5=D1=80=D0=B8=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20onecError=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BB=D0=B0=D1=82=D1=84=D0=BE=D1=80=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B9=20=D1=81=D1=82=D0=B5=D0=BA=201=D0=A1?= =?UTF-8?q?=20=D0=B2=20JSON=20/=20Allure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В тест-обёртке ACTION_FN при 1С-исключении на throw-ed Error вешалась полная структура (step, args, errors, formState, stack, screenshot), но при сборке отчёта движок брал из неё только {message, step, screenshot} — остальные поля терялись. Платформенный стек 1С, ради которого делается fetchErrorStack, в JSON-отчёт не попадал; в Allure statusDetails.trace писался только log()-вывод теста. Что поменялось: - errInfo собирается один раз после teardown (раньше был дубликат на 732 и 745), используется и для ctx.testResult (afterEach), и для lastError, и для итоговой записи в results[]. - В errInfo добавлено поле onecError: e.onecError — структура с stack.entries[{location, code}], formState, args, errors доезжает до JSON-отчёта без обрезания. - writeAllure склеивает statusDetails.trace из tr.output + (если есть) onecError.stack.raw под разделителем "--- 1C stack ---". В Allure UI платформенный стек теперь виден прямо в карточке упавшего теста. Обратная совместимость: для падений без 1С-исключения (assertion, навигация и т.п.) e.onecError === undefined → JSON.stringify его выкидывает, форма записи { message, screenshot } сохраняется в точности. Проверено вручную на стенде tests/web-test/ — падающий тест с ВызватьИсключение, JSON и Allure оба содержат полный stack.entries и формированный trace. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/web-test/scripts/run.mjs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/.claude/skills/web-test/scripts/run.mjs b/.claude/skills/web-test/scripts/run.mjs index 90622990..d6ed3f9f 100644 --- a/.claude/skills/web-test/scripts/run.mjs +++ b/.claude/skills/web-test/scripts/run.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -// web-test run v1.12 — CLI runner for 1C web client automation +// web-test run v1.13 — CLI runner for 1C web client automation // Source: https://github.com/Nikolay-Shirokov/cc-1c-skills /** * CLI runner for 1C web client automation. @@ -728,8 +728,11 @@ async function cmdTest(rawArgs) { // per-test teardown (always) if (t.teardown) try { await t.teardown(ctx); } catch {} - // Expose preliminary testResult to afterEach (final testResult assembled below). - const errInfo = { message: e.message, step: e.onecError?.step, screenshot: shotFile }; + // Build the error record once: shared between ctx.testResult (afterEach), lastError + // (retry-loop carry-over and console output), and the final report record. + // onecError carries the structured 1C exception payload (stack, formState, args, ...) + // produced by ACTION_FN wrappers — preserve it in the report. + const errInfo = { message: e.message, step: e.onecError?.step, screenshot: shotFile, onecError: e.onecError }; ctx.testResult = { status: 'failed', duration: elapsed(t0), attempts: attempt, error: errInfo, steps }; // afterEach (always) if (hooks.afterEach) try { await hooks.afterEach(ctx); } catch {} @@ -742,9 +745,9 @@ async function cmdTest(rawArgs) { if (videoFile) { try { await browser.stopRecording(); } catch {} } - lastError = { message: e.message, step: e.onecError?.step, screenshot: shotFile }; + lastError = errInfo; const dur = elapsed(t0); - testResult = { name: t.name, file: t.file, tags: t.tags, contexts: testContextNames, severity: t.severity, status: 'failed', duration: dur, attempts: attempt, start: t0, stop: Date.now(), steps, output: output.join('\n'), error: lastError, screenshot: shotFile, video: videoFile }; + testResult = { name: t.name, file: t.file, tags: t.tags, contexts: testContextNames, severity: t.severity, status: 'failed', duration: dur, attempts: attempt, start: t0, stop: Date.now(), steps, output: output.join('\n'), error: errInfo, screenshot: shotFile, video: videoFile }; } } @@ -882,7 +885,17 @@ function writeAllure(results, reportDir, severityIndex) { ], }; if (tr.status === 'failed' && tr.error) { - out.statusDetails = { message: tr.error.message || '', trace: tr.output || '' }; + // Allure UI shows statusDetails.trace right next to the error message. We compose it + // from the test's log() output plus, when present, the platform 1C stack — so the + // raw call chain is visible without opening attachments. + const traceParts = []; + if (tr.output) traceParts.push(tr.output); + const onecStack = tr.error.onecError?.stack?.raw; + if (onecStack) { + if (traceParts.length) traceParts.push('\n--- 1C stack ---\n'); + traceParts.push(onecStack); + } + out.statusDetails = { message: tr.error.message || '', trace: traceParts.join('') }; } writeFileSync(resolve(reportDir, `${uuid}-result.json`), JSON.stringify(out, null, 2)); }