Команда `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>
30 KiB
Регрессионное тестирование прикладного решения
Навык /web-test умеет не только разово выполнить сценарий в браузере, но и сопровождать прикладное решение полноценным набором автотестов: каждый тест — отдельный файл, с шагами, проверками, тегами, отчётом и видеозаписью падений. После каждой правки конфигурации модель прогоняет весь набор и показывает, что ожидаемо ведёт себя как раньше, а что сломалось.
правка конфигурации → загрузка → обновление → публикация → прогон тестов → отчёт
Это про прикладное решение в целом, не про разовую проверку одной формы. Для разовых сценариев («открой накладную, проверь сумму») по-прежнему удобнее интерактивный режим из web-test-guide.md.
Предусловия
- База опубликована через Apache (
/web-publish). - Установлен Node.js 18+, зависимости подняты:
cd .claude/skills/web-test/scripts && npm install. - ffmpeg — нужен только если хотите видеозапись прогона как доказательство падения. Без него падения фиксируются скриншотами. Установка описана в 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
Порядок выполнения — по алфавиту, поэтому удобно префиксовать папки и файлы номерами. Это даёт предсказуемый сценарий: сначала вход, потом справочники, потом документы, потом отчёты, в конце — процессы с несколькими пользователями.
Быстрый старт
Самый короткий путь от нуля до зелёного теста — попросить модель пройти ваш сценарий руками и зафиксировать его как тест:
> Покрой регрессом справочник Контрагенты в моей конфигурации.
> Нужны проверки: создание, правка телефона, удаление.
Что сделает модель:
- Соберёт информацию о справочнике через
/meta-infoи/form-info— посмотрит реквизиты и форму элемента, чтобы знать правильные имена полей. - Подключится к опубликованной базе в интерактивном режиме и руками пройдёт каждый сценарий — создание, правка, удаление. Это нужно, чтобы зафиксировать настоящие имена кнопок, увидеть, какие диалоги показывает 1С, понять, требуется ли подтверждение сохранения.
- Зафиксирует пройденный сценарий как файл
tests/<ваша-конфигурация>/02-контрагенты/01-создание.test.mjs. - Запустит его и покажет результат.
При следующих прогонах ничего этого делать не нужно — модель просто запустит готовый набор.
Сценарии работы с моделью
Покрытие регрессом доработанного объекта
> Я добавил в справочник Номенклатура реквизит "Цена" и "Активен".
> Покрой это регрессом — создание, редактирование, фильтрация по активности
Модель:
- посмотрит структуру справочника и формы (через
/meta-info,/form-info); - интерактивно проверит, как ведут себя новые поля в браузере;
- сгенерирует 2-3 тестовых файла под папкой
02-номенклатура/; - прогонит — покажет, что зелёное, что красное.
Тест процесса с несколькими пользователями
> Сделай тест для процесса согласования приходных накладных.
> Кладовщик создаёт накладную, менеджер утверждает,
> кладовщик видит обновлённый статус
Модель настроит в webtest.config.mjs двух пользователей (с разными URL базы — например, app-clerk и app-manager), напишет тест, который оркестрирует переключение между ними, и положит его в 05-согласование/.
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-поступление-товаров/(что делает пользователь), чемдокументы/(что лежит в дереве конфигурации). - Цифровые префиксы — на папке и на файле. Гарантируют, что сначала отработают базовые проверки (вход, справочники), потом сложные (документы, отчёты, процессы). При падении базы остальное и так не пройдёт — нет смысла занимать стенд получасом.
- Один файл — одна логически связанная история. Не «всё про контрагентов в одном файле», а «отдельно создание, отдельно правка, отдельно удаление». Когда падает — сразу видно, какой именно сценарий сломан.
- Негативные тесты тоже есть. «Документ без контрагента не проводится» — такой же важный регресс, как и позитивный сценарий, особенно после правок в обработчиках проверки заполнения.
- Процессные тесты — в конце. Они самые хрупкие (зависят от двух сессий, лицензий, синхронизации) и самые длинные. Если упадут — у вас уже есть данные от предыдущих тестов.
Анатомия одного теста
Пользователь, как правило, тест не пишет — генерирует модель. Но прочитать и поправить полезно уметь. Стандартный файл выглядит так:
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) плюс
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 — стандартный визуальный отчёт с категориями падений, графиками, таймлайном. Чтобы посмотреть отчёт после прогона:
# 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 — это список регулярных выражений, по которым ошибка теста относится к той или иной группе:
[
{ "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С, если ошибка была серверной.
- Нестабильность стенда. Apache не ответил, не освободилась лицензия, база отвалилась. Это лечится не правкой теста, а починкой подготовки стенда в
_hooks.mjsили, реже, повторным прогоном с одним повтором.
Просите модель не «исправь упавший тест», а «разберись с падением» — иначе она может молча подкрутить ожидание под текущее поведение, замаскировав настоящий баг.
Полезные подробности
Тестовые данные
В прикладном решении обычно нужны какие-то стартовые данные: пара контрагентов, номенклатура, заведённые организации. Их кладём не в каждый тест, а один раз в подготовку стенда (_hooks.mjs) — после восстановления базы загружаются эталонные данные, на которых работают все тесты.
Если конкретному тесту нужны свои данные (например, документ, который мы будем редактировать), он создаёт их сам в начале и убирает в конце.
Имена документов и уникальность
Тесты прогоняются многократно. Если тест создаёт документ «Накладная-Тест», следующий прогон может натолкнуться на старую запись. Решение — добавлять к имени метку времени:
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/regress.md.
- Интерактивный режим без тестов — web-test-guide.md.
- Запись видеоинструкций — web-test-recording-guide.md.