fix(web-test): детектировать форму-декорацию без полей ввода

Страницы настроек (напр. «Администрирование → Интернет-поддержка и
сервисы») собраны из гиперссылок, frameButton и сворачиваемых групп —
без единого input.editInput / textarea / a.press. detectForm/detectForms
считали такую форму отсутствующей → getFormState = {form:null,
formCount:0}, навык её не видел.

Расширен союзный селектор детекции (.staticTextHyper/.frameButton/
.checkbox/.radio/.tumblerItem/.grid). detectForm двухуровневый: обычные
формы выбираются по редактируемым контролам (поведение не меняется), по
расширенному счёту — только когда у формы нет ни одного поля ввода.
form0 (рабочий стол) по-прежнему исключён фильтром n>0.

Регресс: обработка-фикстура СтраницаНастроек (форма без командной панели:
гиперссылка + сворачиваемая группа) в подсистеме Администрирование +
тест 25-decoration-form. Полный набор 29 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-17 22:03:13 +03:00
co-authored by Claude Opus 4.8
parent 37f39a5022
commit 9856fd5180
3 changed files with 111 additions and 8 deletions
@@ -1257,6 +1257,51 @@ export const steps = [
`,
},
// Обработка СтраницаНастроек — форма-«страница настроек» БЕЗ полей ввода: только
// гиперссылка-декорация + сворачиваемая группа (как «Администрирование → Интернет-поддержка
// и сервисы» в реальных конфигурациях). commandBarLocation:None убирает автокомандную панель,
// поэтому у формы НЕТ ни одного input.editInput / textarea / a.press — это воспроизводит баг
// детекции формы (detectForm возвращал null → getFormState = «No form detected»). Регресс на
// расширенный селектор детекции (dom/_shared.mjs). Открывается командой раздела и navigateLink.
{
name: 'meta-compile: Обработка СтраницаНастроек',
script: 'meta-compile/scripts/meta-compile',
input: {
type: 'DataProcessor', name: 'СтраницаНастроек',
},
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
validate: { script: 'meta-validate/scripts/meta-validate', flag: '-ObjectPath', path: 'DataProcessors/СтраницаНастроек' },
},
{
name: 'form-add: Форма обработки СтраницаНастроек',
script: 'form-add/scripts/form-add',
args: { '-ObjectPath': '{workDir}/DataProcessors/СтраницаНастроек.xml', '-FormName': 'ФормаОбработки' },
},
{
name: 'form-compile: Форма обработки СтраницаНастроек',
script: 'form-compile/scripts/form-compile',
input: {
title: 'Страница настроек',
// Без командной панели → на форме нет ни одного a.press.
properties: { commandBarLocation: 'None' },
attributes: [
{ name: 'Объект', type: 'DataProcessorObject.СтраницаНастроек', main: true },
],
elements: [
// Гиперссылка-декорация → .staticTextHyper (кликабельна через findClickTargetScript).
{ label: 'СсылкаТехническаяИнформация', title: 'Техническая информация', hyperlink: true },
// Сворачиваемая группа с дочерней декорацией (пустая группа не рендерится).
{ group: 'collapsible', name: 'ГруппаКлассификаторы', title: 'Классификаторы и курсы валют',
collapsed: true, showTitle: true, children: [
{ label: 'ПояснениеКлассификаторы', title: 'Загрузка курсов валют и классификаторов' },
],
},
],
},
args: { '-JsonPath': '{inputFile}', '-OutputPath': '{workDir}/DataProcessors/СтраницаНастроек/Forms/ФормаОбработки/Ext/Form.xml' },
validate: { script: 'form-validate/scripts/form-validate', flag: '-FormPath', path: 'DataProcessors/СтраницаНастроек/Forms/ФормаОбработки/Ext/Form.xml' },
},
// Обработка МножественныйВыбор — основная форма с 4 полями типа «список значений».
{
name: 'form-add: Основная форма МножественныйВыбор',
@@ -1761,6 +1806,7 @@ export const steps = [
'DataProcessor.ТестовыеОшибки',
'DataProcessor.ДеревоНоменклатуры',
'DataProcessor.ПроверкаДоступности',
'DataProcessor.СтраницаНастроек',
],
},
args: { '-DefinitionFile': '{inputFile}', '-OutputDir': '{workDir}' },
@@ -1786,6 +1832,7 @@ export const steps = [
'DataProcessor.БезшапочнаяТаблица: Use View',
'DataProcessor.МногострочнаяШапка: Use View',
'DataProcessor.ПроверкаДоступности: Use View',
'DataProcessor.СтраницаНастроек: Use View',
],
},
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
@@ -0,0 +1,36 @@
export const name = 'decoration-form: форма без полей ввода (гиперссылки/группы) детектируется';
export const tags = ['formstate', 'smoke'];
export const timeout = 90000;
// Обработка СтраницаНастроек — форма-«страница настроек» БЕЗ единого input.editInput /
// textarea / a.press (commandBarLocation:None): только гиперссылка-декорация + сворачиваемая
// группа. Воспроизводит реальный кейс «Администрирование → Интернет-поддержка и сервисы».
// Регресс на расширенный селектор детекции формы (dom/_shared.mjs, DETECT_FORM_FN /
// DETECT_FORMS_FN): до фикса detectForm возвращал null → getFormState = «No form detected»
// (form:null, formCount:0), навык такую форму НЕ видел.
export default async function({ navigateSection, openCommand, navigateLink, getFormState, closeForm, assert, step, log }) {
await step('раздел: открыть «Страница настроек» командой из «Администрирование»', async () => {
await navigateSection('Администрирование');
const r = await openCommand('Страница настроек');
log(`form=${r.form} formCount=${r.formCount} activeTab=${r.activeTab}`);
assert.ok(r.form != null, 'форма распознана (form != null) — до фикса тут был null');
assert.ok(r.formCount >= 1, 'formCount >= 1');
const hy = (r.hyperlinks || []).map(h => h.name);
assert.includes(hy, 'Техническая информация', 'гиперссылка-декорация видна в состоянии');
assert.equal((r.fields || []).length, 0, 'полей ввода на форме нет (декорации-only)');
await closeForm();
});
await step('navigateLink: та же форма через «Обработка.СтраницаНастроек»', async () => {
const r = await navigateLink('Обработка.СтраницаНастроек');
log(`form=${r.form} formCount=${r.formCount} activeTab=${r.activeTab}`);
assert.ok(r.form != null, 'форма распознана через navigateLink');
assert.ok(r.formCount >= 1, 'formCount >= 1');
const state = await getFormState();
const hy = (state.hyperlinks || []).map(h => h.name);
assert.includes(hy, 'Техническая информация', 'гиперссылка видна и в getFormState');
await closeForm();
});
}