mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-13 22:35:16 +03:00
docs(web-test): пользовательский гайд регресса + skill-инструкция regress.md
- docs/web-test-regression-guide.md — пользовательские сценарии работы с моделью для покрытия прикладного решения регрессом (русский, по аналогии с web-test-recording-guide.md): структура tests/<app-name>/, диалоги с моделью, пример организации покрытия, отчёты Allure + categories.json. - .claude/skills/web-test/regress.md — инструкция модели по написанию регрессионного набора: разведка (метаданные + живой проход через exec), layout по фичам, готовые шаблоны (CRUD/document/DCS/multi-user/repro), severity, anti-patterns, failure triage, _allure/ конвенция. - SKILL.md — указатель на regress.md в конце файла (рядом с recording). - docs/web-test-runner-spec.md → upload/ (был внутренним планом разработки, не пользовательской документацией). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,391 @@
|
||||
# Регрессионное тестирование прикладного решения
|
||||
|
||||
Навык `/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)
|
||||
Не найден столбец "Цена" в табличной части "Товары"
|
||||
скриншот: tests/учёт-поступлений/error-shot.png
|
||||
|
||||
23 пройдено, 1 упал, 0 пропущено (3 мин 42 с)
|
||||
```
|
||||
|
||||
### Подробный отчёт
|
||||
|
||||
```
|
||||
> Прогони регресс и сохрани подробный отчёт
|
||||
```
|
||||
|
||||
Модель добавит флаг записи отчёта (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).
|
||||
Reference in New Issue
Block a user