mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-21 20:21:02 +03:00
fix(web-test): не отдавать управление, пока список ещё ищет
filterList отчитывался успехом, пока динамический список ещё выполнял поиск: readTable возвращал предыдущие строки, а следующий клик по строке попадал в чужой объект. Отличить такой результат от верного нельзя — сценарий проходит зелёным по не тому документу. Дыра оказалась не в filterList, а в общем ожидании. waitForStable следил только за .loadingImage/.waitCurtain/.progressBar и счётчиком полей ввода — ни то, ни другое не меняется при перерисовке строк списка. Замер на реальной базе: за весь поиск старый признак isLoading не сработал НИ РАЗУ. При этом 1С всё это время показывает над списком информбар «Поиск...» — тот же .stateWindowSupportSurface, который движок уже отдаёт в errors.stateText. Читать умели, ждать — нет. waitForStable теперь считает видимый маркер занятости признаком «не готово»: счётчик стабильности сбрасывается, дедлайн продлевается, пока маркер виден, но не дольше BUSY_MAX_WAIT (60 с) — реальный поиск на боевом списке идёт десятки секунд, а зависшая операция всё равно завершает ожидание. Маркеры сопоставляются ПО ТЕКСТУ (Поиск/Ожид/Searching/Please wait), а не по факту наличия информбара: тот же носитель несёт терминальные сообщения отчётов («Отчет не сформирован», «Не установлено значение параметра»), и ожидание их исчезновения вешало бы каждый отчёт до таймаута. Радиус общий, а не точечный в filterList: маркер — индикатор длительной операции вообще, тот же класс гонки достижим из clickElement и openCommand. Частное лечение для кнопок (CDP-монитор в click-form) уже есть; второй костыль сделал бы третий неизбежным. CDP-монитор как гейт не годится отдельно: он считает готовностью паузу 300 мс без запросов, а при фоновой операции с периодическим опросом такие паузы штатны. Проверено на реальной базе: текст информбара — «Поиск...», виден t=6.7..13.2 с, опрос, идентичный гейтовому, увидел занятость дважды (isLoading — ноль раз); полный регресс 29/29 до и после, file/name/status идентичны, суммарное время 879.9 → 859.0 с, отчёты не зависли. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6256c48c05
commit
9b65dccd8a
@@ -170,7 +170,7 @@ const form = await getFormState();
|
||||
|
||||
**confirmation** — if present, a Yes/No dialog is shown. Call `clickElement('Да')` or `clickElement('Нет')`.
|
||||
|
||||
**errors.stateText** — array of SpreadsheetDocument state messages (e.g. `"Не установлено значение параметра \"X\""`, `"Отчет не сформирован..."`, `"Изменились настройки..."`). Present when the report area shows an info bar instead of data.
|
||||
**errors.stateText** — array of SpreadsheetDocument state messages (e.g. `"Не установлено значение параметра \"X\""`, `"Отчет не сформирован..."`, `"Изменились настройки..."`). Present when the report area shows an info bar instead of data. The same info bar carries `"Поиск..."` while a list is still searching — actions do not return while it is up, so a filtered list never hands you the previous rows.
|
||||
|
||||
### Reading data
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test core/state v1.17 — module-level state for the web-test engine.
|
||||
// web-test core/state v1.18 — module-level state for the web-test engine.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
//
|
||||
// Holds the single browser/page/recorder slot plus the multi-context registry,
|
||||
@@ -80,6 +80,11 @@ export const ACTION_WAIT = 2000; // fallback minimum wait
|
||||
export const MAX_WAIT = 10000; // max wait for stability
|
||||
export const POLL_INTERVAL = 200; // polling interval
|
||||
export const STABLE_CYCLES = 3; // consecutive stable cycles needed
|
||||
// Ceiling for the case where 1C is VISIBLY still working (its "Поиск…" state window is up).
|
||||
// Higher than MAX_WAIT on purpose: a search on a production-sized list legitimately runs for
|
||||
// tens of seconds, and returning mid-search hands the caller the previous rows — a wrong
|
||||
// result that looks like a right one. Bounded so a wedged operation still ends the wait.
|
||||
export const BUSY_MAX_WAIT = 60000;
|
||||
|
||||
// 1C browser extension ID (stable across versions, defined by key in manifest.json)
|
||||
export const EXT_ID = 'pbhelknnhilelbnhfpcjlcabhmfangik';
|
||||
|
||||
@@ -1,30 +1,49 @@
|
||||
// web-test core/wait v1.17 — Smart wait helpers: DOM stability polling, JS-expression polling, CDP network monitor.
|
||||
// web-test core/wait v1.18 — Smart wait helpers: DOM stability polling, JS-expression polling, CDP network monitor.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import { page, MAX_WAIT, POLL_INTERVAL, STABLE_CYCLES } from './state.mjs';
|
||||
import { page, MAX_WAIT, BUSY_MAX_WAIT, POLL_INTERVAL, STABLE_CYCLES } from './state.mjs';
|
||||
import { detectFormScript } from '../../dom.mjs';
|
||||
|
||||
/**
|
||||
* Smart wait: poll until DOM is stable and no loading indicators are visible.
|
||||
* Checks: form number change, loading indicators, DOM stability.
|
||||
* Checks: form number change, loading indicators, busy state window, DOM stability.
|
||||
* @param {number|null} previousFormNum — form number before the action (null = don't check)
|
||||
*/
|
||||
export async function waitForStable(previousFormNum = null) {
|
||||
let stableCount = 0;
|
||||
let lastSnapshot = '';
|
||||
const start = Date.now();
|
||||
let deadline = start + MAX_WAIT;
|
||||
|
||||
while (Date.now() - start < MAX_WAIT) {
|
||||
while (Date.now() < deadline) {
|
||||
await page.waitForTimeout(POLL_INTERVAL);
|
||||
|
||||
// Check for loading indicators
|
||||
const status = await page.evaluate(`(() => {
|
||||
const loading = document.querySelector('.loadingImage, .waitCurtain, .progressBar');
|
||||
const isLoading = loading && loading.offsetWidth > 0;
|
||||
// While a dynamic list is still searching, 1C floats a state window over the grid
|
||||
// ("Поиск…") — and NOTHING else in the DOM says so: the old rows stay put, the element
|
||||
// counters below don't move, so the page looks perfectly stable with stale data.
|
||||
// Match busy markers by text, never "state window present": the same carrier also holds
|
||||
// TERMINAL report messages ("Отчет не сформирован", "Не установлено значение параметра"),
|
||||
// and waiting for those to disappear would hang until the timeout on every report.
|
||||
const busy = [...document.querySelectorAll('.stateWindowSupportSurface')].some(el =>
|
||||
el.offsetWidth > 0 && /^\\s*(Поиск|Ожид|Searching|Please wait)/i.test(el.innerText || ''));
|
||||
const formCount = document.querySelectorAll('input.editInput[id], a.press[id]').length;
|
||||
return { isLoading, formCount };
|
||||
return { isLoading, busy, formCount };
|
||||
})()`);
|
||||
|
||||
// A visible busy indicator outranks DOM stability — it is the only evidence we get that the
|
||||
// server is still working. Push the deadline while it lasts (bounded by BUSY_MAX_WAIT) instead
|
||||
// of reporting "stable": returning mid-search is what handed a caller the previous rows and
|
||||
// let the next click open the wrong document.
|
||||
if (status.busy) {
|
||||
deadline = Math.min(Date.now() + MAX_WAIT, start + BUSY_MAX_WAIT);
|
||||
stableCount = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status.isLoading) {
|
||||
stableCount = 0;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user