mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-23 05:01:01 +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('Нет')`.
|
**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
|
### 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
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
//
|
//
|
||||||
// Holds the single browser/page/recorder slot plus the multi-context registry,
|
// 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 MAX_WAIT = 10000; // max wait for stability
|
||||||
export const POLL_INTERVAL = 200; // polling interval
|
export const POLL_INTERVAL = 200; // polling interval
|
||||||
export const STABLE_CYCLES = 3; // consecutive stable cycles needed
|
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)
|
// 1C browser extension ID (stable across versions, defined by key in manifest.json)
|
||||||
export const EXT_ID = 'pbhelknnhilelbnhfpcjlcabhmfangik';
|
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
|
// 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';
|
import { detectFormScript } from '../../dom.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smart wait: poll until DOM is stable and no loading indicators are visible.
|
* 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)
|
* @param {number|null} previousFormNum — form number before the action (null = don't check)
|
||||||
*/
|
*/
|
||||||
export async function waitForStable(previousFormNum = null) {
|
export async function waitForStable(previousFormNum = null) {
|
||||||
let stableCount = 0;
|
let stableCount = 0;
|
||||||
let lastSnapshot = '';
|
let lastSnapshot = '';
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
let deadline = start + MAX_WAIT;
|
||||||
|
|
||||||
while (Date.now() - start < MAX_WAIT) {
|
while (Date.now() < deadline) {
|
||||||
await page.waitForTimeout(POLL_INTERVAL);
|
await page.waitForTimeout(POLL_INTERVAL);
|
||||||
|
|
||||||
// Check for loading indicators
|
// Check for loading indicators
|
||||||
const status = await page.evaluate(`(() => {
|
const status = await page.evaluate(`(() => {
|
||||||
const loading = document.querySelector('.loadingImage, .waitCurtain, .progressBar');
|
const loading = document.querySelector('.loadingImage, .waitCurtain, .progressBar');
|
||||||
const isLoading = loading && loading.offsetWidth > 0;
|
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;
|
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) {
|
if (status.isLoading) {
|
||||||
stableCount = 0;
|
stableCount = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user