diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 5de877ba..d52a1e4c 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -214,6 +214,20 @@ async function waitForStable(previousFormNum = null) { // Fallback: max wait reached } +/** + * Poll until a JS expression returns truthy, or timeout (ms) expires. + * Resolves early — typically within 100-300ms instead of fixed delays. + */ +async function waitForCondition(evalScript, timeout = 2000) { + const start = Date.now(); + while (Date.now() - start < timeout) { + const result = await page.evaluate(evalScript); + if (result) return result; + await page.waitForTimeout(100); + } + return null; +} + /** * Check for validation errors / diagnostics after an action. * Detects: inline balloon tooltip, messages panel, modal error dialog. @@ -565,7 +579,18 @@ async function fillReferenceField(selector, fieldName, value, formNum) { // 3. Paste text via clipboard (trusted event → triggers real 1C autocomplete) await page.evaluate(`navigator.clipboard.writeText(${JSON.stringify(text)})`); await page.keyboard.press('Control+V'); - await page.waitForTimeout(2000); + // Smart wait: poll for EDD dropdown or "not in list" cloud (max 2s) + await waitForCondition(`(() => { + const edd = document.getElementById('editDropDown'); + if (edd && edd.offsetWidth > 0) return true; + for (const el of document.querySelectorAll('div')) { + if (el.offsetWidth === 0) continue; + const s = getComputedStyle(el); + if ((s.position === 'absolute' || s.position === 'fixed') && + (parseInt(s.zIndex) || 0) >= 100 && (el.innerText || '').includes('нет в списке')) return true; + } + return false; + })()`, ACTION_WAIT); // 4. Check editDropDown for autocomplete suggestions const eddState = await page.evaluate(`(() => { @@ -1217,13 +1242,18 @@ export async function fillTableRow(fields, { tab, add, row } = {}) { // 1. Switch tab if requested if (tab) { await clickElement(tab); - await page.waitForTimeout(500); } // 2. Add new row if requested if (add) { await clickElement('Добавить'); - await page.waitForTimeout(1000); + // Smart wait: poll until an INPUT inside a grid gets focus + await waitForCondition(`(() => { + const f = document.activeElement; + if (!f || f.tagName !== 'INPUT') return false; + let n = f; while (n) { if (n.classList?.contains('grid')) return true; n = n.parentElement; } + return false; + })()`, ACTION_WAIT); } // 2b. Enter edit mode on existing row by dblclick @@ -1343,9 +1373,10 @@ export async function fillTableRow(fields, { tab, add, row } = {}) { } if (!matchedKey) { - // Skip this cell + // Skip this cell — Tab + wait for focus to move + const skipId = cell.id; await page.keyboard.press('Tab'); - await page.waitForTimeout(300); + await waitForCondition(`document.activeElement?.id !== '${skipId}'`, 500); continue; } @@ -1356,7 +1387,13 @@ export async function fillTableRow(fields, { tab, add, row } = {}) { await page.keyboard.press('Control+A'); await page.evaluate(`navigator.clipboard.writeText(${JSON.stringify(text)})`); await page.keyboard.press('Control+V'); - await page.waitForTimeout(1500); + // Smart wait: poll for EDD dropdown (reference) or stable input value + await waitForCondition(`(() => { + const edd = document.getElementById('editDropDown'); + if (edd && edd.offsetWidth > 0) return true; + const f = document.activeElement; + return f && f.tagName === 'INPUT' && f.value && f.value.length > 0; + })()`, 1500); // Check for EDD autocomplete (indicates reference field) const eddItems = await page.evaluate(`(() => { @@ -1411,15 +1448,17 @@ export async function fillTableRow(fields, { tab, add, row } = {}) { // Done? If so, don't Tab (avoids creating a new row after last cell) if ([...pending.values()].every(p => p.filled)) break; - // Tab to move to next cell + // Tab to move to next cell — wait for focus to change + const afterEddId = cell.id; await page.keyboard.press('Tab'); - await page.waitForTimeout(500); + await waitForCondition(`document.activeElement?.id !== '${afterEddId}'`, 500); continue; } - // No EDD — press Tab to commit the value + // No EDD — press Tab to commit the value, wait for focus change + const commitId = cell.id; await page.keyboard.press('Tab'); - await page.waitForTimeout(1000); + await waitForCondition(`document.activeElement?.id !== '${commitId}'`, 1000); // Check for "нет в списке" cloud popup (reference field, value not found) const notInList = await page.evaluate(`(() => {