mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-11 00:14:56 +03:00
547f336cf8
Команда `test` приведена к поведению тест-раннеров (jest/pytest/playwright): человеческий отчёт со сводкой в последней строке идёт в stdout, а машинный JSON/JUnit — опционально через `--report=-` (Unix-конвенция `-` = stdout), при этом прогресс уезжает в stderr. Убран безусловный дамп JSON в stdout, из-за которого `test … | tail` хоронил сводку под отчётом. - test.mjs: writer выбирается по режиму (--report=- → stderr-прогресс); развилка `-` в обеих ветках записи (json и junit), чтобы не плодить файл "-"; валидация: --report=- несовместимо с --format=allure (каталог, не поток). - util.mjs: строка --report=- в справке. - Документация (spec/guide/regress/README) приведена к фактическому английскому выводу и описывает матрицу потоков stdout/stderr. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
392 lines
30 KiB
Markdown
392 lines
30 KiB
Markdown
# Регрессионное тестирование прикладного решения
|
||
|
||
Навык `/web-test` умеет не только разово выполнить сценарий в браузере, но и сопровождать прикладное решение полноценным набором автотестов: каждый тест — отдельный файл, с шагами, проверками, тегами, отчётом и видеозаписью падений. После каждой правки конфигурации модель прогоняет весь набор и показывает, что ожидаемо ведёт себя как раньше, а что сломалось.
|
||
|
||
```
|
||
правка конфигурации → загрузка → обновление → публикация → прогон тестов → отчёт
|
||
```
|
||
|
||
Это про прикладное решение в целом, не про разовую проверку одной формы. Для разовых сценариев («открой накладную, проверь сумму») по-прежнему удобнее интерактивный режим из [web-test-guide.md](web-test-guide.md).
|
||
|
||
## Предусловия
|
||
|
||
- База опубликована через Apache (`/web-publish`).
|
||
- Установлен Node.js 18+, зависимости подняты: `cd .claude/skills/web-test/scripts && npm install`.
|
||
- ffmpeg — нужен только если хотите видеозапись прогона как доказательство падения. Без него падения фиксируются скриншотами. Установка описана в [web-test-recording-guide.md](web-test-recording-guide.md).
|
||
|
||
## Как это устроено
|
||
|
||
Набор тестов живёт в каталоге `tests/` вашего проекта. Каждое прикладное решение — отдельная подпапка. Внутри подпапки:
|
||
|
||
- `_hooks.mjs` — подготовка стенда (восстановление базы, публикация) и общая очистка после прогона. Необязателен.
|
||
- `webtest.config.mjs` — адрес базы и набор пользователей (например, кладовщик и менеджер для процессов согласования). Необязателен — если в проекте один пользователь и один URL, можно обойтись без него.
|
||
- Сами тесты — файлы `*.test.mjs`, сгруппированные по функциональным папкам.
|
||
|
||
```
|
||
tests/
|
||
моя-конфигурация/
|
||
_hooks.mjs
|
||
webtest.config.mjs
|
||
01-вход/
|
||
01-открытие-базы.test.mjs
|
||
02-контрагенты/
|
||
01-создание.test.mjs
|
||
02-правка-телефона.test.mjs
|
||
03-поступление-товаров/
|
||
01-оформление.test.mjs
|
||
02-проведение.test.mjs
|
||
04-отчёт-остатки/
|
||
01-формирование.test.mjs
|
||
05-согласование/
|
||
01-полный-цикл.test.mjs
|
||
```
|
||
|
||
Порядок выполнения — по алфавиту, поэтому удобно префиксовать папки и файлы номерами. Это даёт предсказуемый сценарий: сначала вход, потом справочники, потом документы, потом отчёты, в конце — процессы с несколькими пользователями.
|
||
|
||
## Быстрый старт
|
||
|
||
Самый короткий путь от нуля до зелёного теста — попросить модель пройти ваш сценарий руками и зафиксировать его как тест:
|
||
|
||
```
|
||
> Покрой регрессом справочник Контрагенты в моей конфигурации.
|
||
> Нужны проверки: создание, правка телефона, удаление.
|
||
```
|
||
|
||
Что сделает модель:
|
||
|
||
1. Соберёт информацию о справочнике через `/meta-info` и `/form-info` — посмотрит реквизиты и форму элемента, чтобы знать правильные имена полей.
|
||
2. Подключится к опубликованной базе в интерактивном режиме и **руками пройдёт** каждый сценарий — создание, правка, удаление. Это нужно, чтобы зафиксировать настоящие имена кнопок, увидеть, какие диалоги показывает 1С, понять, требуется ли подтверждение сохранения.
|
||
3. Зафиксирует пройденный сценарий как файл `tests/<ваша-конфигурация>/02-контрагенты/01-создание.test.mjs`.
|
||
4. Запустит его и покажет результат.
|
||
|
||
При следующих прогонах ничего этого делать не нужно — модель просто запустит готовый набор.
|
||
|
||
## Сценарии работы с моделью
|
||
|
||
### Покрытие регрессом доработанного объекта
|
||
|
||
```
|
||
> Я добавил в справочник Номенклатура реквизит "Цена" и "Активен".
|
||
> Покрой это регрессом — создание, редактирование, фильтрация по активности
|
||
```
|
||
|
||
Модель:
|
||
- посмотрит структуру справочника и формы (через `/meta-info`, `/form-info`);
|
||
- интерактивно проверит, как ведут себя новые поля в браузере;
|
||
- сгенерирует 2-3 тестовых файла под папкой `02-номенклатура/`;
|
||
- прогонит — покажет, что зелёное, что красное.
|
||
|
||
### Тест процесса с несколькими пользователями
|
||
|
||
```
|
||
> Сделай тест для процесса согласования приходных накладных.
|
||
> Кладовщик создаёт накладную, менеджер утверждает,
|
||
> кладовщик видит обновлённый статус
|
||
```
|
||
|
||
Модель настроит в `webtest.config.mjs` двух пользователей (с разными URL базы — например, `app-clerk` и `app-manager`), напишет тест, который оркестрирует переключение между ними, и положит его в `05-согласование/`.
|
||
|
||
```js
|
||
export const contexts = ['кладовщик', 'менеджер'];
|
||
|
||
export default async function({ кладовщик, менеджер, step, assert }) {
|
||
await step('Кладовщик создаёт накладную', async () => {
|
||
await кладовщик.navigateSection('Склад');
|
||
await кладовщик.openCommand('Приходные накладные');
|
||
await кладовщик.clickElement('Создать');
|
||
// ...
|
||
});
|
||
await step('Менеджер утверждает', async () => {
|
||
await менеджер.navigateSection('Согласование');
|
||
// ...
|
||
});
|
||
// ...
|
||
}
|
||
```
|
||
|
||
Учтите ограничение по лицензиям 1С: каждый одновременно открытый пользователь — это занятая клиентская лицензия. Если в наборе много многопользовательских тестов, а на стенде лицензий впритык, прогоны начнут спотыкаться на «свободных лицензий не осталось». Модель освобождает сессии между тестами автоматически (закрывает контексты после процессного теста), но если стенд ограничен — закладывайте это в планирование набора: один-два многопользовательских сценария вместо десяти.
|
||
|
||
### Воспроизведение ошибки тестом
|
||
|
||
```
|
||
> При проведении накладной без заполненного контрагента у нас не появляется
|
||
> ошибка валидации, документ просто проводится с пустым контрагентом — это баг.
|
||
> Зафиксируй это падающим тестом
|
||
```
|
||
|
||
Модель воспроизведёт сценарий, напишет тест с проверкой «должна быть ошибка», получит красный — потом, когда вы поправите конфигурацию и попросите перепрогнать, тест станет зелёным. Это документирует ожидаемое поведение в виде кода.
|
||
|
||
### Прогон регресса после изменений
|
||
|
||
```
|
||
> Я обновил расширение, накатил в базу. Прогони регресс
|
||
```
|
||
|
||
Модель запустит весь набор, дождётся завершения и расскажет:
|
||
- сколько тестов прошло, сколько упало, сколько пропущено;
|
||
- по каждому упавшему — что именно сломалось (название шага, сообщение об ошибке, ссылка на скриншот);
|
||
- классифицирует падения: это ошибка в самом тесте (нужно поправить тест), ошибка в приложении (баг, который вы внесли изменением), или нестабильность стенда (Apache не ответил вовремя, лицензия не освободилась).
|
||
|
||
```
|
||
> Прогони только тесты по контрагентам с подробным отчётом
|
||
```
|
||
|
||
Запустит подмножество — фильтр по тегу или папке, с записью JSON-отчёта.
|
||
|
||
### Подготовка автономного стенда
|
||
|
||
Если вы хотите, чтобы регресс можно было запустить «с нуля» — даже на чистой машине без подготовленной базы, — модель настроит автоматическую подготовку стенда:
|
||
|
||
```
|
||
> Сделай, чтобы перед прогоном тестов база восстанавливалась из эталона,
|
||
> а после прогона публикация снималась
|
||
```
|
||
|
||
Это пишется один раз в файле `_hooks.mjs`: при запуске тестов запускается подготовка (через навыки `/db-create`, `/db-load-xml`, `/web-publish`), а после — очистка. Внутри предусмотрено кэширование: если ничего не менялось со прошлого прогона, повторная подготовка занимает доли секунды.
|
||
|
||
## Пример организации покрытия
|
||
|
||
Допустим, у нас условное прикладное решение «Учёт поступлений товаров» — справочники контрагентов и номенклатуры, документ приходной накладной, отчёт остатков, процесс согласования с двумя пользователями. Логично организовать набор так:
|
||
|
||
```
|
||
tests/учёт-поступлений/
|
||
_hooks.mjs # подготовка: восстановление базы + публикация
|
||
webtest.config.mjs # URL базы, контексты кладовщика и менеджера
|
||
01-вход/
|
||
01-открытие-базы.test.mjs # базовая работоспособность: вход проходит, разделы видны
|
||
02-навигация-по-разделам.test.mjs # обход всех разделов конфигурации
|
||
02-контрагенты/
|
||
01-создание.test.mjs # создание, проверка появления в списке
|
||
02-редактирование.test.mjs # правка реквизита, проверка сохранения
|
||
03-удаление.test.mjs # удаление с подтверждением
|
||
03-номенклатура/
|
||
01-создание.test.mjs
|
||
02-фильтр-по-активности.test.mjs # быстрая фильтрация списка
|
||
04-поступление-товаров/
|
||
01-оформление.test.mjs # заполнение шапки и табличной части
|
||
02-проведение.test.mjs # проведение документа, проверка движений
|
||
03-отмена-проведения.test.mjs
|
||
04-валидация-обязательных.test.mjs # негативный тест: пустой контрагент → ошибка
|
||
05-отчёт-остатки/
|
||
01-формирование.test.mjs
|
||
02-отбор-по-складу.test.mjs
|
||
03-расшифровка.test.mjs # переход из ячейки отчёта в исходный документ
|
||
06-согласование/
|
||
01-полный-цикл.test.mjs # многопользовательский тест
|
||
```
|
||
|
||
Принципы:
|
||
|
||
- **Папки — по бизнес-функции**, не по типу метаданных. Лучше `04-поступление-товаров/` (что делает пользователь), чем `документы/` (что лежит в дереве конфигурации).
|
||
- **Цифровые префиксы** — на папке и на файле. Гарантируют, что сначала отработают базовые проверки (вход, справочники), потом сложные (документы, отчёты, процессы). При падении базы остальное и так не пройдёт — нет смысла занимать стенд получасом.
|
||
- **Один файл — одна логически связанная история.** Не «всё про контрагентов в одном файле», а «отдельно создание, отдельно правка, отдельно удаление». Когда падает — сразу видно, какой именно сценарий сломан.
|
||
- **Негативные тесты тоже есть.** «Документ без контрагента не проводится» — такой же важный регресс, как и позитивный сценарий, особенно после правок в обработчиках проверки заполнения.
|
||
- **Процессные тесты — в конце.** Они самые хрупкие (зависят от двух сессий, лицензий, синхронизации) и самые длинные. Если упадут — у вас уже есть данные от предыдущих тестов.
|
||
|
||
## Анатомия одного теста
|
||
|
||
Пользователь, как правило, тест не пишет — генерирует модель. Но прочитать и поправить полезно уметь. Стандартный файл выглядит так:
|
||
|
||
```js
|
||
export const name = 'Создание контрагента';
|
||
export const tags = ['контрагенты', 'базовая-проверка'];
|
||
export const timeout = 60000;
|
||
|
||
export default async function({
|
||
navigateSection, openCommand, clickElement, fillFields,
|
||
readTable, closeForm, assert, step
|
||
}) {
|
||
await step('Открыть список контрагентов', async () => {
|
||
await navigateSection('Продажи');
|
||
await openCommand('Контрагенты');
|
||
});
|
||
|
||
await step('Создать нового контрагента', async () => {
|
||
await clickElement('Создать');
|
||
await fillFields({ 'Наименование': 'ТД Тест', 'ИНН': '7707083893' });
|
||
await clickElement('Записать и закрыть');
|
||
});
|
||
|
||
await step('Убедиться, что элемент появился в списке', async () => {
|
||
const t = await readTable();
|
||
assert.tableHasRow(t, r => r['Наименование'] === 'ТД Тест');
|
||
});
|
||
}
|
||
```
|
||
|
||
Что здесь есть:
|
||
|
||
- **`name`** — человекочитаемое имя теста. Появится в отчёте.
|
||
- **`tags`** — теги для фильтрации. Можно прогонять не весь набор, а только нужные: `--tags=контрагенты`.
|
||
- **`timeout`** — сколько максимум тест может идти. По умолчанию 30 секунд, для длинных сценариев увеличиваем.
|
||
- **Тело теста** — функция, которая получает API браузера (см. [SKILL.md](../.claude/skills/web-test/SKILL.md)) плюс `assert` и `step`.
|
||
- **`step('имя', async () => {...})`** — обёртка шага. Имена шагов попадают в отчёт, при падении видно, какой именно шаг сломался.
|
||
- **`assert.*`** — проверки. `assert.tableHasRow`, `assert.equal`, `assert.ok` и т.д. Если проверка не выполнилась — тест считается упавшим.
|
||
|
||
Имена шагов и теста — по-русски, описательные. Они показываются и в консоли, и в отчётах.
|
||
|
||
## Запуск и отчёты
|
||
|
||
### Простой прогон
|
||
|
||
```
|
||
> Прогони регресс
|
||
```
|
||
|
||
Модель запустит весь набор, дождётся, покажет сводку:
|
||
|
||
```
|
||
✓ Открытие базы (2.1s)
|
||
✓ Создание контрагента (8.4s)
|
||
✗ Проведение приходной накладной (12.7s)
|
||
└ Заполнить табличную часть (5.2s)
|
||
Не найден столбец "Цена" в табличной части "Товары"
|
||
screenshot: tests/учёт-поступлений/error-shot.png
|
||
|
||
23 passed, 1 failed, 0 skipped (3m 42s)
|
||
```
|
||
|
||
### Подробный отчёт
|
||
|
||
```
|
||
> Прогони регресс и сохрани подробный отчёт
|
||
```
|
||
|
||
Модель добавит флаг записи отчёта (JSON или Allure) — потом по нему можно листать историю прогонов, видеть длительности шагов, открывать прикреплённые скриншоты.
|
||
|
||
Allure — стандартный визуальный отчёт с категориями падений, графиками, таймлайном. Чтобы посмотреть отчёт после прогона:
|
||
|
||
```bash
|
||
# Allure CLI устанавливается отдельно (npm install -g allure-commandline)
|
||
allure serve allure-results
|
||
```
|
||
|
||
### Категории падений в Allure
|
||
|
||
Без дополнительной настройки Allure складывает все упавшие тесты в один общий список «Defects». Если в прогоне упало 15 тестов, не сразу понятно, что из этого — пятнадцать разных проблем или одна и та же ошибка (например, нехватка лицензии на стенде), которая зацепила пятнадцать тестов подряд.
|
||
|
||
Чтобы Allure группировал падения по причинам, рядом с тестами кладётся каталог `_allure/` с файлом `categories.json`. Подчёркивание в имени каталога — чтобы он не воспринимался как папка с тестами; раннер копирует его содержимое в отчёт.
|
||
|
||
```
|
||
tests/моя-конфигурация/
|
||
_allure/
|
||
categories.json # классификация падений
|
||
environment.properties # необязательно: URL, версия 1С, ветка git
|
||
executor.json # необязательно: метаданные сборки CI
|
||
_hooks.mjs
|
||
01-вход/
|
||
...
|
||
```
|
||
|
||
`categories.json` — это список регулярных выражений, по которым ошибка теста относится к той или иной группе:
|
||
|
||
```json
|
||
[
|
||
{ "name": "Нехватка лицензий 1С",
|
||
"matchedStatuses": ["failed", "broken"],
|
||
"messageRegex": ".*Не обнаружено свободной лицензии.*" },
|
||
{ "name": "Ошибка приложения 1С",
|
||
"matchedStatuses": ["failed"],
|
||
"messageRegex": ".*(ВызватьИсключение|В поле введены некорректные данные|Произошла ошибка).*" },
|
||
{ "name": "Элемент не найден",
|
||
"matchedStatuses": ["failed"],
|
||
"messageRegex": ".*(clickElement|fillFields|selectValue).*not found.*" },
|
||
{ "name": "Превышен лимит времени теста",
|
||
"matchedStatuses": ["failed", "broken"],
|
||
"messageRegex": "Timeout \\(\\d+ms\\)" },
|
||
{ "name": "Несовпадение ожидания и факта",
|
||
"matchedStatuses": ["failed"],
|
||
"messageRegex": "(Expected|AssertionError).*" }
|
||
]
|
||
```
|
||
|
||
Когда вы попросите модель в первый раз настроить регресс, она положит шаблонный `categories.json` со стандартными классами. По мере того как вы будете находить новые типичные причины падений (например, специфичные для вашего расширения тексты ошибок), категории дополняются.
|
||
|
||
В виджете «Categories» итогового отчёта вы увидите примерно так:
|
||
|
||
```
|
||
Нехватка лицензий 1С — 12 падений
|
||
Ошибка приложения 1С — 2 падения
|
||
Несовпадение ожидания и факта — 1 падение
|
||
```
|
||
|
||
— и сразу понятно, что 12 падений — это один стенд-баг, а двумя «ошибками приложения» нужно разобраться по существу.
|
||
|
||
Помимо `categories.json` в каталог `_allure/` можно положить ещё два стандартных файла:
|
||
|
||
- **`environment.properties`** — список `ключ=значение` (URL базы, версия платформы 1С, имя ветки git, номер сборки). Покажется в отчёте в виджете «Environment». Полезно, когда регресс гоняется на нескольких стендах или после каждого билда — видно, на чём именно был получен результат. Этот файл удобно генерировать прямо в подготовке стенда (`_hooks.mjs`), а не держать статичной копией.
|
||
- **`executor.json`** — метаданные системы сборки: ссылка на Jenkins-задачу, идентификатор запуска GitHub Actions и т.д. Нужен только если регресс запускается на сервере сборки. При локальном прогоне ничего класть не надо.
|
||
|
||
### Прогон части набора
|
||
|
||
```
|
||
> Прогони только тесты по поступлениям товаров
|
||
> Прогони только базовые проверки
|
||
> Прогони только упавший вчера тест с проведением накладной
|
||
```
|
||
|
||
Модель выберет нужное подмножество — по папке, по тегу или по имени теста.
|
||
|
||
### Принудительная пересборка стенда
|
||
|
||
Если хотите, чтобы перед прогоном база восстановилась с нуля:
|
||
|
||
```
|
||
> Прогони регресс с полной пересборкой стенда
|
||
```
|
||
|
||
Это передаст в подготовку флаг типа `--rebuild-stand` — `_hooks.mjs` пересоздаст базу из эталона. Полезно после крупных правок или если подозреваете, что предыдущие прогоны загрязнили данные.
|
||
|
||
## Что делать, когда тест упал
|
||
|
||
Модель проанализирует падение и отнесёт его к одной из трёх категорий:
|
||
|
||
1. **Ошибка в самом тесте.** Например, переименовали реквизит — тест ищет старое имя поля. Решение: модель обновит тест.
|
||
2. **Ошибка в приложении.** Это и есть то, ради чего регресс существует: что-то поменялось в конфигурации, и сценарий, который раньше работал, теперь не отрабатывает. Модель опишет, что именно произошло, со скриншотом и трассировкой стека 1С, если ошибка была серверной.
|
||
3. **Нестабильность стенда.** Apache не ответил, не освободилась лицензия, база отвалилась. Это лечится не правкой теста, а починкой подготовки стенда в `_hooks.mjs` или, реже, повторным прогоном с одним повтором.
|
||
|
||
Просите модель не «исправь упавший тест», а «разберись с падением» — иначе она может молча подкрутить ожидание под текущее поведение, замаскировав настоящий баг.
|
||
|
||
## Полезные подробности
|
||
|
||
### Тестовые данные
|
||
|
||
В прикладном решении обычно нужны какие-то стартовые данные: пара контрагентов, номенклатура, заведённые организации. Их кладём не в каждый тест, а один раз в подготовку стенда (`_hooks.mjs`) — после восстановления базы загружаются эталонные данные, на которых работают все тесты.
|
||
|
||
Если конкретному тесту нужны свои данные (например, документ, который мы будем редактировать), он создаёт их сам в начале и убирает в конце.
|
||
|
||
### Имена документов и уникальность
|
||
|
||
Тесты прогоняются многократно. Если тест создаёт документ «Накладная-Тест», следующий прогон может натолкнуться на старую запись. Решение — добавлять к имени метку времени:
|
||
|
||
```js
|
||
const метка = 'Тест-' + Date.now();
|
||
await fillFields({ 'Комментарий': метка });
|
||
// ...
|
||
const t = await readTable();
|
||
assert.tableHasRow(t, r => r['Комментарий'] === метка);
|
||
```
|
||
|
||
Модель это делает автоматически, но если правите тест руками — держите в голове.
|
||
|
||
### Видео при падении
|
||
|
||
Можно включить запись видео всех тестов — тогда при падении прикладывается не только скриншот, но и MP4 со всей сессией:
|
||
|
||
```
|
||
> Прогони регресс с записью видео
|
||
```
|
||
|
||
Размер прогона при этом растёт (на 2-3 минутах теста выходит 5-10 МБ), но при отладке сложного падения видео экономит кучу времени.
|
||
|
||
### Многоязычные конфигурации
|
||
|
||
Если у вас есть конфигурация с командами и реквизитами на нескольких языках, тесты пишутся под один язык (как правило, тот, в котором ведётся работа в проде). При смене языка интерфейса в браузере тесты не пройдут — модель видит другие подписи кнопок.
|
||
|
||
## Где смотреть дальше
|
||
|
||
- API браузера, которое вызывают тесты — [SKILL.md](../.claude/skills/web-test/SKILL.md).
|
||
- Подробная инструкция для модели по написанию тестов (на английском, технический документ) — [.claude/skills/web-test/regress.md](../.claude/skills/web-test/regress.md).
|
||
- Интерактивный режим без тестов — [web-test-guide.md](web-test-guide.md).
|
||
- Запись видеоинструкций — [web-test-recording-guide.md](web-test-recording-guide.md).
|