# Регрессионное тестирование прикладного решения

Навык `/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).
