feat(web-test): чтение и раскрытие сворачиваемых групп формы

getFormState().groups → [{name, title, collapsed}] для сворачиваемых
групп (оба варианта ControlRepresentation: заголовок-гиперссылка и
картинка-каретка #titleBtn). Обычные несворачиваемые группы не
попадают. Состояние — по display первого контент-сиблинга за #title_div
(переживает свободные элементы между группами: при обходе Form.xml дети
группы идут до следующего сиблинга).

clickElement(title, {expand}/{expand:false}/{toggle}) раскрывает/
сворачивает группу — единый словарь с грид-узлами/деревьями, клик по
#titleBtn (вариант «картинка») или заголовку-гиперссылке. Новый
kind:'formGroup' в findClickTargetScript + хендлер click-group.mjs.

Фикстура СтраницаНастроек расширена вариантами A/B + негатив (обычная
группа) + стресс-привязка (свободный элемент между группами); тест
25-decoration-form покрывает чтение и expand/collapse/toggle. Полный
регресс 29 passed.

Popup-группы: содержимое в отдельном слое, состояние пока не читается
надёжно (follow-up).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-18 12:56:55 +03:00
co-authored by Claude Opus 4.8
parent 86cddc8ec3
commit bd86ece90e
7 changed files with 167 additions and 5 deletions
@@ -1,4 +1,4 @@
// web-test dom shared v1.5 — embedded JS function constants
// web-test dom shared v1.6 — embedded JS function constants
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
/**
* Shared function strings embedded into page.evaluate() generators.
@@ -674,12 +674,37 @@ function readForm(p) {
});
if (iframeCount) result.iframes = iframeCount;
// Collapsible / popup groups — surface that part of the form is hidden + its state.
// Идентификация раскрываемой группы: у неё есть заголовок <base>#title_text, и либо этот
// заголовок — гиперссылка (.staticTextHyper: ControlRepresentation=TitleHyperlink / popup),
// либо рядом есть кнопка-каретка <base>#titleBtn (ControlRepresentation=Picture). Обычные
// (несворачиваемые) группы не имеют ни того, ни другого — их не показываем.
// Состояние: DOM у 1С плоский (контент группы — сиблинги под mainGroup, не вложены), но при
// глубинном обходе Form.xml ПЕРВЫЙ контент-сиблинг сразу за #title_div — всегда дочерний
// элемент группы (свободные соседи идут после всех детей). Его display = состояние группы:
// none → свёрнута, block → развёрнута. Переживает свободные элементы между группами.
const groups = [];
document.querySelectorAll('[id^="' + p + '"][id$="#title_text"]').forEach(tt => {
if (tt.offsetWidth === 0 && tt.offsetHeight === 0) return;
const base = tt.id.slice(0, -('#title_text'.length));
const isHyper = tt.classList.contains('staticTextHyper');
const hasBtn = !!document.getElementById(base + '#titleBtn');
if (!isHyper && !hasBtn) return; // обычная (несворачиваемая) группа — пропускаем
const title = nbsp(tt.innerText?.trim() || '');
const g = { name: base.replace(p, ''), title };
const titleDiv = document.getElementById(base + '#title_div');
const contentSib = titleDiv?.nextElementSibling;
if (contentSib) g.collapsed = getComputedStyle(contentSib).display === 'none';
groups.push(g);
});
if (fields.length) result.fields = fields;
if (buttons.length) result.buttons = buttons;
if (formTabs.length) result.tabs = formTabs;
if (navigation.length) result.navigation = navigation;
if (texts.length) result.texts = texts;
if (hyperlinks.length) result.hyperlinks = hyperlinks;
if (groups.length) result.groups = groups;
// Group DCS report settings into readable format
if (result.fields) {