mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-28 07:31:00 +03:00
Группа, содержимое которой — таблица, отдавала 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>
138 lines
12 KiB
JavaScript
138 lines
12 KiB
JavaScript
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();
|
||
});
|
||
}
|