Files
cc-1c-skills/docs/web-test-guide.md
T
Nick Shirokov 506f0b84df feat(web-test): clear fields via empty value — Shift+F4 in fillFields, selectValue, fillTableRow
Pass '' or null as value to clear any field (except checkbox/radio) via native 1C Shift+F4.
Returns method: 'clear'. Handles tree grids (close selection form first) and flat grids (dblclick to enter edit mode).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:15:52 +03:00

20 KiB
Raw Blame History

Тестирование через веб-клиент 1С

Навык /web-test автоматизирует действия в веб-клиенте 1С через Playwright — навигация по разделам, заполнение форм, чтение таблиц и отчётов, фильтрация списков. Замыкает цикл: правка исходников → загрузка → обновление → публикация → автоматическое тестирование.

Навык

Навык Скрипт Описание
/web-test .mjs (Node.js) Автоматизация 1С через браузер — навигация, формы, таблицы, отчёты

Предусловия

  • База опубликована через Apache (/web-publish)
  • Node.js 18+ установлен
  • Зависимости установлены: cd .claude/skills/web-test/scripts && npm install

Рабочий цикл

/web-publish → /web-test → результат
     ↑                          |
     └── правки → /db-load-xml → /db-update ──┘

Сценарии использования

Навигация и чтение данных

> Открой базу erp в браузере, перейди в раздел Склад и покажи какие команды там есть

Claude откроет браузер, перейдёт в раздел и покажет список команд.

> Открой список поступлений товаров и покажи первые 10 строк

Claude откроет список и прочитает таблицу.

Поиск и открытие элементов

> Найди в списке номенклатуры товар "Вентилятор" и открой его карточку

Claude отфильтрует список, откроет найденный элемент двойным кликом и прочитает реквизиты формы.

> Открой справочник Контрагенты и найди "Торговый дом"

Claude может работать с иерархическими справочниками — поиск автоматически сглаживает иерархию.

Создание документа

> Создай заказ клиента: организация "Андромеда Плюс", контрагент "Торговый дом Комплексный",
> добавь строку: номенклатура "Вентилятор", количество 5

Claude откроет форму создания, заполнит шапку и добавит строку в табличную часть.

Работа с отчётами

> Открой отчёт "Остатки и доступность товаров",
> установи отбор Склад = "Склад бытовой техники", сформируй и прочитай результат

Claude заполнит фильтры отчёта по человекочитаемым именам (не надо знать технические имена DCS), нажмёт "Сформировать" и прочитает структурированные данные: заголовки, строки, итоги.

Сравнение данных

> Сформируй отчёт по остаткам для "Склад бытовой техники" и "Западный склад",
> сравни итоги по доступным товарам

Claude напишет сценарий, который сформирует отчёт дважды с разными фильтрами и сравнит результаты.

Проверка после загрузки расширения

> Загрузи расширение ТестОшибки и проверь через браузер, что при создании заказа клиента
> появляется ошибка "Тестовая ошибка из расширения"

Claude загрузит расширение через /db-load-xml, затем через /web-test откроет форму и проверит ожидаемое поведение.

Открытие внешней обработки

> Открой обработку build/РедакторДвижений.epf в веб-клиенте и покажи что на форме

Claude откроет EPF через Ctrl+O, автоматически обработает диалог безопасности (если есть) и прочитает форму.

Пошаговая отладка

> Запусти браузер на базе erp
> Перейди в раздел Продажи
> Посмотри что на форме
> Открой первый заказ
> Какие реквизиты заполнены?

Claude будет выполнять команды по одной, показывая состояние формы между шагами.

Режимы работы

Автономный режим (run)

Одна команда: открывает браузер → логинится → выполняет сценарий → закрывает браузер → завершает процесс. Не оставляет висящих процессов.

RUN=".claude/skills/web-test/scripts/run.mjs"
node $RUN run http://localhost:8081/erp scenario.js

Claude пишет .js файл со сценарием и запускает его. Ответ — JSON:

{ "ok": true, "output": "...console.log output...", "elapsed": 12.3 }

При ошибке — автоматический скриншот (пока модалка ещё видна) и стек вызова:

{ "ok": false, "error": "Тестовая проверка: запись запрещена", "screenshot": "error-shot.png",
  "stack": { "raw": "...", "entries": [{"location": "Модуль(4)", "code": "ВызватьИсключение..."}] } }

Стек извлекается автоматически — через OpenReport (платформенные исключения) или "О программе" → "Информация для техподдержки" (ВызватьИсключение).

Интерактивный режим (start/exec/stop)

Браузер остаётся открытым между командами. Состояние (открытые вкладки, формы) сохраняется.

node $RUN start http://localhost:8081/erp    # запустить сессию (фоновый процесс)
cat <<'SCRIPT' | node $RUN exec -            # выполнить скрипт
await navigateSection('Продажи');
SCRIPT
node $RUN shot current-state.png             # скриншот
node $RUN stop                               # завершить сессию

Когда какой

Режим Когда использовать
Автономный (run) Готовый сценарий целиком, субагенты, CI
Интерактивный (start/exec) Пошаговое исследование, отладка, разговор с пользователем

Пример: автономный сценарий

Сравнение остатков по двум складам — один файл, один запуск:

// scenario-compare-stocks.js

// 1. Открыть отчёт
await navigateSection('Склад и доставка');
await openCommand('Отчеты по складу');
await clickElement('Остатки и доступность товаров', { dblclick: true });

// 2. Первый склад
await fillFields({ 'Склад': 'Склад бытовой техники' });
await clickElement('Сформировать');
await wait(5);
const report1 = await readSpreadsheet();
console.log('=== Склад бытовой техники ===');
console.log('Строк:', report1.data?.length, '| Доступно:', report1.totals?.['Доступно']);

// 3. Второй склад
await fillFields({ 'Склад': 'Западный склад' });
await clickElement('Сформировать');
await wait(5);
const report2 = await readSpreadsheet();
console.log('=== Западный склад ===');
console.log('Строк:', report2.data?.length, '| Доступно:', report2.totals?.['Доступно']);

// 4. Сравнение
const parse = s => parseFloat((s || '0').replace(/\s/g, '').replace(',', '.'));
const diff = parse(report1.totals?.['Доступно']) - parse(report2.totals?.['Доступно']);
console.log('Разница:', diff.toFixed(0));

await closeForm({ save: false });

Запуск: node $RUN run http://localhost:8081/erp scenario-compare-stocks.js

API

Все функции доступны как глобальные переменные в скриптах. console.log() выводит данные в ответ.

Навигация

Функция Описание Возвращает
navigateSection(name) Перейти в раздел (fuzzy match) { sections, commands }
openCommand(name) Открыть команду из панели функций form state
navigateLink(path) Открыть по пути метаданных (Документ.ЗаказКлиента) form state
openFile(path) Открыть внешнюю обработку/отчёт (EPF/ERF) через «Файл → Открыть» form state
switchTab(name) Переключить открытую вкладку form state

Чтение

Функция Описание Возвращает
getFormState() Структура формы: поля, кнопки, таблица, фильтры, состояние окон { form, formCount, openForms, fields, buttons, tabs, table, filters, reportSettings? }
readTable({ maxRows?, offset? }) Данные таблицы с пагинацией { columns, rows: [{col: val}], total }
readSpreadsheet() Результат отчёта { title?, headers?, data?, totals?, total }
getSections() Разделы и команды { activeSection, sections, commands }
getPageState() Разделы + открытые вкладки { activeSection, activeTab, sections, tabs }

getFormState — подробнее

Основной способ «увидеть» что на экране:

  • form — номер активной формы, null когда ничего не открыто (десктоп)
  • formCount — количество открытых форм. 0 = десктоп. Работает даже если панель открытых окон скрыта
  • openForms[0, 1, 2] — номера всех открытых форм в DOM
  • modaltrue когда активная форма — модальный диалог, блокирующий интерфейс
  • openTabs[{ name, active? }] из панели открытых окон (только когда панель включена в настройках 1С)
  • fields[{ name, value, label?, actions?, required? }]. actions = select/clear/open. required: true = незаполненное обязательное поле
  • table{ name, columns, rowCount } (метаданные; для данных — readTable())
  • reportSettings — DCS-фильтры в читаемом виде: [{ name: "Склад", enabled: true, value: "..." }]
  • errorModal — 1С показала ошибку
  • confirmation — диалог Да/Нет, вызовите clickElement('Да') или clickElement('Нет')
  • platformDialogs[{ type, title }] — платформенные диалоги (О программе, Информация для техподдержки). Невидимы для обычного определения форм, но блокируют интерфейс. closeForm() закрывает их. Автоочистка через dismissPendingErrors перед каждым action

readTable — подробнее

Каждая строка — объект { columnName: value }. Специальные поля для иерархии и дерева:

  • _kind: 'group' — группа в иерархическом списке
  • _tree: 'expanded'|'collapsed' — состояние узла дерева
  • _level: N — уровень вложенности
  • На объекте результата: hierarchical: true, viewMode: 'tree'

Действия

Функция Описание Возвращает
clickElement(text, {dblclick?}) Клик по кнопке/ссылке/строке. {dblclick: true} для открытия из списка form state или { submenu }
fillFields({name: value}) Заполнить поля (текст, чекбокс, радио, ссылки, DCS-фильтры). Пустое значение (''/null) = очистка { filled: [{field, ok, method}], form }
selectValue(field, search, opts?) Выбрать из справочника. search: текст, {поле: значение} или ''/null для очистки. { type } для составного типа form state с selected
fillTableRow(fields, {tab?, add?, row?}) Заполнить строку. Значение: строка, { value, type } для составного типа, ''/null для очистки form state
deleteTableRow(row, {tab?}) Удалить строку по индексу form state
closeForm({save?}) Закрыть форму. save: false = "Нет", save: true = "Да". Возвращает closed: true/false form state с closed
filterList(text, {field?, exact?}) Фильтр списка. Без field = все колонки, с field = расширенный поиск form state
unfilterList({field?}) Снять фильтры (все или конкретный) form state

fillFields — типы полей

Значение Тип поля Метод
'Андромеда Плюс' Ссылочное clipboard paste + typeahead
'5000' Текст clipboard paste
'true' / 'да' Чекбокс toggle
'Оплата поставщику' Радио fuzzy match по меткам
'Склад бытовой техники' (DCS) Фильтр отчёта авто-включение чекбокса + заполнение
'' / null Любое (кроме чекбокс/радио) очистка через Shift+F4

Утилиты

Функция Описание
screenshot() Скриншот (PNG Buffer)
wait(seconds) Пауза, возвращает form state
getPage() Сырой Playwright Page для горячих клавиш и нестандартных операций
startRecording(path, opts?) Начать запись видео (CDP screencast → ffmpeg → MP4)
stopRecording() Остановить запись, вернуть { file, duration, size }
showCaption(text, opts?) Текстовая подпись поверх страницы (speech — текст озвучки)
hideCaption() Убрать подпись
showTitleSlide(text, opts?) Полноэкранный титульный слайд (subtitle, background, speech)
hideTitleSlide() Убрать титульный слайд
showImage(path, opts?) Полноэкранное изображение (style: blur/dark/light/full, speech)
hideImage() Убрать изображение
addNarration(videoPath, opts?) Озвучка видео по субтитрам (Edge TTS / ElevenLabs / OpenAI)
getCaptions() Субтитры из текущей/последней записи
isRecording() Идёт ли запись (boolean)
setHighlight(on) Включить/выключить авто-выделение элементов при действиях
isHighlightMode() Активен ли режим авто-выделения (boolean)
highlight(text) Ручное выделение элемента (по имени, fuzzy match)
unhighlight() Снять выделение

Клавиатурные сочетания

Через getPage().keyboard.press():

Клавиша Контекст Действие
F8 Ссылочное поле Создать новый элемент
Shift+F4 Любое поле Очистить значение (автоматизировано: fillFields({ поле: '' }))
F4 Ссылочное поле Форма выбора
Alt+F Список/таблица Расширенный поиск

Типичные ошибки

Все функции бросают исключение при ошибке (не возвращают { error }). Сценарий прерывается на проблемном шаге с информативным сообщением. В интерактиве — try/catch для обработки.

Проблема Решение
no form found — форма не открыта Добавьте await wait(2) после навигации
not found. Available: ... — элемент не найден Проверьте имя через getFormState(), используйте вариант из Available
fillFields: N of M field(s) failed Текст ошибки содержит список проблемных полей и доступные варианты
Пустой readSpreadsheet() Увеличьте await wait(N) перед чтением

Особенности

  • Headed mode — 1С требует видимый браузер, headless не поддерживается
  • Время запуска — первое подключение к 1С занимает 30-60 секунд (ожидание встроено)
  • Fuzzy matching — все поиски: точное совпадение → начало строки → вхождение. Буквы ё и е считаются эквивалентными
  • Clipboard paste — поля заполняются через Ctrl+V (корректно триггерит события 1С)
  • Неразрывные пробелы — 1С использует \u00a0, внутри API нормализация автоматическая
  • Ошибки — все функции бросают исключение при ошибке (сценарий прерывается), try/catch для обработки
  • Панель разделовnavigateSection() работает при любом расположении панели (сбоку, сверху), но требует режим «Картинка и текст» или «Текст». Режим «Только картинки» не поддерживается — API не может прочитать имена разделов из иконок

Связанные навыки