Тесты активно используют OS clipboard (`writeText` + Ctrl+V — единственный
способ добиться trusted-paste для autocomplete справочников и кириллицы).
При локальном запуске это перетирало пользовательский буфер. Теперь:
- `pasteText(text, {confirm, postDelay})` в browser.mjs делает узкое окно
save → writeText → confirm-key → restore вокруг каждой пасты (~ms).
- Save/restore через `navigator.clipboard.read()`/`write()` — все MIME
(текст, картинка, HTML), blob'ы стэшатся на `window` без CDP-сериализации.
- 14 callsites переведены на helper.
- При failure save'а (CF_HDROP из Проводника не виден через web-API) restore
явно очищает буфер, чтобы тестовое значение не протекало.
- Опт-аут: CLI `--no-preserve-clipboard`, env `WEB_TEST_PRESERVE_CLIPBOARD=0`,
`preserveClipboard: false` в `webtest.config.mjs`.
Регресс tests/web-test — 6 прогонов 19/19 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run.mjs:
- syncAllureExtras(testDir, reportDir) копирует все файлы из
<testDir>/_allure/ в reportDir перед генерацией отчёта. Underscore
в имени параллелен _hooks.mjs (инфра, не тест) — discovery его
пропускает.
- Вызов после writeAllure при --format=allure.
tests/web-test/_allure/categories.json — 7 правил классификации падений
по нашему 1С-домену:
1. License pool exhausted (1C) — известный multi-context flake.
2. 1C application error (modal) — exception modal через fetchErrorStack.
3. Section panel icon-only — деградация состояния стенда.
4. Navigation lookup miss — navigateSection/openCommand/navigateLink/switchTab.
5. Element not found — clickElement/fillField/selectValue/closeForm/fillTableRow/deleteTableRow.
6. Test timeout — Timeout (Nms) от раннера.
7. Assertion failure — наши createAssertions + 1С-specific (formHasField/tableHasRow/noErrors).
spec §9: раздел «Доп. файлы Allure через <testDir>/_allure/» с таблицей
поддерживаемых типов (categories.json / environment.properties /
executor.json) и минимальным примером.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run.mjs:
- buildSeverityIndex(config) — валидация config.severity (inverted map
«уровень → [теги]») при загрузке: ключи только из blocker|critical|
normal|minor|trivial, теги не дублируются между bucket'ами,
defaultSeverity тоже валидируется. fail-fast через die.
- resolveSeverity(t, severityIndex):
1. mod.severity если задан и валидный — выигрывает.
2. max-rank среди тегов (стандартные имена severity или маппинг).
3. config.defaultSeverity или 'normal'.
Rank: blocker(5) > critical(4) > normal(3) > minor(2) > trivial(1).
Max-wins инвариантен к порядку тегов.
- writeAllure: добавлены labels suite (= dirname(t.file) или 'root') +
severity. Тег `tag` остался как раньше.
- testResult пробрасывает t.severity (для passed/failed веток).
- SEVERITY_RANK/LEVELS объявлены в модульной шапке (top-level await на
cmdTest начинается до конца тела модуля, TDZ-аккуратность).
webtest.config.mjs: severity policy для нашего сьюта (smoke +
multi-context → critical, recording → minor, defaultSeverity = normal).
spec.md §7: раздел про severity-policy в конфиге с валидацией.
spec.md §9: «Авто-эмиссия label-ов» — tag/suite/severity + правила резолва.
Регресс 19/19 ✓ (9m 7.6s). Распределение по уровням после исправления
'record' → 'recording' в маппинге: 13 critical / 5 normal / 1 minor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
browser.mjs:
- + closeContext(name): logout slot + close page (tab) или context (window),
удаление из реестра. Throw если name неактивен (рулило: nicht den aktiven
closen, recorder always attached к active → invariant простой).
- _logoutSlot(slot, waitMs) — извлечён из disconnect, переиспользуется в
closeContext.
run.mjs:
- ensureContext() после createContext вызывает hooks.afterOpenContext(ctx, name, spec).
- wrapCloseContextHook() оборачивает ctx.closeContext (и каждую scoped-обёртку)
чтобы перед browser.closeContext fir'ить hooks.beforeCloseContext.
- Финальный teardown в finally: для всех живых контекстов кроме первого
(survivor) — beforeCloseContext + closeContext; для survivor только хук,
его закрывает disconnect().
_hooks.mjs v0.5:
- afterOpenContext инжектит persistent DOM-badge с displayName в правый
верхний угол page — в записанном видео всегда видно, какой контекст.
- beforeCloseContext counter-only.
- _state расширен полями afterOpenContext / beforeCloseContext.
15-multi-context-handover.test.mjs:
- +2 шага: closeContext('b') после handover, попытка closeContext(active)
ловится throw'ом с проверкой message.
00-hooks.test.mjs:
- +1 ассерт: afterOpenContext >= 1 (default уже создан), beforeCloseContext === 0
в теле первого теста.
spec §6:
- Раздел «Контекстный уровень» (afterOpenContext / beforeCloseContext + правила closeContext).
- ASCII-диаграмма порядка хуков обновлена с per-context lifecycle.
Регресс 19/19 ✓ (9m 16.8s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_hooks.mjs v0.4: beforeEach под условием ctx.isRecording() показывает
title slide с testInfo.name + displayName первичного контекста как
subtitle, ждёт 1.5с через ctx.wait() и убирает.
В обычном регрессе (без --record) — ветка скипается, overhead ноль.
Под --record: 01-navigation 12.1s → 13.9s (+1.8с на слайд).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13-misc.test.mjs: setup-шаг упрощён до `assert.ok(existsSync(epfPath))`.
EPF-сборку (epf-init → form-add → form-compile → epf-build) забрал
_hooks.mjs.prepare() — здесь только проверка артефакта с понятной
ошибкой при отсутствии: «запустите раннер с `-- --rebuild-epf`».
webtest.config.mjs: URL обоих контекстов переключён на
`/webtest-runner/ru_RU` — отдельная публикация автономного стенда,
не конфликтует с интерактивной разведкой через `/webtest` на 8081.
Новый tests/web-test/_hooks.mjs v0.2 с prepare()/cleanup().
prepare() поднимает изолированный стенд:
- Hash-locks `tests/skills/.cache/webtest-stand/{config,epf}.lock`
на sha256 от build-steps и EPF_SPEC — автоматический skip
пересборки при отсутствии изменений.
- Слои конфиг XML / БД / EPF пересобираются независимо. Триггер
ручной — флаги `--rebuild-config`/`--reload-data`/`--rebuild-epf`/
`--rebuild-stand` (через `-- ...` после CLI раннера).
- Smart Apache: web-stop+web-publish выполняются только когда
пересоздаём БД (нужно освободить блокировку). Иначе probe-first:
жив (200) → no-op; мёртв → publish + probeReady. На warm-старте
prepare сводится к чтению локов и одному probe (~200ms).
- web-publish на собственном AppName `webtest-runner` :9191 — не
пересекается с интерактивной публикацией `webtest`.
- Кросс-платформенно: env WEBTEST_HOOKS_RUNTIME=python переключает
на зеркальные py-порты скиллов (для не-Windows стендов).
cleanup() пока stub — оставляем стенд поднятым между прогонами,
для full-shutdown ручной /web-stop или `-- --rebuild-stand`.
E2E-проверено: cold-start `--rebuild-stand` поднимает стенд за
~38s; warm-старт prepare = 0.0s; полный регресс 18/18 зелёный
за 9m 7.1s (включая оба multi-context-теста, которые исторически
флапали).
Раньше шаг был deferred с комментарием «требует список с видимой
filter-панелью». На самом деле существующая абстракция работает:
два advanced filterList на разных колонках Контрагентов создают
два badge'а в state.filters[], а unfilterList({field}) снимает
конкретный — оставляя остальные.
Новый шаг 09-filter/unfilter-specific (~14s):
- filterList('ООО', {field:'Наименование'}) + filterList('123', {field:'ИНН'})
→ state.filters = [{field:'Наименование',value:'ООО'}, {field:'ИНН',value:'123'}]
- unfilterList({field:'ИНН'}) → остался только Наименование badge
- unfilterList() → пусто
Старый комментарий «defer to filter-panel synthetic» удалён —
оказался устаревшим (видимо unfilterList({field}) уже умел работать
с advanced-filter badge'ами на синтетических списках).
timeout 09-filter поднят с 60000 → 120000ms (8 шагов теперь, +14s
для unfilter-specific).
Регресс: 16/18 зелёных. Два multi-context-теста (14/15) упали на
лицензионном пределе 1С — known environmental issue, не связано с
этим коммитом.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Реквизит шапки ПриходнаяНакладная.Менеджер типа CatalogRef.Контрагенты
с дефолтным choiceHistoryOnInput=Auto. Существующий Контрагент в той же
шапке имеет DontUse, что даёт парный контраст для тестирования влияния
флага на selectValue.
Новый шаг 04-selectvalue/auto-history:
- selectValue('Менеджер', 'ООО Юг') → method='dropdown' (typeahead активен,
префиксный поиск по Description находит «ООО Юг» в catalogue).
- Парный 04-selectvalue/direct-form (existing): selectValue('Контрагент',
'Север') → method='form' (typeahead подавлен DontUse → форма выбора).
Тест покрывает существующее ветвление selectValue по флагу
choiceHistoryOnInput без engine-доработок. Истории на сервере писать
заранее не нужно: typeahead использует prefix-match по Description,
а не статистику истории.
Полный регресс **18/18 зелёный** (8m 47.3s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Расширение синтетики: реквизит Поставщик типа CatalogRef.Контрагенты
добавлен в шапку ПриходнаяНакладная. Элемент формы Поставщик скомпилирован
с textEdit:false (новый DSL ключ form-compile v1.21 из коммита 32bf9c1):
ручной ввод запрещён, селект-кнопки нет, выбор только через форму выбора
по pick-кнопке.
Новый шаг 03-fillfields/direct-edit-form (~7s) — fillFields на Поставщик
('ООО Юг') возвращает method:'form', минуя обычные paste/typeahead/dropdown
ветки. fillFields внутренне детектит textEdit:false и сразу идёт через
форму выбора (selectValue path).
Полный регресс **18/18 зелёный** (8m 40.6s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Расширение синтетики: реквизит Источник составного типа
(CatalogRef.Контрагенты + CatalogRef.Номенклатура + CatalogRef.Организации)
добавлен в шапку ПриходнаяНакладная и в ТЧ Товары. meta-compile принимает
составной тип через строковый синтаксис `A + B + C` (см. SKILL.md:56) —
эмитит три `<v8:Type>` элемента с правильным `d5p1:` префиксом.
Элемент ТЧ-колонки переименован в ИсточникТЧ (path/title оставлены
оригинальные) — иначе form-compile генерирует одинаковые companion-имена
(`ИсточникКонтекстноеМеню`) для шапки и ТЧ, и платформа отказывает в
открытии формы документа: "К сожалению, возникла непредвиденная ошибка"
(server-side, без полезного stack). TODO в form-compile-bugs.md: учитывать
путь поля при генерации companion-имён, чтобы избежать конфликта.
Новый шаг 03-fillfields/composite (~25s) — покрывает selectValue с
параметром `{type}` на составном поле:
- Шапка: selectValue('Источник', 'ООО Север', {type:'Контрагенты'})
→ method:'form', type:'Контрагенты', выбор через каталог-форму.
- ТЧ: fillTableRow({Источник: {value:'Альфа', type:'Организации'}},
{row:0}) → method:'form', type:'Организации' (quickChoice=true →
без формы выбора, прямой dropdown).
fillFields на composite без type выбрасывает понятную ошибку
с инструкцией «specify the type: selectValue(...,{type:'ИмяТипа'})» —
поведение API стабильно.
timeout 03-fillfields поднят с 60000 → 120000ms (6 шагов суммарно
~63s, новый composite step добавляет ~25s).
Полный регресс **18/18 зелёный** (8m 28.7s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Расширение синтетики: новая обработка ДеревоНоменклатуры с реквизитом
формы Дерево типа ДеревоЗначений и колонками Номенклатура (ссылка,
read-only) + Цена (Number, editable). ПриСозданииНаСервере рекурсивно
обходит Справочник.Номенклатура и заполняет дерево, отражая иерархию
групп/элементов из справочника.
Обработка зарегистрирована в подсистеме Администрирование и в роли
Администратор (Use+View).
Новый smoke 16-tree-form.test.mjs (5 шагов, 17.1s) — покрывает
05-table/edit-form (fillTableRow method:'direct' на FormDataTree-колонке)
и 08-hierarchy/tree-edit (expand узла + правка Цены через index-row):
- setup: navigateLink('Обработка.ДеревоНоменклатуры'), таблица Дерево
- read-roots: 2 корневые группы (_kind:'group'), columns=Номенклатура,Цена
- expand: clickElement('Товары',{expand:true}) → 16 строк (1 + 15)
- tree-edit: fillTableRow({Цена:1500},{row:1}) → method:'direct',
Цена становится '1 500,00' (с non-breaking space 1С)
- cleanup: closeForm
Гэп: fillTableRow с row-by-name ('Товар 01') ловит SyntaxError в JS
eval. Использую row-by-index (TODO в web-test-bugs).
Полный регресс **18/18 зелёный** (8m 9.8s) на порту 9191.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Чтобы не конфликтовать с интерактивной разработкой на основном
Apache (8081, занят сторонним проектом), регрессионный регресс
теперь использует отдельный httpd-процесс на порту 9191. Тот же
httpd запускает /web-publish webtest -Port 9191 -V8Path 8.3.24.
Один процесс Apache → собственный пул лицензий 1С. На 8081 другие
проекты — наши тесты их не блокируют и наоборот.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Новый 13-misc.test.mjs (3 шага, 11s) — покрытие openFile() для
внешних обработок с автоматической обработкой security confirmation.
- setup: автономный билд EPF (идемпотентный) через epf-init →
form-add → form-compile (с текстовой декорацией) → epf-build.
child_process.spawnSync для вызова PowerShell скриптов.
- openFile: проверки state.form, activeTab='Тест открытия',
state.texts[] содержит декорацию с ожидаемым value,
opened.attempt>=1, security confirm modal не пробивается.
- cleanup: closeForm + soft-проверка activeTab (между тестами в
desktop могут оставаться формы от других тестов — не настаиваем
на formCount=0).
Артефакты в test-tmp/13-openfile/ (.gitignore). Полный регресс
17/17 зелёный (8m 8s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Новый 08-hierarchy.test.mjs (7 шагов, 24s) — покрывает группы и
tree-grid режима «Дерево» на форме списка Номенклатуры через UI
переключение viewMode. Без расширения синтетики.
- setup: явное переключение в «Иерархический список» через Ещё →
Режим просмотра (viewMode сохраняется между сессиями и НЕ
сбрасывается «Установить стандартные настройки»).
- read-groups (P1): readTable возвращает 2 группы (_kind=group).
- group-expand (P1): clickElement({expand:true}) развёртывает группу,
внутри 15 элементов.
- switch-tree: «Ещё → Режим просмотра → Дерево» → viewMode='tree'.
- read-tree (P2): readTable.rows[]._tree (collapsed|expanded) — проверка
только наличия поля (состояние сохраняется между сессиями).
- tree-expand (P1): defensive свёртка через {expand:false} если узел
expanded, затем {expand:true} → kind='gridTreeNode' toggled=true,
видны 15 элементов под Товарами.
- cleanup: восстановить иерархический список.
Замечание: clickElement({expand:true}) — только развернуть (no-op для
expanded), {expand:false} — только свернуть, {toggle:true} —
безусловно переключить.
05-table/direct-edit-form, edit-dblclick остаются deferred — нужен
документ с иерархической ТЧ. Полный регресс 16/16 зелёный (7m 53s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11-report/drill-down: dblclick по ячейке Номенклатуры сформированного
DCS-отчёта открывает форму элемента (DCS auto-drill). После Сформировать
ищется первая строка с заполненной номенклатурой, проверяется что после
clickElement({row,column},{dblclick:true}) form изменился и есть кнопка
«Записать».
02-crud/more-menu усилен под P2 submenu-read: добавлены явные проверки
clicked.kind='submenu', наличия типовых пунктов «Создать», «Изменить»,
«Расширенный поиск» (length>=5).
Покрыто 2 P2-кейса coverage matrix (11-report/drill-down,
02-crud/submenu-read). Полный регресс 14/14 зелёный (7m 1.6s).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10-validation.test.mjs (3 шага): Сообщить() → state.errors.messages,
ВызватьИсключение → onecError.errors.modal с автоматическим закрытием
fetchErrorStack.
14-errors-stack.test.mjs (3 шага): Path 1 OpenReport автоматически фетчит
стек для серверных исключений (entries[] содержит кадр ОбщиеФункции);
оставленная error modal через raw page.click закрывается closeForm;
платформенный диалог «О программе» виден в state.platformDialogs и
закрывается closeForm.
Покрыто 4 P2-кейса coverage matrix: 10-validation/messages,
10-validation/exception-modal, 14-errors/path1, 14-errors/dismiss-platform
+ бонус dismiss-modal. Открытие обработки ТестовыеОшибки через
navigateLink('Обработка.ТестовыеОшибки') — стандартные команды у
DataProcessor отключены.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
browser.mjs v1.12 + run.mjs v1.9: createContext принимает isolation параметр.
По умолчанию 'tab' — все контексты живут в одном launchPersistentContext, каждый
слот получает свою Page (вкладку). Преимущества: 1С extension грузится
надёжно (через --load-extension в persistent profile), один процесс Chromium,
дешёвая память. Cookies делятся между вкладками, но скоупятся по URL-path —
для модели «разные пользователи через разные vrd-публикации» это естественно
и достаточно.
isolation: 'window' (opt-in) — старый путь chromium.launch() + newContext():
полная изоляция cookies, отдельный BrowserContext (и окно) на каждый слот,
но extension может не подняться. Использовать когда нужна изоляция auth
внутри одного URL.
Смешивать режимы в одном прогоне нельзя — createContext бросает явную
ошибку (первый createContext устанавливает activeMode, остальные обязаны
совпадать).
Конфиг tests/web-test/webtest.config.mjs: добавлен комментарий с описанием
обоих режимов. По умолчанию tab — синтетика и наши smoke-тесты идут им.
Live: 11/12 в полном прогоне (default tab) + 3/3 sanity-check в window mode
(01-navigation + 14 + 15). Видеозапись из T4.5 работает в обоих режимах.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
browser.mjs v1.10: createContext/setActiveContext/listContexts/getActiveContext/
hasContext. Несколько изолированных BrowserContext в одном Chromium-процессе через
chromium.launch() + newContext(). Module-level page/sessionPrefix/seanceId/recorder
зеркалят активный слот (атомарный своп через _saveActiveSlot/_activateSlot).
connect() оставлен для exec/run/start без изменений (launchPersistentContext).
run.mjs v1.8: ensureContext(name) + ленивое создание. Single-routing через
export const context = 'name'. Multi через export const contexts = ['a','b'] +
buildScopedContext(name) строит ctx.a/ctx.b — каждое действие префиксится
setActiveContext. Reset state после теста по всем активным контекстам.
Конфиг tests/web-test/webtest.config.mjs: два контекста a/b на одну webtest
публикацию (изолированные cookies через newContext).
Smoke-тесты:
- 14-multi-context-routing.test.mjs — single routing в b (2.6s)
- 15-multi-context-handover.test.mjs — ctx.a создаёт Контрагента, ctx.b в
независимой сессии видит запись через filterList, ctx.a cleanup (14.5s, 4/4)
Live: 11/12 в полном прогоне. 04-selectvalue/direct-form флапает —
pre-existing, воспроизводится на baseline 95e4674 (03→04 sequence).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
section-error / command-error / switchTab error: проверка throw для
несуществующих имён.
navigateLink: link-type (Catalog.Контрагенты) + e1cib URL (с soft-skip
для платформ без поддержки e1cib через Shift+F11).
Live на webtest: 10/10 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
exact: filterList exact:true строго 1 совпадение.
hidden-field: filterList по неотображённому реквизиту через FieldSelector
DLB (КодКПП в синтетике нет — soft-skip).
date: filterList по колонке Дата поступления (синтетика выводит её в форму
списка Номенклатуры).
reference: filterList по ссылочной колонке Контрагент (форма списка ПН).
unfilter-all: unfilterList() полностью восстанавливает список.
unfilter-specific отложен — требует списка с видимой filter-панелью,
synthetic списки фильтруют без создания badge.
cancel-search/clear-input семантически дубликаты unfilter-all через
публичный API.
show-all-form требует quickChoice=true каталога с количеством > порога
(в синтетике нет).
Live на webtest: все 7 шагов passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
modal: F4 на ref-поле открывает модальную форму выбора Контрагентов,
state.modal=true, formCount=2.
tabs: форма элемента Номенклатуры с двумя табами (Основное/Дополнительно)
возвращает state.tabs[].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
02-crud: confirm-save-no (rollback при save:false), confirm-pending
(closeForm() без решения возвращает confirmation), more-menu (clickElement
'Ещё' возвращает submenu).
03-fillfields: clear (Shift+F4 через пустое значение), reference-non-quickchoice
(fillFields на quickChoice=false поле — method=dropdown через DLB; чистый
form-path требует hasPick && !hasSelect, такого поля в синтетике нет).
04-selectvalue: clear (selectValue '' → Shift+F4). show-all-form отложен —
требует quickChoice=true каталога с количеством > порога dropdown
(в синтетике нет).
05-table: checkbox (fillTableRow с Boolean), clear (Shift+F4 на ref-ячейке +
восстановление для последующего delete).
Live на webtest: все шаги проходят.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fillTableRow({Количество, Цена}, {row:1}) — purpose-built проверка inEdit
multi-cell tab-loop. method='direct' для обоих полей, значения
подставляются корректно (live на webtest).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tumbler-представление RadioButtonField не парсится fillFields, но варианты
видны в state.buttons[] и кликаются через clickElement. Уточнили шаг radio:
- RadioButtons (КатегорияЦены) → fillFields с method=radio
- Tumbler (СпособУчёта) → проверка наличия в buttons[] + clickElement('ФИФО')
Семантика Tumbler через fillFields остаётся как баг web-test/browser.mjs
(см. upload/web-test-bugs.md пункт 5), но рабочий путь интеракции есть.
10/10 smoke зелёные после рестарта Apache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
03-fillfields:
- reference-dropdown: переведён с Контрагент на Организация
(после смены quickChoice Контрагенты идут через форму выбора)
- новый шаг radio: КатегорияЦены через method=radio (RadioButtons)
04-selectvalue:
- dropdown: переведён на Организация (quickChoice=true)
- новый шаг direct-form: Контрагент (quickChoice=false), method=form
Закрывает selectValue#3 dropdown (P0), selectValue#6 direct-form (P1),
fillFields#3 radio (P1) из coverage matrix.
Tumbler-представление радио (СпособУчёта) пока не покрыто — getFormState
не возвращает Tumbler в fields[]. Зафиксировано в upload/web-test-bugs.md
пункт 5.
10/10 smoke зелёные на webtest базе.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Синтетика: добавлен template-add ОсновнаяСхемаКомпоновкиДанных к отчёту
(без него skd-compile писал Template.xml в незарегистрированный путь),
переписан DSL skd-compile — fields внутри dataSets, типы полей, totalFields,
явный settingsVariants со structure и быстрым отбором по Номенклатуре
(@off @user @quickAccess).
Тест 11-report покрывает: регистрацию команды в подсистеме, открытие формы
отчёта с дефолтной кнопкой Сформировать, видимость и структуру быстрого
DCS-фильтра, формирование отчёта, применение фильтра через selectValue
(auto-enable чекбокса + значение), пересчёт с фильтром, снятие фильтра
через fillFields toggle off с восстановлением исходных данных.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Покрывает форму списка (form, formCount, openForms, tables, buttons)
и форму элемента (fields с label и value, проверка по конкретному
полю Наименование).
Покрывает:
- filterList('Север') — поиск по всем колонкам списка Контрагенты
- filterList('Север', { field: 'Наименование' }) — фильтр по
конкретной колонке через расширенный поиск
- unfilterList — восстановление исходного набора
Третий запланированный кейс (text-field filter) семантически совпадает
с advanced-column когда колонка строкового типа — оставлен на регресс P1.
Покрывает clickElement по имени страницы как механизм переключения
вкладок формы. Используем форму элемента Номенклатура: page1
показывает шапку (Артикул, ВидНоменклатуры, ...), page2 — Дополнительно
(ЕдиницаИзмерения, Комментарий). Verify: набор state.fields различен
после переключения и совпадает после возврата.
Раньше verify-list брал первый попавшийся проведённый документ Север —
если в базе уже лежал проведённый Север из прошлого прогона, тест
проходил даже если текущий не сохранился. Теперь среди кандидатов
открываем каждый и сверяем Комментарий с уникальным docId текущего
прогона; ассерт срабатывает только при совпадении.
Раньше использовалось отсутствие поля Контрагент после Провести и закрыть
как косвенный признак закрытия — это работало, но было привязано к
конкретному реквизиту накладной. Заменил на сравнение state.form до и
после: номер активной формы меняется (11 → 5), это прямой и общий
признак, что мы переключились с формы документа на другую.
Создание, заполнение шапки и табличной части, Провести и закрыть,
проверка появления документа в списке с Проведён=Да.
Проверка закрытия формы документа: в синтетике web-test форма списка и
форма документа делят один слот (formCount=1 в обоих состояниях),
поэтому используем признак отсутствия поля Контрагент в текущем
state.fields после Провести и закрыть — если поле есть, мы остались
на форме документа.
Покрывает работу с табличной частью Товары документа Приходная накладная:
- fillTableRow с add:true добавляет строки последовательно
- fillTableRow с row:N редактирует существующую строку (Tab-навигация)
- deleteTableRow удаляет строку по индексу
Закрытие формы без сохранения (save:false) — соответствует новой
семантике после фикса form-compile (SavedData).
01-navigation: первое открытое окно 1С имеет form=0 (number), и
assert.ok(state.form, ...) валился на falsy при первом запуске сессии.
Сменил на state.form != null.
04-selectvalue: явный save:false при закрытии модифицированной формы
накладной — после фикса SavedData=true главного реквизита платформа
требует решения по confirmation dialog.
После фикса form-compile (kind=check для Boolean + SavedData=true для
главного реквизита) Активен передаётся как настоящий boolean (toggle),
getFormState возвращает value:true/false. Закрытие модифицированных форм
теперь требует явного save:false — иначе платформа показывает
confirmation dialog «Записать?».
После фикса form-compile (a59be4b SavedData=true для главного реквизита)
canonical confirm-save-yes flow работает без ручного патча Form.xml —
предупреждение в шаге неактуально.
Один P0 кейс из coverage matrix:
- dropdown: selectValue('Контрагент', 'ООО Север') → method='dropdown'
на форме новой ПриходнойНакладной (CatalogRef + малый список)
API возвращает form state с .selected = {field, search, method}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 шага, 5 типов полей зелёные на синтетике webtest:
- text (paste): Артикул на форме Номенклатура
- dropdown (Да/Нет): Активен — Boolean рендерится как Да/Нет селектор
- dropdown (EnumRef): ВидНоменклатуры
- date (paste): ДатаПоступления
- reference (dropdown CatalogRef): Контрагент в новой ПриходнаяНакладная
NB: 1C рендерит Boolean-атрибут не как чекбокс, а как dropdown «Да/Нет»
(actions: ["select"]) — fillFields правильно определяет это.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Гипотеза о баге fillField paste была ошибочной — реальная причина в form-compile
который не эмитит <SavedData>true</SavedData> для MainAttribute главной формы.
Платформа без SavedData не трекает modified-state, confirmation dialog не
появляется.
Платформенная верификация на патченной Form.xml: closeForm({save:true})
после fillField корректно ловит confirmation, жмёт «Да», изменения
сохраняются. См. T11 в upload/web-test-runner-tasks.md.
ВНИМАНИЕ: тест зависит от ручного патча Form.xml. После прогона
build-webtest-db.mjs тест упадёт до фикса form-compile (T11).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 шага зелёные на синтетике webtest:
- read: список Контрагентов отдаёт колонки/строки/total
- open-item: dblclick открывает форму элемента
- close-clean: Escape без изменений закрывает форму без диалога
- save-via-button: fillField + «Записать и закрыть» → значение сохраняется
confirm-save-yes (P0 из coverage matrix) отложен — fillField через paste не
выставляет 1C "modified" флаг, confirmation dialog не появляется. Зафиксировано
в upload/web-test-runner-tasks.md как T11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix: discoverTests падал с ENOTDIR при передаче .test.mjs файла
- Добавлен 01-navigation.test.mjs — навигация по разделам, открытие
списков через navigateLink, переключение между подсистемами
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>