mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-22 04:31:02 +03:00
fix(web-test): резолвить корень сьюта подъёмом вверх, а не от переданного пути
Конфиг и хуки резолвились строго от каталога первого позиционного пути, поэтому запуск подкаталога сьюта был невозможен: `test tests/app/00-smoke/` падал с «No URL provided and no webtest.config.mjs found» — хотя спека прямо обещает запуск подкаталога («Фильтр по пути с CLI»). Опаснее отказа по URL были два молчаливых следствия: при `--url=` прогон подкаталога терял `_hooks.mjs` и ехал по неподготовленному стенду без единого предупреждения, а `_allure/` не находился. Плюс `file:` в отчёте считался от переданного пути, из-за чего один и тот же тест получал разный ID в зависимости от способа запуска и рвал историю Allure/JUnit. Введён корень сьюта: подъём от каталога пути до первого `webtest.config.mjs` ИЛИ `_hooks.mjs` (конфиг необязателен — сьют только с хуками иначе снова терял бы подготовку), с ограничением подъёма каталогом `.git`/`.v8-project.json`, а при их отсутствии — cwd. Граница ничего не выбирает, только останавливает, так что ложная граница даёт «корень не найден», а не чужой корень. От найденного корня берутся все пять ролей: конфиг, хуки, каталог отчёта, пути в отчёте, `_allure/`. Попутно: пути из разных сьютов в одном прогоне теперь отвергаются (раньше молча выигрывал первый путь, и сьют B ехал по подготовке сьюта A); найденный корень печатается в шапке; отсутствие корня — предупреждение в stderr; диагностика говорит про корень сьюта, а не только про URL. Проверено: 12/12 офлайн-кейсов резолвера; полный регресс 29/29 до и после — `file`/`name`/`status` идентичны; `_suite-root/nested/` (сценарий, который падал) проходит с подхваченными конфигом и хуками; `_hang/` 6/6. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9c7010a49e
commit
90d8263a05
@@ -254,6 +254,15 @@ export default async function({
|
||||
23 passed, 1 failed, 0 skipped (3m 42s)
|
||||
```
|
||||
|
||||
### Прогон части набора
|
||||
|
||||
```
|
||||
> Прогони только сценарии из папки 03-приходные-накладные
|
||||
> Прогони только тесты с тегом контрагенты
|
||||
```
|
||||
|
||||
Подмножество выбирается тремя способами: путём к подпапке или отдельному файлу, тегом (`--tags=`) или фильтром по имени теста (`--grep=`). Путь к подпапке работает наравне с остальными — конфиг и подготовка стенда всё равно берутся из корня набора (папки приложения в `tests/`), движок находит его сам, поднимаясь вверх. Отдельного URL или флагов для этого не нужно.
|
||||
|
||||
### Подробный отчёт
|
||||
|
||||
```
|
||||
|
||||
@@ -29,12 +29,22 @@ node run.mjs test <dir|file>... [флаги]
|
||||
| `--report=path` | (нет) | Записать машинный отчёт в файл (JSON или XML для `--format=junit`) |
|
||||
| `--report=-` | (нет) | Машинный отчёт в stdout (`-` = stdout); человеческий прогресс уходит в stderr |
|
||||
| `--format=fmt` | json | Формат отчёта: `json` / `allure` / `junit` |
|
||||
| `--report-dir=path` | dirname(report) / testDir | Каталог для скриншотов, видео, Allure-результатов |
|
||||
| `--report-dir=path` | dirname(report) / корень сьюта | Каталог для скриншотов, видео, Allure-результатов |
|
||||
| `--screenshot=strategy` | on-failure | `on-failure` / `every-step` / `off` |
|
||||
| `--record` | false | Записывать видео для каждого теста (mp4 в `--report-dir`) |
|
||||
| `-- <hookArgs…>` | — | Всё после `--` пробрасывается в `_hooks.mjs` как `hookArgs` (см. §6.1) |
|
||||
|
||||
URL не передаётся позиционно — он берётся из `webtest.config.mjs` в каталоге тестов, а флаг `--url=` переопределяет URL дефолтного контекста. `webtest.config.mjs` и `_hooks.mjs` резолвятся от каталога **первого** пути, поэтому перечисляемые файлы должны лежать в одной папке сьюта.
|
||||
URL не передаётся позиционно — он берётся из `webtest.config.mjs`, а флаг `--url=` переопределяет URL дефолтного контекста.
|
||||
|
||||
### Резолв корня сьюта
|
||||
|
||||
`webtest.config.mjs` и `_hooks.mjs` резолвятся не от переданного пути, а от **корня сьюта**: от каталога пути движок поднимается вверх до первого каталога, где лежит `webtest.config.mjs` или `_hooks.mjs`. Именно поэтому запуск подкаталога (`test tests/myapp/sales/`) и отдельного файла работает без `--url=`. Подъём ограничен каталогом с `.git` или `.v8-project.json` (сам каталог проверяется), а если их нет — текущим рабочим каталогом; выше поиск не идёт. Не нашли маркер — корнем считается переданный каталог (тогда хуков нет, и движок пишет об этом предупреждение в stderr).
|
||||
|
||||
Маркером служат **оба** файла, а не только конфиг: конфиг необязателен (§7), и сьют, у которого есть только `_hooks.mjs`, иначе молча остался бы без подготовки стенда.
|
||||
|
||||
Если переданные пути принадлежат разным сьютам (корни не совпали) — прогон не стартует: конфиг и хуки были бы взяты от первого пути, то есть чужие. Запускайте сьюты отдельно.
|
||||
|
||||
Найденный корень печатается в шапке прогона, рядом — переданные пути, если они от него отличаются.
|
||||
|
||||
### Валидация CLI
|
||||
|
||||
@@ -200,7 +210,7 @@ export default async function({ clerk, manager, step }) {
|
||||
ctx.testInfo = {
|
||||
name, // 'Навигация по разделам' (с подставленными params)
|
||||
file, // '01-navigation.test.mjs' (basename)
|
||||
filePath, // '01-navigation.test.mjs' (relative к testDir, разделитель '/')
|
||||
filePath, // '01-navigation.test.mjs' (relative к корню сьюта, разделитель '/')
|
||||
tags, // ['nav', 'smoke']
|
||||
timeout, // 60000 (ms)
|
||||
attempt, // 1..maxAttempts (1-based)
|
||||
@@ -320,7 +330,7 @@ assert.noErrors(state, msg?)
|
||||
|
||||
## 6. Хуки
|
||||
|
||||
Все хуки определяются в `_hooks.mjs` в корне каталога тестов.
|
||||
Все хуки определяются в `_hooks.mjs` в корне сьюта (§1 «Резолв корня сьюта»).
|
||||
|
||||
### Три уровня
|
||||
|
||||
@@ -451,7 +461,7 @@ node run.mjs test tests/myapp/ --bail -- --rebuild-stand --reload-data
|
||||
|
||||
## 7. Файл конфигурации
|
||||
|
||||
`webtest.config.mjs` в корне каталога тестов. Необязателен — если отсутствует, URL должен быть передан через CLI.
|
||||
`webtest.config.mjs` в корне сьюта (§1 «Резолв корня сьюта»). Необязателен — если отсутствует, URL должен быть передан через CLI.
|
||||
|
||||
```js
|
||||
export default {
|
||||
@@ -782,7 +792,7 @@ await step('Кладовщик проверяет статус', async () => {
|
||||
Движок всегда заполняет следующие метки (`labels`):
|
||||
|
||||
- **`tag`** — по одному на каждый элемент `mod.tags[]`. Готовая фильтрация в Allure-отчёте без дополнительной разметки.
|
||||
- **`suite`** — `dirname(t.filePath)`. Тесты в корне `testDir` идут под `'root'`, тесты в подкаталоге `sales/` — под `'sales'`. Это даёт левую группировку отчёта без ручной разметки.
|
||||
- **`suite`** — `dirname(t.filePath)`. Тесты в корне сьюта идут под `'root'`, тесты в подкаталоге `sales/` — под `'sales'`. Это даёт левую группировку отчёта без ручной разметки.
|
||||
- **`severity`** — резолв в порядке приоритета:
|
||||
1. `export const severity = 'critical'` в самом тесте, **если значение валидное** (одно из `blocker | critical | normal | minor | trivial`). Если экспорт задан, но значение невалидное — пункт пропускается и идём в (3); резолв через теги (пункт 2) при этом **не выполняется** (хотел бы автор иначе — он бы не объявлял `severity`).
|
||||
2. Иначе **максимальный ранг** среди тегов теста (стандартные имена `blocker | critical | normal | minor | trivial` напрямую, либо через `config.severity`-маппинг).
|
||||
@@ -792,9 +802,9 @@ await step('Кладовщик проверяет статус', async () => {
|
||||
|
||||
Пример: `tags: ['smoke', 'recording']` + `severity: { critical: ['smoke'], minor: ['recording'] }` → severity = `critical` (5 > 2).
|
||||
|
||||
#### Доп. файлы Allure через `<testDir>/_allure/`
|
||||
#### Доп. файлы Allure через `<корень сьюта>/_allure/`
|
||||
|
||||
Движок ищет каталог `_allure/` рядом с тестами и копирует все его файлы в `reportDir` перед генерацией отчёта. Конвенция для статичной настройки Allure, для которой нет места внутри JSON-файла теста:
|
||||
Движок ищет каталог `_allure/` в корне сьюта и копирует все его файлы в `reportDir` перед генерацией отчёта. Конвенция для статичной настройки Allure, для которой нет места внутри JSON-файла теста:
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
@@ -922,7 +932,7 @@ export default async function({ fillFields, getFormState, assert }, { type, fiel
|
||||
|
||||
## 14. Обнаружение тестов
|
||||
|
||||
`testDir` (первый позиционный аргумент после URL) — каталог, в котором живут тесты. Сборщик рекурсивно обходит дерево и собирает файлы по правилам ниже.
|
||||
Позиционные аргументы — пути к тестам; каталог, от которого считаются относительные пути в отчёте, — корень сьюта (§1 «Резолв корня сьюта»). Сборщик рекурсивно обходит дерево и собирает файлы по правилам ниже.
|
||||
|
||||
```
|
||||
tests/myapp/
|
||||
@@ -944,14 +954,13 @@ tests/myapp/
|
||||
| Шаблон имени | Только `*.test.mjs` |
|
||||
| Несколько путей | `node run.mjs test a.test.mjs b.test.mjs dir/` — наборы объединяются, дублируются и сортируются |
|
||||
| Порядок | Сортировка по полному относительному пути (`sales/01` идёт до `warehouse/01`) |
|
||||
| `file` в отчёте | `relative(testDir, file)` с разделителем `/`, например `sales/01-order-create.test.mjs` |
|
||||
| `file` в отчёте | `relative(<корень сьюта>, file)` с разделителем `/`, например `sales/01-order-create.test.mjs` |
|
||||
| Фильтр по пути с CLI | `node run.mjs test tests/myapp/sales/` запустит только подкаталог |
|
||||
| Конкретный файл | `node run.mjs test tests/myapp/sales/01-order-create.test.mjs` |
|
||||
|
||||
### Чего НЕТ (сознательное упрощение)
|
||||
|
||||
- **`_hooks.mjs` на уровне подкаталога.** Движок ищет `_hooks.mjs` только в корне `testDir`. Подкаталоги свои хуки не получают.
|
||||
- **`webtest.config.mjs` на уровне подкаталога.** Тоже только в корне.
|
||||
- **`_hooks.mjs` / `webtest.config.mjs` на уровне подкаталога.** Оба берутся только из корня сьюта — того каталога, где лежит ближайший из них (§1 «Резолв корня сьюта»). Подкаталоги своих копий не получают; вложенный каталог со своим `webtest.config.mjs` — это уже отдельный сьют.
|
||||
- **Многоуровневой Suite-разметки из дерева каталогов.** Allure-метка `suite` строится только по первому уровню (`dirname(filePath)`); более глубокую группировку делайте через `tags`.
|
||||
- **Контекста по умолчанию на уровне подкаталога.** Каждый тест объявляет `context` / `contexts` сам; от пути контексты не наследуются.
|
||||
|
||||
@@ -1117,7 +1126,8 @@ JSON-отчёт (`tests[]`, полная структура — §9) для ка
|
||||
|
||||
| Термин | Определение |
|
||||
|--------|-------------|
|
||||
| **testDir** | Каталог тестов, переданный позиционным аргументом движку. Корень для discovery, `_hooks.mjs`, `webtest.config.mjs`, `_allure/`. |
|
||||
| **Test path** | Путь к тесту или каталогу тестов, переданный позиционным аргументом. Корень только для discovery — что именно запускать. |
|
||||
| **Suite root (корень сьюта)** | Каталог, найденный подъёмом от test path до первого `webtest.config.mjs` / `_hooks.mjs` (§1). От него берутся конфиг, хуки, `_allure/`, каталог отчёта по умолчанию и относительные пути `file` в отчёте. Не зависит от того, запустили сьют целиком или один его подкаталог, — поэтому ID теста в отчёте стабилен. |
|
||||
| **Context (BrowserContext)** | Изолированная сессия Playwright. Куки/состояние/страница независимы. В рамках одного теста используется один или несколько контекстов. |
|
||||
| **Active context** | Контекст, на котором сейчас оперируют функции browser-API. Переключается `setActiveContext`. |
|
||||
| **Primary context** | Контекст, активный на входе в тест. Декларация (`mod.context` или `mod.contexts[0]`). Зафиксирован в `testInfo.primaryContext`. |
|
||||
|
||||
Reference in New Issue
Block a user