From 09b2084672aa9d5eb1245c9b20e2909e8fae8612 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 25 May 2026 22:34:21 +0300 Subject: [PATCH] =?UTF-8?q?refactor(web-test):=20=D1=8D=D1=82=D0=B0=D0=BF?= =?UTF-8?q?=20B.5.3=20=E2=80=94=20detectNewForm=20=D1=85=D0=B5=D0=BB=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=20(3=20=D0=BA=D0=BE=D0=BF=D0=B8=D0=B8=20=E2=86=92?= =?UTF-8?q?=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В fillReferenceField, selectValue и fillTableRow была одна и та же логика: сканировать DOM на наличие элемента с id="form{N}_*" где N > prevFormNum. Две вариации: strict (только visible interactive — input.editInput/a.press) и broad (любой [id], учитывает type-dialogs с пустыми button-id). core/helpers.mjs: detectNewForm(prevFormNum, { strict }) → number|null. Внутри функций оставлены тонкие локальные обёртки (для совместимости с уже использующейся сигнатурой без аргументов) — будут убраны на C.8/D. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/web-test/scripts/browser.mjs | 49 +++++-------------- .../skills/web-test/scripts/core/helpers.mjs | 30 ++++++++++++ 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 625085e1..c48e7fda 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -142,7 +142,10 @@ import { closeModals, checkForErrors, dismissPendingErrors, fetchErrorStack, _detectPlatformDialogs, _closePlatformDialogs, } from './core/errors.mjs'; -import { safeClick, findFieldInputId } from './core/helpers.mjs'; +import { + safeClick, findFieldInputId, + detectNewForm as helperDetectNewForm, +} from './core/helpers.mjs'; // Re-export only what was publicly exported before the refactor. // waitForStable/waitForCondition/startNetworkMonitor/closeModals/checkForErrors/ // dismissPendingErrors are internal helpers — imported above for local use only. @@ -1408,19 +1411,9 @@ async function fillReferenceField(selector, fieldName, value, formNum) { const text = String(value); const escapedSel = selector.replace(/'/g, "\\'"); - // Helper: detect new forms opened above the current one - async function detectNewForm() { - return page.evaluate(`(() => { - const forms = {}; - document.querySelectorAll('input.editInput[id], a.press[id]').forEach(el => { - if (el.offsetWidth === 0) return; - const m = el.id.match(/^form(\\d+)_/); - if (m) forms[m[1]] = true; - }); - const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum}); - return nums.length > 0 ? Math.max(...nums) : null; - })()`); - } + // Helper: detect new forms opened above the current one (strict — interactive + // elements only; fillReferenceField-specific) + const detectNewForm = () => helperDetectNewForm(formNum, { strict: true }); // Helper: clear the field using Shift+F4 (native 1C mechanism) async function clearField() { @@ -2290,20 +2283,9 @@ export async function selectValue(fieldName, searchText, { type } = {}) { })()`); } - // Helper: detect any new form (broader than detectSelectionForm — also finds type dialogs - // whose a.press buttons have empty IDs). Looks for any visible element with id="form{N}_*". - async function detectNewForm() { - return page.evaluate(`(() => { - const forms = {}; - document.querySelectorAll('[id]').forEach(el => { - if (el.offsetWidth === 0 && el.offsetHeight === 0) return; - const m = el.id.match(/^form(\\d+)_/); - if (m) forms[m[1]] = true; - }); - const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum}); - return nums.length > 0 ? Math.max(...nums) : null; - })()`); - } + // Helper: detect any new form (broad — finds type dialogs whose a.press + // buttons have empty IDs). Looks for any visible element with id="form{N}_*". + const detectNewForm = () => helperDetectNewForm(formNum); // Helper: open selection form and pick value async function openFormAndPick() { @@ -3496,16 +3478,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) { } // Check for a new form (broad detection — also catches type dialogs whose buttons lack IDs) - const newForm = await page.evaluate(`(() => { - const forms = {}; - document.querySelectorAll('[id]').forEach(el => { - if (el.offsetWidth === 0 && el.offsetHeight === 0) return; - const m = el.id.match(/^form(\\d+)_/); - if (m) forms[m[1]] = true; - }); - const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum}); - return nums.length > 0 ? Math.max(...nums) : null; - })()`); + const newForm = await helperDetectNewForm(formNum); if (newForm !== null) { if (await isTypeDialog(newForm)) { diff --git a/.claude/skills/web-test/scripts/core/helpers.mjs b/.claude/skills/web-test/scripts/core/helpers.mjs index 7f1ecdc3..a80cc6c7 100644 --- a/.claude/skills/web-test/scripts/core/helpers.mjs +++ b/.claude/skills/web-test/scripts/core/helpers.mjs @@ -53,3 +53,33 @@ export async function findFieldInputId(formNum, fieldName) { return el ? el.id : null; })()`); } + +/** + * Detect a new form opened above the given `prevFormNum`. Two modes: + * `{ strict: true }` — only counts visible interactive elements + * (`input.editInput[id], a.press[id]`); used by fillReferenceField. + * default (broad) — any element with `id^=form{N}_` that's visible + * in either dimension; also finds type-dialogs whose a.press buttons + * have empty IDs. Used by selectValue / fillTableRow. + * + * @param {number} prevFormNum + * @param {object} [opts] + * @param {boolean} [opts.strict=false] + * @returns {Promise} new form number or null + */ +export async function detectNewForm(prevFormNum, { strict = false } = {}) { + const selector = strict ? 'input.editInput[id], a.press[id]' : '[id]'; + const visibleCheck = strict + ? 'el.offsetWidth === 0' + : 'el.offsetWidth === 0 && el.offsetHeight === 0'; + return page.evaluate(`(() => { + const forms = {}; + document.querySelectorAll(${JSON.stringify(selector)}).forEach(el => { + if (${visibleCheck}) return; + const m = el.id.match(/^form(\\d+)_/); + if (m) forms[m[1]] = true; + }); + const nums = Object.keys(forms).map(Number).filter(n => n > ${prevFormNum}); + return nums.length > 0 ? Math.max(...nums) : null; + })()`); +}