mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-15 15:25:16 +03:00
refactor(web-test): uniform ok:true/false в filled-items + контракт fillTableRow (Phase 4)
Раньше per-item shape в filled[] был heterogeneous:
- success: {field, ok: true, method, value}
- failure: {field, error, message} ← без ok!
Естественная проверка `item.ok === false` молча промахивалась (latent bug в
production-клиенте Titan C:\WS\projects\titan\tests\helpers\query.mjs:69).
И документация утверждала «все функции throw'ают на ошибке» (guide.md:352),
что для fillTableRow было неправдой.
Что изменено:
- engine/table/row-fill.mjs v1.18 → v1.19: 15 error-pushes теперь включают
ok: false. item.ok — единый дискриминатор success/failure.
- SKILL.md: fillFields раздел уточнён («throws on per-field failure — если
вернулся, всё заполнено»). fillTableRow раздел: документирует контракт
(НЕ throws на per-field), перечисляет error-коды и recovery hints
(composite_type → retry с {value, type}, column_not_found → проверить
readTable, и т.д.).
- docs/web-test-guide.md: строка 352 нюансирована (fillTableRow исключение
из «все throw'ают»); строка 296 (таблица) уточнена.
Контракт обеих функций теперь сознательно различается и явно описан:
- fillFields = fail-fast (throws на любую ошибку, удобно для fill-and-go)
- fillTableRow = partial-recovery (errors в filled[] как ok:false, модель
может retry'нуть селективно отдельную ячейку)
Бонус: query.mjs в Titan'е теперь работает корректно без правки клиентского
кода — cell.ok === false наконец-то дискриминирует error-items.
Полный регресс 19/19 зелёный.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// web-test table/row-fill v1.18 — fillTableRow — заполнение строки табличной части/списка через Tab-навигацию и попутный выбор значений.
|
||||
// web-test table/row-fill v1.19 — fillTableRow — заполнение строки табличной части/списка через Tab-навигацию и попутный выбор значений.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import {
|
||||
@@ -242,17 +242,17 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
// After type selection, detect the actual selection form
|
||||
selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm === null) {
|
||||
return { field: key, error: 'no_selection_after_type', message: `Type selected but no selection form opened for "${key}"` };
|
||||
return { field: key, ok: false, error: 'no_selection_after_type', message: `Type selected but no selection form opened for "${key}"` };
|
||||
}
|
||||
} else {
|
||||
// No type specified — close type dialog and report error
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(300);
|
||||
return { field: key, error: 'composite_type', message: `Composite type field "${key}" requires {value, type}` };
|
||||
return { field: key, ok: false, error: 'composite_type', message: `Composite type field "${key}" requires {value, type}` };
|
||||
}
|
||||
}
|
||||
const pr = await pickFromSelectionForm(selForm, key, info.value, formNum);
|
||||
return pr.ok ? { field: key, ok: true, method: 'form' } : { field: key, error: pr.error, message: pr.message };
|
||||
return pr.ok ? { field: key, ok: true, method: 'form' } : { field: key, ok: false, error: pr.error, message: pr.message };
|
||||
}
|
||||
|
||||
// First field: selection form is already open from the dblclick above
|
||||
@@ -277,7 +277,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
const nextCoords = await page.evaluate(findNextCellCoordsByKeyScript(gridSelector, row, key));
|
||||
if (!nextCoords) {
|
||||
info.filled = true;
|
||||
results.push({ field: key, error: 'column_not_found', message: `Column for "${key}" not found` });
|
||||
results.push({ field: key, ok: false, error: 'column_not_found', message: `Column for "${key}" not found` });
|
||||
continue;
|
||||
}
|
||||
// Skip if cell already contains the desired value
|
||||
@@ -319,7 +319,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
}
|
||||
if (selForm === null) {
|
||||
info.filled = true;
|
||||
results.push({ field: key, error: 'no_selection_form', message: `Dblclick on "${key}" did not open selection form` });
|
||||
results.push({ field: key, ok: false, error: 'no_selection_form', message: `Dblclick on "${key}" did not open selection form` });
|
||||
continue;
|
||||
}
|
||||
const pr = await directEditPick(selForm, key, info);
|
||||
@@ -511,7 +511,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
info.filled = true;
|
||||
results.push(pickResult.ok
|
||||
? { field: matchedKey, cell: cell.fullName, ok: true, method: 'form', type: info.type }
|
||||
: { field: matchedKey, cell: cell.fullName,
|
||||
: { field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: pickResult.error, message: pickResult.message });
|
||||
continue;
|
||||
}
|
||||
@@ -521,7 +521,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'type_dialog_failed',
|
||||
message: `Cell "${matchedKey}": F4 did not open type dialog for type "${info.type}"` });
|
||||
await page.keyboard.press('Tab');
|
||||
@@ -539,7 +539,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
if (!inputAfterPaste && text) {
|
||||
// No type specified — can't fill this composite-type cell
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'type_required',
|
||||
message: `Cell "${matchedKey}" rejected text input (composite-type). Use { value: '...', type: 'Тип' } syntax` });
|
||||
await page.keyboard.press('Tab');
|
||||
@@ -575,7 +575,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(300);
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'not_found', message: `No match for "${text}"` });
|
||||
}
|
||||
|
||||
@@ -611,16 +611,16 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
continue;
|
||||
}
|
||||
// Not found in selection form — fall through to clear + skip
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: pickResult.error, message: pickResult.message });
|
||||
} else {
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'not_found', message: `Value "${text}" not in list` });
|
||||
}
|
||||
} else {
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'not_found', message: `Value "${text}" not in list` });
|
||||
}
|
||||
|
||||
@@ -686,7 +686,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
info.filled = true;
|
||||
results.push(pickResult.ok
|
||||
? { field: matchedKey, cell: cell.fullName, ok: true, method: 'form', type: info.type }
|
||||
: { field: matchedKey, cell: cell.fullName,
|
||||
: { field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: pickResult.error, message: pickResult.message });
|
||||
continue;
|
||||
} else {
|
||||
@@ -699,7 +699,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.keyboard.press('Tab');
|
||||
await page.waitForTimeout(500);
|
||||
info.filled = true;
|
||||
results.push({ field: matchedKey, cell: cell.fullName,
|
||||
results.push({ field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: 'type_required',
|
||||
message: `Cell "${matchedKey}" opened a type selection dialog. Use { value: '...', type: 'Тип' } syntax` });
|
||||
continue;
|
||||
@@ -710,7 +710,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
info.filled = true;
|
||||
results.push(pickResult.ok
|
||||
? { field: matchedKey, cell: cell.fullName, ok: true, method: 'form' }
|
||||
: { field: matchedKey, cell: cell.fullName,
|
||||
: { field: matchedKey, cell: cell.fullName, ok: false,
|
||||
error: pickResult.error, message: pickResult.message });
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user