refactor(web-test): replace fixed delays with smart waitForCondition polls

Add waitForCondition() utility — polls JS expression every 100ms with
timeout fallback. Replace key fixed delays:
- fillReferenceField paste: 2000ms → poll for EDD/cloud (max 2s)
- fillTableRow add: 1000ms → poll for grid INPUT focus (max 2s)
- fillTableRow cell paste: 1500ms → poll for EDD/value (max 1.5s)
- fillTableRow Tab skip/commit: 300-1000ms → poll for focus change

Benchmark: 31.8s vs 32.0s baseline — no measurable speedup because
1C backend response time (~1.5-2s for autocomplete) is the real
bottleneck, not our delay calibration. Code is correct and will
benefit from faster backends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-02-27 20:05:42 +03:00
parent 00ec14aed3
commit ae0c3e3278
+49 -10
View File
@@ -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(`(() => {