From a92bce05fbc85ea6eed642ba5323105fd229cd41 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Tue, 12 May 2026 20:25:33 +0300 Subject: [PATCH] =?UTF-8?q?feat(web-test):=20runner=20v1.11=20=E2=80=94=20?= =?UTF-8?q?`--`=20separator=20+=20spec=20=C2=A76.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В CLI раннера всё после `--` собирается в массив hookArgs и передаётся в инфра-хуки prepare/cleanup без интерпретации со стороны раннера. Сигнатура расширена до { hookArgs, log, config }: log — структурированный вывод раннера, config — разобранный webtest.config.mjs. Шаблон «всё после `--` принадлежит вложенному инструменту» — стандартная shell-конвенция (npm, cargo, pytest). Спека §6 обновлена под новую сигнатуру, §6.1 закрепляет контракт `--` ↔ hookArgs с примером. Help-строка раннера упоминает разделитель. --- .claude/skills/web-test/scripts/run.mjs | 29 ++++++++++++----- docs/web-test-runner-spec.md | 41 ++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/.claude/skills/web-test/scripts/run.mjs b/.claude/skills/web-test/scripts/run.mjs index a76aeaed..b3f74f3c 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.10 — CLI runner for 1C web client automation +// web-test run v1.11 — CLI runner for 1C web client automation // Source: https://github.com/Nikolay-Shirokov/cc-1c-skills /** * CLI runner for 1C web client automation. @@ -356,11 +356,18 @@ function cmdStatus() { // ============================================================ async function cmdTest(rawArgs) { + // Split off everything after `--` — those args belong to user-defined hooks + // (see spec §6: "all arguments after `--` are forwarded verbatim to _hooks.mjs + // via the hookArgs field; the runner does not interpret them"). + const sepIdx = rawArgs.indexOf('--'); + const ownArgs = sepIdx >= 0 ? rawArgs.slice(0, sepIdx) : rawArgs; + const hookArgs = sepIdx >= 0 ? rawArgs.slice(sepIdx + 1) : []; + // Parse flags const opts = { bail: false, retry: 0, timeout: 30000, report: null, format: 'json', screenshot: null, reportDir: null, record: false }; let tags = null, grep = null; const positional = []; - for (const a of rawArgs) { + for (const a of ownArgs) { if (a.startsWith('--tags=')) tags = a.slice(7).split(','); else if (a.startsWith('--grep=')) grep = new RegExp(a.slice(7), 'i'); else if (a === '--bail') opts.bail = true; @@ -417,8 +424,8 @@ async function cmdTest(rawArgs) { // Apply config defaults (CLI flags override) if (!tags && config.tags) tags = config.tags; - opts.timeout = rawArgs.some(a => a.startsWith('--timeout=')) ? opts.timeout : (config.timeout || opts.timeout); - opts.retry = rawArgs.some(a => a.startsWith('--retry=')) ? opts.retry : (config.retries || opts.retry); + opts.timeout = ownArgs.some(a => a.startsWith('--timeout=')) ? opts.timeout : (config.timeout || opts.timeout); + opts.retry = ownArgs.some(a => a.startsWith('--retry=')) ? opts.retry : (config.retries || opts.retry); opts.record = opts.record || !!config.record; opts.screenshot = opts.screenshot || config.screenshot || 'on-failure'; if (!['on-failure', 'every-step', 'off'].includes(opts.screenshot)) { @@ -498,7 +505,10 @@ async function cmdTest(rawArgs) { let passCount = 0, failCount = 0, skipCount = 0; // Prepare: infrastructure hooks (no browser) - if (hooks.prepare) await hooks.prepare(); + // Spec §6: prepare receives { hookArgs, log, config } — see ExternalDoc. + const hookLog = (...a) => W.write(`[hooks] ${a.map(String).join(' ')}\n`); + const hookEnv = { hookArgs, log: hookLog, config }; + if (hooks.prepare) await hooks.prepare(hookEnv); // Lazy context creation: ensures the named browser context exists, creating it on first request. async function ensureContext(name) { @@ -692,8 +702,8 @@ async function cmdTest(rawArgs) { } finally { // Disconnect try { await browser.disconnect(); } catch {} - // Cleanup: infrastructure hooks - if (hooks.cleanup) try { await hooks.cleanup(); } catch {} + // Cleanup: infrastructure hooks (same signature as prepare) + if (hooks.cleanup) try { await hooks.cleanup(hookEnv); } catch {} } const finishedAt = new Date().toISOString(); @@ -1002,5 +1012,8 @@ Options for test: --report-dir=path Directory for screenshots and other artifacts --screenshot=mode on-failure (default) | every-step | off --format=fmt json (default) | allure | junit - --record Record video for each test (mp4 in report-dir)`); + --record Record video for each test (mp4 in report-dir) + -- Everything after \`--\` is forwarded to _hooks.mjs + prepare/cleanup as hookArgs (runner does not parse it). + Example: ... tests/web-test/ -- --rebuild-stand`); } diff --git a/docs/web-test-runner-spec.md b/docs/web-test-runner-spec.md index ffa5ad4e..3210bc82 100644 --- a/docs/web-test-runner-spec.md +++ b/docs/web-test-runner-spec.md @@ -269,8 +269,15 @@ assert.noErrors(state, msg) ### Два уровня **Инфраструктурный уровень** (без браузера): -- `prepare()` -- до подключения (восстановление БД, публикация, загрузка данных) -- `cleanup()` -- после отключения (удаление публикации, очистка) +- `prepare({ hookArgs, log, config })` -- до подключения (восстановление БД, публикация, загрузка данных) +- `cleanup({ hookArgs, log, config })` -- после отключения (удаление публикации, очистка) + +Поля: +- `hookArgs: string[]` -- всё что в командной строке передано после разделителя `--`, + без интерпретации со стороны раннера. Хук парсит сам (см. §6.1 ниже). +- `log: (...args) => void` -- функция логирования раннера (структурированный вывод + с префиксом `[hooks]`). Использовать вместо `console.log` чтобы не ломать формат отчёта. +- `config: object` -- разобранный `webtest.config.mjs` (URL контекстов, isolation, etc.). **Тестовый уровень** (с контекстом браузера): - `beforeAll(ctx)` -- после подключения, перед первым тестом @@ -317,12 +324,17 @@ while (есть открытые формы) { ```js import { execSync } from 'child_process'; -export async function prepare() { +export async function prepare({ hookArgs, log, config }) { + // Простой парсер ad-hoc флагов: hookArgs приходит как есть, без интерпретации + // раннером (см. §6.1 ниже). + const force = hookArgs.includes('--rebuild-stand'); + log('preparing stand, force=', force); execSync('powershell.exe -File scripts/restore-db.ps1'); execSync('powershell.exe -File scripts/publish.ps1'); } -export async function cleanup() { +export async function cleanup({ log }) { + log('cleaning up stand'); execSync('powershell.exe -File scripts/unpublish.ps1'); } @@ -335,6 +347,27 @@ export async function afterEach({ closeForm }) { } ``` +### 6.1. Проброс пользовательских флагов через `--` + +Раннер не знает о пользовательских флагах хуков. Чтобы хуки получили ad-hoc +параметры без правки `webtest.config.mjs` или окружения, используется стандартная +shell-конвенция `--` (как у `npm`, `cargo`, `pytest`): всё что идёт после `--` +в CLI раннера передаётся в `prepare`/`cleanup` через поле `hookArgs: string[]` +без интерпретации. + +``` +node run.mjs test tests/web-test/ --bail -- --rebuild-stand --reload-data + └─ runner ─┘ └──── hookArgs ────────────┘ +``` + +В этом примере раннер получает `--bail`, а `hookArgs` хуков становится +`['--rebuild-stand', '--reload-data']`. Парсинг этого массива — ответственность +хуков. + +Если разделитель `--` не указан, `hookArgs` — пустой массив. Это позволяет +раннеру и хукам эволюционировать независимо: новый builtin-флаг раннера +никогда не пересечётся с пользовательским. + --- ## 7. Файл конфигурации