mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-20 09:30:59 +03:00
refactor(web-test): этап B.5.1 — safeClick хелпер вместо 3 копий pointer-events retry
В fillReferenceField, clickElement и DLB-ветке selectValue был один и тот же
паттерн: page.click → catch 'intercepts pointer events' → force-click →
catch снова → Escape + retry. Три копии (плюс одна с dismissPendingErrors).
core/helpers.mjs (новый): safeClick(selector, { timeout, dismissErrors }).
Экономия ~60 LOC дублей. Поведение 1-в-1 (dismissErrors:true только в
fillReferenceField — там единственное место, где исходно было).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
2cba13a8cc
commit
5b6243bbcc
@@ -142,6 +142,7 @@ import {
|
|||||||
closeModals, checkForErrors, dismissPendingErrors, fetchErrorStack,
|
closeModals, checkForErrors, dismissPendingErrors, fetchErrorStack,
|
||||||
_detectPlatformDialogs, _closePlatformDialogs,
|
_detectPlatformDialogs, _closePlatformDialogs,
|
||||||
} from './core/errors.mjs';
|
} from './core/errors.mjs';
|
||||||
|
import { safeClick } from './core/helpers.mjs';
|
||||||
// Re-export only what was publicly exported before the refactor.
|
// Re-export only what was publicly exported before the refactor.
|
||||||
// waitForStable/waitForCondition/startNetworkMonitor/closeModals/checkForErrors/
|
// waitForStable/waitForCondition/startNetworkMonitor/closeModals/checkForErrors/
|
||||||
// dismissPendingErrors are internal helpers — imported above for local use only.
|
// dismissPendingErrors are internal helpers — imported above for local use only.
|
||||||
@@ -1502,23 +1503,7 @@ async function fillReferenceField(selector, fieldName, value, formNum) {
|
|||||||
} catch { /* DLB approach failed — fall through to paste */ }
|
} catch { /* DLB approach failed — fall through to paste */ }
|
||||||
|
|
||||||
// 1. Focus (handle surface/modal overlay from previous interaction)
|
// 1. Focus (handle surface/modal overlay from previous interaction)
|
||||||
try {
|
await safeClick(selector, { dismissErrors: true });
|
||||||
await page.click(selector);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.includes('intercepts pointer events')) {
|
|
||||||
// Try force click first (no side effects), then Escape as fallback
|
|
||||||
try {
|
|
||||||
await page.click(selector, { force: true });
|
|
||||||
} catch (e2) {
|
|
||||||
if (e2.message.includes('intercepts pointer events')) {
|
|
||||||
await dismissPendingErrors();
|
|
||||||
await page.keyboard.press('Escape');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click(selector);
|
|
||||||
} else throw e2;
|
|
||||||
}
|
|
||||||
} else throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. If field already has a value, clear using Shift+F4 (native 1C mechanism).
|
// 2. If field already has a value, clear using Shift+F4 (native 1C mechanism).
|
||||||
// This is needed for reference fields — Shift+F4 properly clears the ref link.
|
// This is needed for reference fields — Shift+F4 properly clears the ref link.
|
||||||
@@ -2064,27 +2049,7 @@ export async function clickElement(text, { dblclick, table, toggle, expand, modi
|
|||||||
} else {
|
} else {
|
||||||
const selector = `[id="${target.id}"]`;
|
const selector = `[id="${target.id}"]`;
|
||||||
// Use Playwright click for proper mousedown/mouseup events
|
// Use Playwright click for proper mousedown/mouseup events
|
||||||
try {
|
await safeClick(selector, { timeout: 5000 });
|
||||||
await page.click(selector, { timeout: 5000 });
|
|
||||||
} catch (clickErr) {
|
|
||||||
if (clickErr.message.includes('intercepts pointer events')) {
|
|
||||||
// Surface overlay intercepts — try force click first (no side effects),
|
|
||||||
// then Escape + retry as fallback (Escape can trigger save dialogs on forms)
|
|
||||||
try {
|
|
||||||
await page.click(selector, { force: true, timeout: 5000 });
|
|
||||||
} catch (clickErr2) {
|
|
||||||
if (clickErr2.message.includes('intercepts pointer events')) {
|
|
||||||
await page.keyboard.press('Escape');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click(selector, { timeout: 5000 });
|
|
||||||
} else {
|
|
||||||
throw clickErr2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw clickErr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If submenu button — read popup items and return them as hints
|
// If submenu button — read popup items and return them as hints
|
||||||
@@ -2427,21 +2392,7 @@ export async function selectValue(fieldName, searchText, { type } = {}) {
|
|||||||
|
|
||||||
// 2. Click DLB (handle funcPanel / surface overlay intercept)
|
// 2. Click DLB (handle funcPanel / surface overlay intercept)
|
||||||
const dlbSel = `[id="${btn.buttonId}"]`;
|
const dlbSel = `[id="${btn.buttonId}"]`;
|
||||||
try {
|
await safeClick(dlbSel, { timeout: 5000 });
|
||||||
await page.click(dlbSel, { timeout: 5000 });
|
|
||||||
} catch (dlbErr) {
|
|
||||||
if (dlbErr.message.includes('intercepts pointer events')) {
|
|
||||||
try {
|
|
||||||
await page.click(dlbSel, { force: true, timeout: 5000 });
|
|
||||||
} catch (dlbErr2) {
|
|
||||||
if (dlbErr2.message.includes('intercepts pointer events')) {
|
|
||||||
await page.keyboard.press('Escape');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click(dlbSel, { timeout: 5000 });
|
|
||||||
} else throw dlbErr2;
|
|
||||||
}
|
|
||||||
} else throw dlbErr;
|
|
||||||
}
|
|
||||||
await page.waitForTimeout(ACTION_WAIT);
|
await page.waitForTimeout(ACTION_WAIT);
|
||||||
|
|
||||||
// 3A. Check if a dropdown popup appeared (inline quick selection)
|
// 3A. Check if a dropdown popup appeared (inline quick selection)
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// web-test core/helpers v1.16 — private, cross-cutting helpers used by the
|
||||||
|
// public action functions (clickElement/fillFields/selectValue/etc).
|
||||||
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
|
import { page } from './state.mjs';
|
||||||
|
import { dismissPendingErrors } from './errors.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* page.click with the standard "intercepts pointer events" retry ladder:
|
||||||
|
* normal → force → Escape (+ optional dismissPendingErrors) → normal.
|
||||||
|
* Mirrors the three hand-written copies in fillReferenceField, clickElement
|
||||||
|
* and the DLB branch of selectValue.
|
||||||
|
*
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {object} [opts]
|
||||||
|
* @param {number} [opts.timeout] — passed through to page.click
|
||||||
|
* @param {boolean} [opts.dismissErrors=false] — call dismissPendingErrors()
|
||||||
|
* before pressing Escape on the second retry (used in fillReferenceField).
|
||||||
|
*/
|
||||||
|
export async function safeClick(selector, { timeout, dismissErrors = false } = {}) {
|
||||||
|
const baseOpts = timeout != null ? { timeout } : {};
|
||||||
|
try {
|
||||||
|
await page.click(selector, baseOpts);
|
||||||
|
} catch (e) {
|
||||||
|
if (!e.message.includes('intercepts pointer events')) throw e;
|
||||||
|
try {
|
||||||
|
await page.click(selector, { ...baseOpts, force: true });
|
||||||
|
} catch (e2) {
|
||||||
|
if (!e2.message.includes('intercepts pointer events')) throw e2;
|
||||||
|
if (dismissErrors) await dismissPendingErrors();
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click(selector, baseOpts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user