Files
cc-1c-skills/tests/web-test/16-tree-form.test.mjs
T
Nick Shirokov 7c9769c644 feat(web-test): fillTableRow заполняет ячейку-выбор-из-списка через форму выбора
Поле с кнопкой выбора и обработчиком НачалоВыбора (значение выбирается из программного
списка — например колонка Тип в типовой Консоли запросов) раньше заполнялось plain-paste,
который молча откатывался → ok:true/method:direct (ложный успех). Теперь движок детектит
такую ячейку и выбирает значение из формы выбора.

- dom/grid-edit.mjs: readActiveGridCellScript отдаёт buttonKind активной ячейки
  (ref/calc/date/choice по кнопке _DLB/_CB и её классу).
- engine/table/row-fill.mjs v1.20: для kind=choice — F4 → pickFromTypeDialog
  (скан/Ctrl+F/OK) → method:choice; если после выбора открылась форма значения,
  это составная ячейка (нужен {value,type}). Ветка добавлена в Tab-цикл и directEditPick.
- engine/forms/select-value.mjs v1.21: умный dismiss диалога типов на путях
  not_found/multiple — Escape только пока диалог открыт, больше не закрывает
  исходную форму слепым Escape×3.
- Стенд: строковая колонка-выбор ТипЗначения (НачалоВыбора → ПоказатьВыборЭлемента)
  в ДеревоНоменклатуры; тест 16 покрывает method:choice и негатив not_found.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:26:37 +03:00

116 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = 'tree-form: FormDataTree edit (ДеревоНоменклатуры obrabotka)';
export const tags = ['tree', 'table', 'picture'];
export const timeout = 90000;
// ДеревоНоменклатуры obrabotka: реквизит формы Дерево типа ДеревоЗначений
// заполняется в ПриСозданииНаСервере рекурсивным обходом справочника Номенклатура.
// Колонки: Номенклатура (CatalogRef, readOnly), Цена (Number, editable),
// Картинка (PictureField на булев, ValuesPicture=StdPicture.Favorites, иконка у Цена>1000),
// Флаг (CheckBoxField на тот же булев — кросс-проверка). Selection-обработчик
// ДеревоВыбор инвертирует Картинка по двойному клику в колонке.
// Покрывает: 05-table/edit-form (fillTableRow method:'direct' на FormDataTree-колонке)
// + 08-hierarchy/tree-edit (expand узла + edit Цены внутри expanded группы)
// + readTable picture-колонки (pic:N/'') и Selection-toggle.
export default async function({ navigateLink, clickElement, closeForm, readTable, fillTableRow, assert, step, log }) {
await step('setup: открыть обработку ДеревоНоменклатуры', async () => {
const r = await navigateLink('Обработка.ДеревоНоменклатуры');
log(`form=${r.form} activeTab=${r.activeTab}`);
assert.equal(r.activeTab, 'Дерево номенклатуры', 'форма открыта');
assert.ok(r.tables?.some(t => t.name === 'Дерево'), 'таблица Дерево присутствует');
});
await step('read-roots: на верхнем уровне видны группы (Товары, Услуги, БольшойСписок)', async () => {
const t = await readTable('Дерево');
log(`columns=${t.columns?.join(',')} rows=${t.rows?.length}`);
assert.deepEqual(t.columns, ['Номенклатура', 'Цена', 'Картинка', 'Флаг', 'Тип значения'], 'колонки: Номенклатура + Цена + Картинка + Флаг + Тип значения');
assert.equal(t.rows.length, 3, '3 корневые строки');
const names = t.rows.map(r => r['Номенклатура']);
assert.includes(names, 'Товары', 'есть Товары');
assert.includes(names, 'Услуги', 'есть Услуги');
assert.includes(names, 'БольшойСписок', 'есть БольшойСписок');
assert.ok(t.rows.every(r => r._kind === 'group'), 'все корневые — group (есть expand-стрелка)');
});
await step('expand: clickElement({expand}) раскрывает Товары — 15 элементов', async () => {
const r = await clickElement('Товары', { expand: true });
log(`clicked: ${JSON.stringify(r.clicked)}`);
assert.equal(r.clicked?.toggled, true, 'expand toggled');
const t = await readTable('Дерево');
log(`after expand: total=${t.total}`);
assert.ok(t.total >= 16, `Товары + 15 элементов (got ${t.total})`);
const tovar01 = t.rows.find(row => row['Номенклатура'] === 'Товар 01');
assert.ok(tovar01, 'Товар 01 виден внутри Товары');
assert.equal(tovar01['Цена'], '100,00', 'исходная Цена 100,00 (из справочника)');
});
await step('tree-edit: fillTableRow меняет Цену в развёрнутой группе', async () => {
// row:1 — это Товар 01 (row:0 — Товары после expand). Используем index, т.к.
// fillTableRow{row:'Товар 01'} ловит SyntaxError в JS-эвале — TODO в bug list.
const r = await fillTableRow({ Цена: 1500 }, { row: 1 });
log(`filled: ${JSON.stringify(r.filled)}`);
assert.equal(r.filled?.length, 1, '1 поле заполнено');
assert.equal(r.filled[0].field, 'Цена', 'поле Цена');
assert.equal(r.filled[0].method, 'direct', 'method=direct (in-place edit)');
assert.equal(r.filled[0].ok, true, 'ok=true');
const t = await readTable('Дерево');
const tovar01 = t.rows.find(row => row['Номенклатура'] === 'Товар 01');
assert.ok(tovar01, 'Товар 01 виден');
// 1С web использует non-breaking space ( ) как разделитель разрядов
assert.equal(tovar01['Цена'], '1 500,00', 'Цена обновилась до 1 500,00');
});
await step('choice-cell: fillTableRow задаёт ТипЗначения через форму выбора (НачалоВыбора)', async () => {
// Колонка-строка с кнопкой выбора + обработчиком НачалоВыбора → СписокТипов.ПоказатьВыборЭлемента
// («Выбрать тип»). Plain-paste тут не годится — движок открывает форму выбора и выбирает из списка.
const r = await fillTableRow({ ТипЗначения: 'Число' }, { row: 1 });
log(`filled: ${JSON.stringify(r.filled)}`);
const cell = r.filled?.find(f => f.field === 'ТипЗначения');
assert.ok(cell, 'поле ТипЗначения в результате');
assert.equal(cell.ok, true, 'ok=true');
assert.equal(cell.method, 'choice', 'method=choice (выбор из списка, не direct)');
const t = await readTable('Дерево');
const tovar01 = t.rows.find(row => row['Номенклатура'] === 'Товар 01');
assert.equal(tovar01['Тип значения'], 'Число', 'ТипЗначения = Число');
});
await step('choice-cell-negative: несуществующий тип → ok:false/not_found (форма не закрывается)', async () => {
// not_found гасит только диалог выбора типа (умный dismiss), исходная форма остаётся —
// следующие шаги (picture) это подтверждают.
const r = await fillTableRow({ ТипЗначения: 'Нетакоготипа' }, { row: 1 });
const cell = r.filled?.find(f => f.field === 'ТипЗначения');
assert.ok(cell, 'поле ТипЗначения в результате');
assert.equal(cell.ok, false, 'ok=false для несуществующего типа');
assert.equal(cell.error, 'not_found', 'error=not_found');
});
await step('picture: колонка-картинка (pic:0/\'\') + кросс-проверка чекбоксом', async () => {
const t = await readTable('Дерево');
const t15 = t.rows.find(r => r['Номенклатура'] === 'Товар 15'); // Цена 1500 > 1000 → иконка
const t02 = t.rows.find(r => r['Номенклатура'] === 'Товар 02'); // Цена 200 < 1000 → нет
assert.ok(t15 && t02, 'Товар 15 и Товар 02 видны');
assert.equal(t15['Картинка'], 'pic:0', 'Товар 15 — иконка показана (pic:0)');
assert.equal(t15['Флаг'], 'true', 'Товар 15 — флаг true');
assert.equal(t02['Картинка'], '', 'Товар 02 — иконки нет (пусто)');
assert.equal(t02['Флаг'], 'false', 'Товар 02 — флаг false');
// Инвариант: иконка есть тогда и только тогда, когда флаг true.
const items = t.rows.filter(r => (r['Номенклатура'] || '').startsWith('Товар '));
assert.ok(items.length >= 15, 'видны все 15 товаров');
assert.ok(items.every(r => (r['Картинка'] === 'pic:0') === (r['Флаг'] === 'true')),
'по всем товарам: картинка ⟺ флаг');
});
await step('picture-toggle: Selection инвертирует Картинка по двойному клику', async () => {
await clickElement({ row: { 'Номенклатура': 'Товар 02' }, column: 'Картинка' }, { dblclick: true });
const t = await readTable('Дерево');
const t02 = t.rows.find(r => r['Номенклатура'] === 'Товар 02');
assert.equal(t02['Картинка'], 'pic:0', 'после двойного клика иконка появилась');
assert.equal(t02['Флаг'], 'true', 'и флаг стал true');
});
await step('cleanup: закрыть форму', async () => {
await closeForm();
});
}