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>
This commit is contained in:
Nick Shirokov
2026-05-31 17:26:37 +03:00
parent 52478a6c39
commit 7c9769c644
5 changed files with 178 additions and 12 deletions
@@ -1,4 +1,4 @@
// web-test forms/select-value v1.20 — Reference & composite-type value selection: selectValue, fillReferenceField, selection/type-dialog pickers.
// web-test forms/select-value v1.21 — Reference & composite-type value selection: selectValue, fillReferenceField, selection/type-dialog pickers.
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import {
@@ -265,6 +265,20 @@ export async function pickFromTypeDialog(formNum, typeName) {
return page.evaluate(readTypeDialogVisibleRowsScript(formNum, typeNorm));
}
// Helper: dismiss the type-selection dialog (and any child "Найти") on error.
// Escape closes the dialog chain, but a blind Escape×3 cascades into the underlying
// form. So press Escape only while THIS type dialog is still present, then stop —
// leaving the source form (and cell edit mode) for the caller to handle.
async function dismissTypeDialog() {
for (let i = 0; i < 4; i++) {
const stillOpen = await page.evaluate(
`!!document.getElementById('form${formNum}_OK') || !!document.getElementById('form${formNum}_ValueList')`);
if (!stillOpen) break;
await page.keyboard.press('Escape');
await page.waitForTimeout(300);
}
}
// Step 1: Scan visible rows (fast path — no Ctrl+F needed for small lists)
const scan = await readVisibleRows();
@@ -278,7 +292,7 @@ export async function pickFromTypeDialog(formNum, typeName) {
}
if (scan.matches.length > 1) {
for (let i = 0; i < 3; i++) { await page.keyboard.press('Escape'); await page.waitForTimeout(300); }
await dismissTypeDialog();
await waitForStable();
throw new Error(`selectValue: multiple types match "${typeName}": ${scan.matches.map(m => '"' + m.text + '"').join(', ')}. Specify a more precise type name`);
}
@@ -307,7 +321,7 @@ export async function pickFromTypeDialog(formNum, typeName) {
const findFormNum = await page.evaluate(findChildFormByButtonScript(formNum, 'Find'));
if (findFormNum === null) {
await page.keyboard.press('Escape');
await dismissTypeDialog();
await waitForStable();
throw new Error('selectValue: Ctrl+F did not open "Найти" dialog in type selection');
}
@@ -320,14 +334,14 @@ export async function pickFromTypeDialog(formNum, typeName) {
const afterSearch = await readVisibleRows();
if (afterSearch.matches.length === 0) {
for (let i = 0; i < 3; i++) { await page.keyboard.press('Escape'); await page.waitForTimeout(300); }
await dismissTypeDialog();
await waitForStable();
throw new Error(`selectValue: type "${typeName}" not found in type selection dialog` +
`. Visible: ${(scan.visible || []).join(', ')}`);
}
if (afterSearch.matches.length > 1) {
for (let i = 0; i < 3; i++) { await page.keyboard.press('Escape'); await page.waitForTimeout(300); }
await dismissTypeDialog();
await waitForStable();
throw new Error(`selectValue: multiple types match "${typeName}": ${afterSearch.matches.map(m => '"' + m.text + '"').join(', ')}. Specify a more precise type name`);
}