feat(web-test): гард недоступных элементов + метка disabled в getFormState

clickElement/fillFields/selectValue бросали ложный успех при действии над
недоступным (disabled) контролом — в 1С это no-op. Причина: резолвер цели
клика не смотрел признак недоступности, который ридер getFormState уже знал.

- резолвер клика снимает disabled (кнопки/frameButton/флажок/тумблер/поле),
  clickElement бросает `"X" is disabled` вместо тихого no-op;
- fillFields и selectValue тоже бросают на недоступном поле/флажке/ссылке;
- getFormState помечает disabled у frameButton, флажка, переключателя и
  тумблера (раньше был только у a.press-кнопок и полей ввода);
- стенд: обработка ПроверкаДоступности с парами доступный/недоступный по всем
  типам контролов (в подсистеме Администрирование) + тест 23-availability;
- SKILL.md: заметка про disabled у getFormState и throw у clickElement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-16 13:53:33 +03:00
parent 4a778cb3b1
commit b81a7504ce
9 changed files with 299 additions and 21 deletions
@@ -1,4 +1,4 @@
// web-test core/click v1.22 — clickElement dispatcher: routes to spreadsheet / popup / grid-row / form-element / field-focus handlers by target kind.
// web-test core/click v1.23 — clickElement dispatcher: routes to spreadsheet / popup / grid-row / form-element / field-focus handlers by target kind.
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import { page, ensureConnected, highlightMode } from './state.mjs';
@@ -116,7 +116,12 @@ export async function clickElement(text, { dblclick, table, toggle, expand, modi
throw new Error(`clickElement: "${text}" not found. Available: ${target.available?.join(', ') || 'none'}`);
}
// 4. Dispatch to the right handler by target kind.
// 4. Guard: a disabled target (button/frameButton/checkbox/tumbler/field) is a silent
// no-op in 1C — clicking it does nothing. Fail loud instead of masquerading success.
// Availability is checkable up front via getFormState().buttons[].disabled / fields[].disabled.
if (target.disabled) throw new Error(`clickElement: "${text}" is disabled`);
// 5. Dispatch to the right handler by target kind.
const ctx = { formNum, modifier, dblclick, toggle, expand, timeout, table, gridSelector };
if (target.kind === 'gridGroup' || target.kind === 'gridParent') return await clickGridGroupTarget(target, ctx);
if (target.kind === 'gridTreeNode') return await clickGridTreeNodeTarget(target, ctx);
@@ -1,4 +1,4 @@
// web-test forms/fill v1.20 — Fill form fields by name (text/checkbox/date/number/dropdown/reference; array → multi-select via selectValue). Delegates references to selectValue / fillReferenceField.
// web-test forms/fill v1.21 — Fill form fields by name (text/checkbox/date/number/dropdown/reference; array → multi-select via selectValue). Delegates references to selectValue / fillReferenceField.
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import {
@@ -33,6 +33,12 @@ export async function fillFields(fields) {
results.push(r);
continue;
}
// Disabled field: filling/toggling it is a silent no-op in 1C. Fail loud via the
// standard failure aggregation below instead of reporting a fake success.
if (r.disabled) {
results.push({ field: r.field, error: 'disabled', message: `field "${r.field}" is disabled` });
continue;
}
// Array value → multi-select. Delegate to selectValue's array branch (auto-detects
// the surface) so fillFields({field:[...]}) works the same as selectValue(field,[...]).
if (Array.isArray(fields[r.field])) {
@@ -1,4 +1,4 @@
// web-test forms/select-value v1.33 — Reference & composite-type value selection: selectValue (+ array multi-select), fillReferenceField, selection/type-dialog pickers.
// web-test forms/select-value v1.34 — Reference & composite-type value selection: selectValue (+ array multi-select), fillReferenceField, selection/type-dialog pickers.
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import {
@@ -942,6 +942,9 @@ export async function selectValue(fieldName, searchText, { type } = {}) {
btn = await page.evaluate(findFieldButtonScript(formNum, fieldName, 'CB'));
}
if (btn?.error) return btn;
// Disabled reference field keeps its pick button visible, so the click would silently
// do nothing. Fail loud, consistent with clickElement's disabled guard.
if (btn?.disabled) throw new Error(`selectValue: field "${btn.fieldName || fieldName}" is disabled`);
if (highlightMode) try { await highlight(fieldName); await page.waitForTimeout(500); await unhighlight(); } catch {}
try {