Files
cc-1c-skills/tests/web-test/25-decoration-form.test.mjs
T
Nick ShirokovandClaude Opus 5 ba27f2218f fix(web-test): фактическое состояние свёрнутой группы и попадание клика по заголовку
Группа, содержимое которой — таблица, отдавала collapsed:false в обоих состояниях,
а свернуть её обратно было нельзя. Два независимых дефекта:

1. Первым сиблингом за <base>#title_div платформа кладёт пустую служебную обёртку
   <дочерний>#group_div.logicGroupContainer (display:block, height:0), а контент идёт
   дальше по сиблингам — внутри обёртки его нет. Чтение display первого сиблинга
   давало collapsed:false и свёрнутой, и развёрнутой группе.
2. <base>#title_text растягивается по ширине содержимого (173px свёрнута → 1295px
   развёрнута), кликабелен только вложенный label шириной по тексту. Клик в
   геометрический центр попадал в пустоту: раскрыть удавалось, свернуть — нет.

GROUP_STATE_FN обходит сиблинги по id-префиксу обёртки (префикс обрывает обход на
чужих узлах — свободный элемент между группами по-прежнему не путает определение).
TEXT_CLICK_POINT_FN целится во вложенный текст с клампом влево, как rowClickPoint.
Обе — общие для getFormState().groups[] и резолвера цели клика.

Клик по заголовку, не изменивший состояние, теперь бросает ошибку вместо
toggled:true — идемпотентность {expand} не затронута, при нечитаемом collapsed
проверка пропускается.

Фикстура: свёрнутая группа с таблицей (прежние содержали только декорации, поэтому
регресс был зелёным) и растянутая гиперссылка с Сообщить в обработчике — у декораций
промаха нет, их внутренний узел тянется вместе с контейнером.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 20:54:23 +03:00

138 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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), навык такую форму НЕ видел.
//
// Плюс: сворачиваемые группы формы в getFormState().groups (состояние collapsed) и их
// раскрытие/сворачивание через clickElement {expand}/{toggle}. Форма содержит оба варианта
// рендера контрола (заголовок-гиперссылка и картинка-каретка ControlRepresentation), негатив
// (обычная несворачиваемая группа — НЕ в groups[]) и стресс-привязку (свободный элемент между
// группами не путает определение состояния).
export default async function({ navigateSection, openCommand, navigateLink, getFormState, clickElement, closeForm, assert, step, log }) {
const collapsedOf = (s, name) => (s.groups || []).find(x => x.name === name)?.collapsed;
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();
});
await step('groups: getFormState показывает сворачиваемые группы + состояние', async () => {
const s = await navigateLink('Обработка.СтраницаНастроек');
log(`groups=${JSON.stringify((s.groups || []).map(g => [g.name, g.collapsed]))}`);
assert.ok(s.groups?.length, 'groups[] присутствует');
assert.equal(collapsedOf(s, 'ГруппаКлассификаторы'), true, 'вариант A (гиперссылка) — свёрнута');
assert.equal(collapsedOf(s, 'ГруппаРазвёрнутая'), false, 'развёрнутая — collapsed:false');
assert.equal(collapsedOf(s, 'ГруппаКартинкой'), true, 'вариант B (картинка) — свёрнута');
assert.ok(!s.groups.some(g => g.name === 'ГруппаОбычная'),
'обычная несворачиваемая группа НЕ попадает в groups[] (негатив — не шумит)');
// Стресс-привязка: свободный элемент между группами не путает определение состояния.
assert.equal(collapsedOf(s, 'ГруппаСтрессСвёрнутая'), true, 'стресс: свёрнутая определена верно');
assert.equal(collapsedOf(s, 'ГруппаСтрессРазвёрнутая'), false, 'стресс: развёрнутая определена верно');
});
await step('group toggle: clickElement {expand}/{toggle} по вариантам A и B', async () => {
// Вариант A (заголовок-гиперссылка): раскрыть → идемпотентный повтор → свернуть.
let r = await clickElement('Классификаторы и курсы валют', { expand: true });
assert.equal(r.clicked.toggled, true, 'A expand:true — кликнул');
assert.equal(collapsedOf(r, 'ГруппаКлассификаторы'), false, 'A раскрыта');
r = await clickElement('Классификаторы и курсы валют', { expand: true });
assert.equal(r.clicked.toggled, false, 'A expand:true повторно — идемпотентно (no-op)');
r = await clickElement('Классификаторы и курсы валют', { expand: false });
assert.equal(collapsedOf(r, 'ГруппаКлассификаторы'), true, 'A свёрнута обратно');
// Вариант B (картинка-каретка #titleBtn): toggle.
r = await clickElement('Свёрнута картинкой', { toggle: true });
assert.equal(collapsedOf(r, 'ГруппаКартинкой'), false, 'B раскрыта тоглом');
await closeForm();
});
await step('свёрнутая группа С ТАБЛИЦЕЙ: collapsed против факта видимости грида', async () => {
// Багрепорт 2026-07-23. Два дефекта на этой группе (у остальных содержимое — декорации):
// 1) 1С кладёт первым сиблингом за #title_div служебную обёртку <дочерний>#group_div
// (logicGroupContainer, display:block, height:0), а контент идёт дальше по сиблингам →
// чтение display первого сиблинга давало collapsed:false в ОБОИХ состояниях;
// 2) #title_text растягивается по ширине содержимого (173px свёрнута → 1295px развёрнута),
// кликабелен только вложенный label слева → клик в центр промахивался, и свернуть
// обратно было нельзя.
// Грид в tables[] — независимая от collapsed проверка факта.
const grid = (st) => (st.tables || []).some(t => t.name === 'ТаблицаВСвёрнутой');
const s = await navigateLink('Обработка.СтраницаНастроек');
assert.equal(collapsedOf(s, 'ГруппаСвёрнутаяСТаблицей'), true, 'шаг 0: collapsed:true сразу после открытия');
assert.equal(grid(s), false, 'шаг 0: грида нет — группа действительно свёрнута');
let r = await clickElement('Свёрнутая с таблицей', { expand: true });
assert.equal(r.clicked.toggled, true, 'expand:true — кликнул');
assert.equal(collapsedOf(r, 'ГруппаСвёрнутаяСТаблицей'), false, 'раскрыта');
assert.equal(grid(r), true, 'грид появился в tables[]');
r = await clickElement('Свёрнутая с таблицей', { expand: true });
assert.equal(r.clicked.toggled, false, 'expand:true повторно — идемпотентно (no-op)');
// Свернуть обратно: до фикса точки клика этот шаг молча не срабатывал (заголовок растянут).
r = await clickElement('Свёрнутая с таблицей', { expand: false });
assert.equal(r.clicked.toggled, true, 'expand:false — кликнул');
assert.equal(collapsedOf(r, 'ГруппаСвёрнутаяСТаблицей'), true, 'свёрнута обратно');
assert.equal(grid(r), false, 'грид пропал из tables[]');
await closeForm();
});
await step('растянутая гиперссылка: клик доходит до обработчика', async () => {
// Проверка соседнего с дефектом 2 случая. Ссылка растянута на всю ширину формы
// (horizontalStretch + autoMaxWidth:false → контейнер 1295px), но промаха тут НЕ бывает:
// её внутренний текстовый узел — div.ellipsis.flex-1-1-100, он тянется ВМЕСТЕ с
// контейнером (1293 из 1295), тогда как у заголовка группы внутри label без flex-1-1-100,
// шириной по тексту. Шаг стоит страховкой на случай смены вёрстки платформой.
// Обработчик Нажатие сообщает — без следа доставку клика не отличить от no-op.
const сообщения = (st) => (st.errors?.messages || []).join(' | ');
const s = await navigateLink('Обработка.СтраницаНастроек');
assert.equal(сообщения(s), '', 'до клика сообщений нет');
const r = await clickElement('Растянутая ссылка');
assert.includes(сообщения(r), 'клик по растянутой ссылке доставлен',
'клик по растянутой ссылке дошёл до обработчика');
await closeForm();
});
await step('popup: всплывающая группа — behavior, открытие показывает содержимое', async () => {
const s = await navigateLink('Обработка.СтраницаНастроек');
const pg = (s.groups || []).find(g => g.name === 'ГруппаВсплывающая');
assert.ok(pg, 'всплывающая группа в groups[]');
assert.equal(pg.behavior, 'popup', 'помечена behavior:popup (отличима от collapsible)');
assert.equal(pg.collapsed, true, 'закрыта — collapsed:true');
assert.ok(!(s.texts || []).some(t => /всплывающей/.test(t.value)), 'содержимое скрыто, пока закрыта');
// Открыть → содержимое панели становится видно в состоянии формы.
let r = await clickElement('Всплывающая группа', { expand: true });
assert.equal((r.groups || []).find(g => g.name === 'ГруппаВсплывающая')?.collapsed, false, 'открыта');
assert.ok((r.texts || []).some(t => /всплывающей/.test(t.value)), 'после открытия содержимое видно');
r = await clickElement('Всплывающая группа', { expand: true });
assert.equal(r.clicked.toggled, false, 'expand идемпотентен для popup');
r = await clickElement('Всплывающая группа', { expand: false });
assert.equal((r.groups || []).find(g => g.name === 'ГруппаВсплывающая')?.collapsed, true, 'закрыта обратно');
await closeForm();
});
}