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>
This commit is contained in:
Nick Shirokov
2026-07-27 20:54:23 +03:00
co-authored by Claude Opus 5
parent 5472e03417
commit ba27f2218f
5 changed files with 201 additions and 26 deletions
@@ -1284,12 +1284,23 @@ export const steps = [
title: 'Страница настроек',
// Без командной панели → на форме нет ни одного a.press.
properties: { commandBarLocation: 'None' },
events: { OnCreateAtServer: 'ПриСозданииНаСервере' },
attributes: [
{ name: 'Объект', type: 'DataProcessorObject.СтраницаНастроек', main: true },
// Содержимое свёрнутой группы-с-таблицей (ниже). Заполняется в ПриСозданииНаСервере.
{ name: 'ТаблицаВСвёрнутой', type: 'ValueTable', columns: [
{ name: 'Товар', type: 'CatalogRef.Номенклатура', title: 'Товар' },
{ name: 'Количество', type: 'Number(15,3)', title: 'Количество' },
]},
],
elements: [
// Гиперссылка-декорация → .staticTextHyper (кликабельна через findClickTargetScript).
{ label: 'СсылкаТехническаяИнформация', title: 'Техническая информация', hyperlink: true },
// РАСТЯНУТАЯ гиперссылка (horizontalStretch + autoMaxWidth:false → контейнер во всю
// ширину формы). Обработчик Нажатие сообщает — без следа доставку клика не отличить
// от no-op (у обычной декорации обработчика нет вовсе).
{ label: 'СсылкаРастянутая', title: 'Растянутая ссылка', hyperlink: true,
horizontalStretch: true, autoMaxWidth: false, events: { Click: 'СсылкаРастянутаяНажатие' } },
// Сворачиваемая группа с дочерней декорацией (пустая группа не рендерится).
{ group: 'collapsible', name: 'ГруппаКлассификаторы', title: 'Классификаторы и курсы валют',
collapsed: true, showTitle: true, children: [
@@ -1334,11 +1345,46 @@ export const steps = [
{ label: 'ВнутриСтрессРазвёрнутой', title: 'Внутри развёрнутой стресс-группы' },
],
},
// СВЁРНУТАЯ ГРУППА С ТАБЛИЦЕЙ — все группы выше содержат только декорации, а 1С
// рендерит их скрытыми (display:none), из-за чего эвристика «первый сиблинг за
// #title_div» работает. Грид — тяжёлый контент, платформа может создавать его
// DOM лениво (только при первом раскрытии). Тогда сиблинг за заголовком — чужой
// видимый узел → collapsed:false при реально свёрнутой группе (багрепорт 2026-07-23).
{ group: 'collapsible', name: 'ГруппаСвёрнутаяСТаблицей', title: 'Свёрнутая с таблицей',
collapsed: true, showTitle: true, children: [
{ table: 'ТаблицаВСвёрнутой', path: 'ТаблицаВСвёрнутой', changeRowSet: true, columns: [
{ input: 'Товар', path: 'ТаблицаВСвёрнутой.Товар', title: 'Товар' },
{ input: 'Количество', path: 'ТаблицаВСвёрнутой.Количество', 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' },
},
{
name: 'writeFile: СтраницаНастроек form Module.bsl',
writeFile: 'DataProcessors/СтраницаНастроек/Forms/ФормаОбработки/Ext/Form/Module.bsl',
content: `&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
\tЗапрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 3 Ссылка КАК Ссылка ИЗ Справочник.Номенклатура ГДЕ НЕ ЭтоГруппа УПОРЯДОЧИТЬ ПО Наименование");
\tВыборка = Запрос.Выполнить().Выбрать();
\tСч = 0;
\tПока Выборка.Следующий() Цикл
\t\tСтрока = ТаблицаВСвёрнутой.Добавить();
\t\tСтрока.Товар = Выборка.Ссылка;
\t\tСтрока.Количество = (Сч + 1) * 10;
\t\tСч = Сч + 1;
\tКонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура СсылкаРастянутаяНажатие(Элемент)
\tСообщить("клик по растянутой ссылке доставлен");
КонецПроцедуры
`,
},
// Обработка МножественныйВыбор — основная форма с 4 полями типа «список значений».
{
@@ -71,6 +71,52 @@ export default async function({ navigateSection, openCommand, navigateLink, getF
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 === 'ГруппаВсплывающая');