mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-16 10:43:18 +03:00
feat(web): add /web-test skill for browser automation scripting
Skill generates and runs .mjs scripts using browser.mjs API. Includes function table, script template, and usage notes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
---
|
||||
name: web-test
|
||||
description: Тестирование 1С через веб-клиент — автоматизация действий в браузере. Используй когда пользователь просит проверить, протестировать, автоматизировать действия в 1С через браузер
|
||||
argument-hint: "сценарий на естественном языке"
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /web-test — Тестирование 1С через веб-клиент
|
||||
|
||||
Пишет и запускает скрипт автоматизации для 1С веб-клиента через Playwright.
|
||||
|
||||
## Аргумент
|
||||
|
||||
Сценарий на естественном языке, например:
|
||||
```
|
||||
/web-test Открой Платежное поручение, заполни сумму 5000, сделай скриншот
|
||||
/web-test Создай документ "Поступление товаров", выбери организацию "Конфетпром"
|
||||
/web-test Проверь список контрагентов — прочитай таблицу
|
||||
```
|
||||
|
||||
## Как работать
|
||||
|
||||
1. Напиши `.mjs` скрипт в корне проекта 1c-web-client-mcp (`C:\WS\tasks\1c-web-client-mcp`)
|
||||
2. Запусти через `node <script>.mjs [url]`
|
||||
3. Прочитай stdout + посмотри скриншоты
|
||||
4. В `finally` ВСЕГДА вызывай `disconnect()` — иначе лицензия зависнет
|
||||
|
||||
## URL по умолчанию
|
||||
|
||||
Прочитай `.v8-project.json` из корня проекта (`C:\WS\tasks\skills\.v8-project.json`).
|
||||
Если задан `webUrl` — используй его. Иначе: `http://localhost:8081/bpdemo`
|
||||
|
||||
## API browser.mjs
|
||||
|
||||
Путь для импорта: `./src/browser.mjs` (из корня `C:\WS\tasks\1c-web-client-mcp`)
|
||||
|
||||
### Функции
|
||||
|
||||
| Функция | Описание | Возвращает |
|
||||
|---------|----------|------------|
|
||||
| `connect(url)` | Открыть браузер, перейти по URL, закрыть стартовые модалки | `{ activeSection, sections, tabs }` |
|
||||
| `disconnect()` | Graceful logout + закрыть браузер | `void` |
|
||||
| `isConnected()` | Проверить соединение | `boolean` |
|
||||
| `getPageState()` | Разделы + вкладки | `{ activeSection, activeTab, sections, tabs }` |
|
||||
| `getSections()` | Разделы + команды текущего раздела | `{ activeSection, sections, commands }` |
|
||||
| `navigateSection(name)` | Перейти в раздел (fuzzy) | `{ navigated, sections, commands }` |
|
||||
| `getCommands()` | Команды текущего раздела | `[[cmd1, cmd2], [cmd3]]` |
|
||||
| `openCommand(name)` | Открыть команду (fuzzy) | `{ form, fields, buttons, tabs, ... }` |
|
||||
| `getFormState()` | Прочитать текущую форму | `{ form, activeTab, fields, buttons, tabs, texts, hyperlinks, table }` |
|
||||
| `readTable({maxRows, offset})` | Прочитать таблицу | `{ name, columns, rows, total, offset, shown }` |
|
||||
| `fillFields({field: value})` | Заполнить поля (fuzzy по имени/метке). Поддерживает input, textarea, checkbox | `{ filled, form }` |
|
||||
| `clickElement(text)` | Кликнуть кнопку/ссылку/вкладку (fuzzy). Обрабатывает submenu | `{ form, clicked, submenu?, hint? }` |
|
||||
| `selectValue(field, search?)` | Выбрать из справочника (составная операция) | `{ form, selected, fields, ... }` |
|
||||
| `screenshot()` | Скриншот | `Buffer (PNG)` |
|
||||
| `wait(seconds)` | Подождать N секунд | `{ form, ... }` |
|
||||
| `getPage()` | Сырой Playwright page для продвинутых сценариев | `Page` |
|
||||
|
||||
### Поля формы (fields[])
|
||||
|
||||
```js
|
||||
{
|
||||
name: "СуммаДокумента", // Внутреннее имя (из DOM ID)
|
||||
label: "Сумма платежа", // Видимая метка (опционально)
|
||||
value: "1000", // Текущее значение
|
||||
type: "text|textarea|checkbox|date", // Тип (если не text)
|
||||
readonly: true, // Только чтение (опционально)
|
||||
disabled: true, // Заблокировано (опционально)
|
||||
actions: ["select","open","clear","pick"] // Кнопки поля (опционально)
|
||||
}
|
||||
```
|
||||
|
||||
### Таблица (table)
|
||||
|
||||
В `getFormState()` таблица — превью: `{ present, columns, rowCount, preview }`.
|
||||
Для полных данных используй `readTable({maxRows: 50, offset: 0})`.
|
||||
|
||||
### Submenu
|
||||
|
||||
Если `clickElement()` вернул `submenu[]` — вызови `clickElement()` ещё раз с именем пункта.
|
||||
|
||||
### Выбор из справочника
|
||||
|
||||
`selectValue("Организация", "Конфетпром")` — составная операция:
|
||||
1. Находит кнопку DLB у поля
|
||||
2. Открывает форму выбора
|
||||
3. Вводит текст в поиск
|
||||
4. Двойной клик по первой строке
|
||||
5. Возвращает обновлённую форму
|
||||
|
||||
Если не сработало — fallback через ручные шаги: `clickElement` → `getFormState` → `readTable`.
|
||||
|
||||
## Шаблон скрипта
|
||||
|
||||
```js
|
||||
import * as browser from './src/browser.mjs';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
const url = process.argv[2] || 'http://localhost:8081/bpdemo';
|
||||
|
||||
try {
|
||||
// 1. Подключение
|
||||
const state = await browser.connect(url);
|
||||
console.log('Sections:', state.sections?.map(s => s.name).join(', '));
|
||||
|
||||
// 2. Навигация
|
||||
await browser.navigateSection('Банк и касса');
|
||||
|
||||
// 3. Открыть команду
|
||||
const list = await browser.openCommand('Платежные поручения');
|
||||
console.log('Form:', list.form, '| Buttons:', list.buttons?.map(b => b.name));
|
||||
|
||||
// 4. Действия...
|
||||
const doc = await browser.clickElement('Создать');
|
||||
console.log('Fields:', doc.fields?.map(f => `${f.label||f.name}: "${f.value}"`));
|
||||
|
||||
// 5. Скриншот
|
||||
const png = await browser.screenshot();
|
||||
writeFileSync('result.png', png);
|
||||
console.log('Screenshot saved: result.png');
|
||||
|
||||
} catch (e) {
|
||||
console.error('ERROR:', e.message);
|
||||
// Скриншот при ошибке
|
||||
try {
|
||||
const png = await browser.screenshot();
|
||||
writeFileSync('error.png', png);
|
||||
console.error('Error screenshot: error.png');
|
||||
} catch {}
|
||||
} finally {
|
||||
await browser.disconnect();
|
||||
}
|
||||
```
|
||||
|
||||
## Важные заметки
|
||||
|
||||
- **Headed mode обязателен** — 1С не работает в headless Chromium
|
||||
- **disconnect() в finally** — ВСЕГДА! Иначе лицензия зависнет на 20 минут
|
||||
- **Fuzzy match** — все функции поиска по имени используют нечёткий поиск (exact → includes)
|
||||
- **Время загрузки** — 1С грузится 30–60 секунд при `connect()`
|
||||
- **Ссылочные поля** — не заполнять через `fillFields`, использовать `selectValue`
|
||||
- **Чекбоксы** — заполнять через `fillFields({field: "true"})` или `fillFields({field: "да"})`
|
||||
- **Ошибки 1С** — если в ответе есть `errorModal`, значит 1С показала ошибку
|
||||
Reference in New Issue
Block a user