mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-23 13:11:05 +03:00
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:
co-authored by
Claude Opus 4.8
parent
86cddc8ec3
commit
bd86ece90e
@@ -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) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test dom/forms v1.10 — form detection, content read, click-target/field-button resolution
|
||||
// web-test dom/forms v1.11 — form detection, content read, click-target/field-button resolution
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import { DETECT_FORM_FN, READ_FORM_FN, ROW_CLICK_POINT_FN } from './_shared.mjs';
|
||||
|
||||
@@ -95,13 +95,36 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
// Hyperlinks (staticTextHyper)
|
||||
// Hyperlinks (staticTextHyper) — кроме заголовков сворачиваемых групп (#title_text),
|
||||
// они обрабатываются ниже как kind:'formGroup' (клик = раскрыть/свернуть, не переход).
|
||||
[...document.querySelectorAll('[id^="' + p + '"].staticTextHyper')].filter(el => el.offsetWidth > 0).forEach(el => {
|
||||
if (el.id.endsWith('#title_text')) return;
|
||||
const idName = el.id.replace(p, '');
|
||||
const text = norm(el.innerText);
|
||||
items.push({ id: el.id, name: text, label: idName, kind: 'hyperlink' });
|
||||
});
|
||||
|
||||
// Сворачиваемые/всплывающие группы — заголовок как цель раскрытия/сворачивания.
|
||||
// Идентификация: <base>#title_text — гиперссылка (TitleHyperlink/popup) ЛИБО рядом есть
|
||||
// кнопка-каретка <base>#titleBtn (ControlRepresentation=Picture). Обычные группы пропускаем.
|
||||
// Мишень клика: #titleBtn (вариант «картинка») иначе сам заголовок-гиперссылка.
|
||||
// Состояние: первый контент-сиблинг за #title_div (display:none = свёрнута).
|
||||
[...document.querySelectorAll('[id^="' + p + '"][id$="#title_text"]')]
|
||||
.filter(el => el.offsetWidth > 0 || el.offsetHeight > 0).forEach(el => {
|
||||
const base = el.id.slice(0, -('#title_text'.length));
|
||||
const btn = document.getElementById(base + '#titleBtn');
|
||||
const btnVisible = btn && (btn.offsetWidth > 0 || btn.offsetHeight > 0);
|
||||
if (!el.classList.contains('staticTextHyper') && !btnVisible) return; // обычная группа
|
||||
const tgt = btnVisible ? btn : el;
|
||||
const r = tgt.getBoundingClientRect();
|
||||
const titleDiv = document.getElementById(base + '#title_div');
|
||||
const sib = titleDiv && titleDiv.nextElementSibling;
|
||||
const item = { id: '', kind: 'formGroup', name: norm(el.innerText) || base.replace(p, ''),
|
||||
label: base.replace(p, ''), x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) };
|
||||
if (sib) item.collapsed = getComputedStyle(sib).display === 'none';
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
// Frame buttons
|
||||
[...document.querySelectorAll('[id^="' + p + '"] .frameButton, [id^="' + p + '"].frameButton')].filter(el => el.offsetWidth > 0).forEach(el => {
|
||||
const text = norm(el.innerText);
|
||||
@@ -211,6 +234,7 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
||||
if (found) {
|
||||
const res = { id: found.id, kind: found.kind, name: found.name };
|
||||
if (found.disabled) res.disabled = true;
|
||||
if (found.collapsed != null) res.collapsed = found.collapsed;
|
||||
if (found.x != null) { res.x = found.x; res.y = found.y; }
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test core/click v1.23 — clickElement dispatcher: routes to spreadsheet / popup / grid-row / form-element / field-focus handlers by target kind.
|
||||
// web-test core/click v1.24 — clickElement dispatcher: routes to spreadsheet / popup / form-group / 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';
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
clickConfirmationButton, tryClickPopupItem,
|
||||
} from '../forms/click-popup.mjs';
|
||||
import { clickFormTarget, focusFormField } from '../forms/click-form.mjs';
|
||||
import { clickFormGroupTarget } from '../forms/click-group.mjs';
|
||||
import {
|
||||
clickSpreadsheetCell, findSpreadsheetCellByText,
|
||||
} from '../spreadsheet/spreadsheet.mjs';
|
||||
@@ -123,6 +124,7 @@ export async function clickElement(text, { dblclick, table, toggle, expand, modi
|
||||
|
||||
// 5. Dispatch to the right handler by target kind.
|
||||
const ctx = { formNum, modifier, dblclick, toggle, expand, timeout, table, gridSelector };
|
||||
if (target.kind === 'formGroup') return await clickFormGroupTarget(target, ctx);
|
||||
if (target.kind === 'gridGroup' || target.kind === 'gridParent') return await clickGridGroupTarget(target, ctx);
|
||||
if (target.kind === 'gridTreeNode') return await clickGridTreeNodeTarget(target, ctx);
|
||||
if (target.kind === 'gridRow') return await clickGridRowTarget(target, ctx);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// web-test forms/click-group v1.0 — click handler for collapsible/popup form-group titles.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
//
|
||||
// Reuses the tree/grid expand vocabulary so the model has ONE mental model:
|
||||
// clickElement('<заголовок группы>', { expand: true }) — раскрыть (идемпотентно)
|
||||
// clickElement('<заголовок группы>', { expand: false }) — свернуть (идемпотентно)
|
||||
// clickElement('<заголовок группы>', { toggle: true }) — переключить
|
||||
// clickElement('<заголовок группы>') — переключить (голый клик)
|
||||
//
|
||||
// target.collapsed приходит из findClickTargetScript (по display первого контент-сиблинга).
|
||||
|
||||
import { waitForStable } from '../core/wait.mjs';
|
||||
import { modifierClick, returnFormState } from '../core/helpers.mjs';
|
||||
import { shouldClickToggle } from '../table/grid-toggle.mjs';
|
||||
|
||||
export async function clickFormGroupTarget(target, ctx) {
|
||||
const { formNum, modifier, toggle, expand } = ctx;
|
||||
// shouldClickToggle ждёт { isExpanded }; при неизвестном состоянии (undefined) кликаем всегда.
|
||||
const state = target.collapsed == null ? null : { isExpanded: !target.collapsed };
|
||||
const shouldClick = shouldClickToggle(state, expand, toggle);
|
||||
if (shouldClick) await modifierClick(target.x, target.y, modifier);
|
||||
await waitForStable(formNum);
|
||||
return returnFormState({
|
||||
clicked: { kind: 'formGroup', name: target.name, toggled: shouldClick, ...(modifier ? { modifier } : {}) },
|
||||
hint: shouldClick
|
||||
? 'Group toggled. Call getFormState — its groups[].collapsed and revealed/hidden content update.'
|
||||
: 'Group already in desired state.',
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user