mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-14 01:44:57 +03:00
refactor(web-test): извлечены detect-new-form и edit-state из inline в dom/
Дедуплицированы 15 копий detect-new-form (13 в row-fill + 2 локальные
обёртки в select-value), 6 копий INPUT-focused, 4 проверки calendar/
calculator popup, 1 INPUT-focused-inside-grid.
Новое:
- dom/forms.mjs: detectNewFormScript(prev, {strict}) — объединяет broad
и strict варианты
- dom/edit-state.mjs: isInputFocusedScript({allowTextarea}),
isInputFocusedInGridScript, findOpenPopupScript
- helpers.mjs: переписан detectNewForm на dom-script; добавлены тонкие
обёртки isInputFocused, isInputFocusedInGrid, findOpenPopup
Метрики row-fill: 1235 → 1065 LOC (−170), inline page.evaluate 47 → 20.
Поведение идентично; точечный регресс зелёный (02/03/05/06/10/16).
04-selectvalue auto-history шаг — pre-existing baseline issue (state-
driven, не связан с S1, воспроизводится на HEAD).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// web-test dom v1.8 — facade re-exporting injectable DOM scripts from dom/
|
||||
// web-test dom v1.9 — facade re-exporting injectable DOM scripts from dom/
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
/**
|
||||
* Facade: re-exports DOM selector & semantic mapping script generators.
|
||||
@@ -15,8 +15,15 @@ export {
|
||||
findClickTargetScript,
|
||||
findFieldButtonScript,
|
||||
resolveFieldsScript,
|
||||
detectNewFormScript,
|
||||
} from './dom/forms.mjs';
|
||||
|
||||
export {
|
||||
isInputFocusedScript,
|
||||
isInputFocusedInGridScript,
|
||||
findOpenPopupScript,
|
||||
} from './dom/edit-state.mjs';
|
||||
|
||||
export { getFormStateScript } from './dom/form-state.mjs';
|
||||
|
||||
export {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// web-test dom/edit-state v1.0 — focus and popup detection inside the 1C web client
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
/**
|
||||
* Is the currently focused element an INPUT (optionally TEXTAREA too)?
|
||||
* Returns boolean.
|
||||
*
|
||||
* @param {object} [opts]
|
||||
* @param {boolean} [opts.allowTextarea=false] — also return true for TEXTAREA.
|
||||
*/
|
||||
export function isInputFocusedScript({ allowTextarea = false } = {}) {
|
||||
const cond = allowTextarea
|
||||
? `f.tagName === 'INPUT' || f.tagName === 'TEXTAREA'`
|
||||
: `f.tagName === 'INPUT'`;
|
||||
return `(() => {
|
||||
const f = document.activeElement;
|
||||
return !!(f && (${cond}));
|
||||
})()`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the currently focused INPUT/TEXTAREA inside a `.grid` ancestor?
|
||||
* Used to verify grid edit-mode (active cell editor).
|
||||
* Returns boolean.
|
||||
*/
|
||||
export function isInputFocusedInGridScript() {
|
||||
return `(() => {
|
||||
const f = document.activeElement;
|
||||
if (!f || (f.tagName !== 'INPUT' && f.tagName !== 'TEXTAREA')) return false;
|
||||
let n = f;
|
||||
while (n) {
|
||||
if (n.classList?.contains('grid')) return true;
|
||||
n = n.parentElement;
|
||||
}
|
||||
return false;
|
||||
})()`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a calculator (`.calculate`) or calendar (`.frameCalendar`) popup visible?
|
||||
* Returns `'calculator' | 'calendar' | null`.
|
||||
*
|
||||
* For the "popup gone" check, callers use: `!await findOpenPopup()`.
|
||||
*/
|
||||
export function findOpenPopupScript() {
|
||||
return `(() => {
|
||||
const calc = document.querySelector('.calculate');
|
||||
if (calc && calc.offsetWidth > 0) return 'calculator';
|
||||
const cal = document.querySelector('.frameCalendar');
|
||||
if (cal && cal.offsetWidth > 0) return 'calendar';
|
||||
return null;
|
||||
})()`;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test dom/forms v1.0 — form detection, content read, click-target/field-button resolution
|
||||
// web-test dom/forms v1.1 — form detection, content read, click-target/field-button resolution
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import { DETECT_FORM_FN, READ_FORM_FN } from './_shared.mjs';
|
||||
|
||||
@@ -396,3 +396,29 @@ export function resolveFieldsScript(formNum, fields) {
|
||||
return results;
|
||||
})()`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect a new form opened above `prevFormNum`. Two modes:
|
||||
* default (broad) — counts any visible `[id]` element; finds dialogs whose
|
||||
* `a.press` buttons have empty IDs. Used by selectValue / fillTableRow.
|
||||
* `{ strict: true }` — only counts visible interactive elements
|
||||
* (`input.editInput[id], a.press[id]`); used by fillReferenceField.
|
||||
*
|
||||
* Returns the highest new form number or `null`.
|
||||
*/
|
||||
export function detectNewFormScript(prevFormNum, { strict = false } = {}) {
|
||||
const selector = strict ? 'input.editInput[id], a.press[id]' : '[id]';
|
||||
const visibleCheck = strict
|
||||
? 'el.offsetWidth === 0'
|
||||
: 'el.offsetWidth === 0 && el.offsetHeight === 0';
|
||||
return `(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll(${JSON.stringify(selector)}).forEach(el => {
|
||||
if (${visibleCheck}) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${prevFormNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
})()`;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
// web-test core/helpers v1.17 — private, cross-cutting helpers used by the
|
||||
// web-test core/helpers v1.18 — 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, checkForErrors } from './errors.mjs';
|
||||
import { getFormState } from '../forms/state.mjs';
|
||||
import {
|
||||
detectNewFormScript,
|
||||
isInputFocusedScript,
|
||||
isInputFocusedInGridScript,
|
||||
findOpenPopupScript,
|
||||
} from '../../dom.mjs';
|
||||
|
||||
/**
|
||||
* page.click with the standard "intercepts pointer events" retry ladder:
|
||||
@@ -69,20 +75,33 @@ export async function findFieldInputId(formNum, fieldName) {
|
||||
* @returns {Promise<number|null>} new form number or null
|
||||
*/
|
||||
export async function detectNewForm(prevFormNum, { strict = false } = {}) {
|
||||
const selector = strict ? 'input.editInput[id], a.press[id]' : '[id]';
|
||||
const visibleCheck = strict
|
||||
? 'el.offsetWidth === 0'
|
||||
: 'el.offsetWidth === 0 && el.offsetHeight === 0';
|
||||
return page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll(${JSON.stringify(selector)}).forEach(el => {
|
||||
if (${visibleCheck}) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${prevFormNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
})()`);
|
||||
return page.evaluate(detectNewFormScript(prevFormNum, { strict }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin wrapper: is the currently focused element an INPUT (or TEXTAREA)?
|
||||
*
|
||||
* @param {object} [opts]
|
||||
* @param {boolean} [opts.allowTextarea=false]
|
||||
*/
|
||||
export async function isInputFocused({ allowTextarea = false } = {}) {
|
||||
return page.evaluate(isInputFocusedScript({ allowTextarea }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin wrapper: is the currently focused INPUT/TEXTAREA inside a `.grid`?
|
||||
* Used to verify grid edit-mode.
|
||||
*/
|
||||
export async function isInputFocusedInGrid() {
|
||||
return page.evaluate(isInputFocusedInGridScript());
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin wrapper: is calculator (`.calculate`) or calendar (`.frameCalendar`)
|
||||
* popup visible? Returns `'calculator' | 'calendar' | null`.
|
||||
*/
|
||||
export async function findOpenPopup() {
|
||||
return page.evaluate(findOpenPopupScript());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,8 @@ import { highlight, unhighlight } from '../recording/highlight.mjs';
|
||||
import {
|
||||
safeClick, findFieldInputId, readEdd,
|
||||
detectNewForm as helperDetectNewForm,
|
||||
} from '../core/helpers.mjs';
|
||||
|
||||
} from '../core/helpers.mjs';
|
||||
import { pasteText } from '../core/clipboard.mjs';
|
||||
import { getFormState } from './state.mjs';
|
||||
|
||||
/**
|
||||
@@ -757,18 +757,9 @@ export async function selectValue(fieldName, searchText, { type } = {}) {
|
||||
if (!isChecked) { await page.click(cbSel); await waitForStable(); }
|
||||
}
|
||||
|
||||
|
||||
// Helper: detect selection form (form number > formNum, strict mode)
|
||||
async function detectSelectionForm() {
|
||||
async function detectSelectionForm() {
|
||||
return page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('input.editInput[id], a.press[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
return helperDetectNewForm(formNum, { strict: true });
|
||||
}
|
||||
|
||||
// detectNewForm is hoisted at the top of selectValue (see above).
|
||||
|
||||
@@ -13,13 +13,14 @@ import { highlight, unhighlight } from '../recording/highlight.mjs';
|
||||
import {
|
||||
safeClick, findFieldInputId,
|
||||
detectNewForm as helperDetectNewForm,
|
||||
isInputFocused, isInputFocusedInGrid, findOpenPopup,
|
||||
} from '../core/helpers.mjs';
|
||||
import { clickElement } from '../core/click.mjs';
|
||||
import {
|
||||
pickFromSelectionForm, isTypeDialog, pickFromTypeDialog,
|
||||
fillReferenceField, selectValue,
|
||||
} from '../forms/select-value.mjs';
|
||||
|
||||
} from '../forms/select-value.mjs';
|
||||
import { pasteText } from '../core/clipboard.mjs';
|
||||
import { getFormState } from '../forms/state.mjs';
|
||||
|
||||
/**
|
||||
@@ -199,16 +200,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
if (firstVal0 === '') {
|
||||
await page.waitForTimeout(500);
|
||||
// Check if click opened a selection form — close it first
|
||||
// Check if click opened a selection form — close it first
|
||||
let openedForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
let openedForm = await helperDetectNewForm(formNum);
|
||||
if (openedForm !== null) {
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(500);
|
||||
@@ -216,16 +208,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
// No form opened — need to enter edit mode first (dblclick), then close any form that opens
|
||||
await page.mouse.dblclick(cellCoords.x, cellCoords.y);
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForTimeout(500);
|
||||
openedForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
openedForm = await helperDetectNewForm(formNum);
|
||||
if (openedForm !== null) {
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(500);
|
||||
@@ -279,21 +262,9 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
let directEditForm = null;
|
||||
for (let dw = 0; dw < 4; dw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
inEdit = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && f.tagName === 'INPUT';
|
||||
inEdit = await isInputFocused();
|
||||
if (inEdit) break;
|
||||
if (inEdit) break;
|
||||
directEditForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
directEditForm = await helperDetectNewForm(formNum);
|
||||
if (directEditForm !== null) break;
|
||||
}
|
||||
// Click didn't enter edit — try dblclick (works for flat grids)
|
||||
@@ -301,21 +272,9 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.mouse.dblclick(cellCoords.x, cellCoords.y);
|
||||
for (let dw = 0; dw < 4; dw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
inEdit = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && f.tagName === 'INPUT';
|
||||
inEdit = await isInputFocused();
|
||||
if (inEdit) break;
|
||||
if (inEdit) break;
|
||||
directEditForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
directEditForm = await helperDetectNewForm(formNum);
|
||||
if (directEditForm !== null) break;
|
||||
}
|
||||
}
|
||||
@@ -324,21 +283,9 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.keyboard.press('F4');
|
||||
for (let fw = 0; fw < 8; fw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
inEdit = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && f.tagName === 'INPUT';
|
||||
inEdit = await isInputFocused();
|
||||
if (inEdit) break;
|
||||
if (inEdit) break;
|
||||
directEditForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
directEditForm = await helperDetectNewForm(formNum);
|
||||
if (directEditForm !== null) break;
|
||||
}
|
||||
}
|
||||
@@ -356,16 +303,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.keyboard.press('F4');
|
||||
for (let fw = 0; fw < 8; fw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
directEditForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
directEditForm = await helperDetectNewForm(formNum);
|
||||
if (directEditForm !== null) break;
|
||||
}
|
||||
// If F4 didn't open a selection form, fall through to Tab loop
|
||||
@@ -394,16 +332,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await pickFromTypeDialog(selForm, info.type);
|
||||
await waitForStable(selForm);
|
||||
// After type selection, detect the actual selection form
|
||||
// After type selection, detect the actual selection form
|
||||
selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm === null) {
|
||||
return { field: key, error: 'no_selection_after_type', message: `Type selected but no selection form opened for "${key}"` };
|
||||
}
|
||||
@@ -490,23 +419,9 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.mouse.dblclick(nextCoords.x, nextCoords.y);
|
||||
await page.waitForTimeout(300);
|
||||
// Check if dblclick entered INPUT mode (plain text/numeric field) — before F4 which may open calculator
|
||||
// Check if dblclick entered INPUT mode (plain text/numeric field) — before F4 which may open calculator
|
||||
const inInputAfterDblclick = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
if (!f || (f.tagName !== 'INPUT' && f.tagName !== 'TEXTAREA')) return false;
|
||||
let n = f; while (n) { if (n.classList?.contains('grid')) return true; n = n.parentElement; }
|
||||
return false;
|
||||
const inInputAfterDblclick = await isInputFocusedInGrid();
|
||||
// Also check if a selection form already appeared
|
||||
// Also check if a selection form already appeared
|
||||
let selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
let selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm === null && inInputAfterDblclick) {
|
||||
// Plain text/numeric field — fill via clipboard paste
|
||||
await pasteText(info.value, { confirm: ['Control+a', 'Control+v'] });
|
||||
@@ -530,16 +445,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
if (attempt === 1) await page.keyboard.press('F4'); // F4 fallback
|
||||
for (let sw = 0; sw < 6; sw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm !== null) break;
|
||||
}
|
||||
}
|
||||
@@ -744,44 +650,20 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
let typeForm = null;
|
||||
for (let tw = 0; tw < 6; tw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
typeForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
typeForm = await helperDetectNewForm(formNum);
|
||||
if (typeForm !== null) break;
|
||||
}
|
||||
if (typeForm !== null && await isTypeDialog(typeForm)) {
|
||||
await pickFromTypeDialog(typeForm, info.type);
|
||||
await waitForStable(typeForm);
|
||||
// After type selection, check if a selection form opened (ref types)
|
||||
// After type selection, check if a selection form opened (ref types)
|
||||
const selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
const selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm === null) {
|
||||
// Primitive type — poll for calculator/calendar popup or settle on INPUT
|
||||
let hasPopup = null;
|
||||
for (let pw = 0; pw < 5; pw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
hasPopup = await page.evaluate(`(() => {
|
||||
const calc = document.querySelector('.calculate');
|
||||
if (calc && calc.offsetWidth > 0) return 'calculator';
|
||||
const cal = document.querySelector('.frameCalendar');
|
||||
if (cal && cal.offsetWidth > 0) return 'calendar';
|
||||
return null;
|
||||
hasPopup = await findOpenPopup();
|
||||
if (hasPopup) break;
|
||||
}
|
||||
if (hasPopup) {
|
||||
@@ -789,21 +671,11 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
// Poll for popup to disappear
|
||||
for (let dw = 0; dw < 4; dw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
const gone = await page.evaluate(`(() => {
|
||||
const calc = document.querySelector('.calculate');
|
||||
if (calc && calc.offsetWidth > 0) return false;
|
||||
const cal = document.querySelector('.frameCalendar');
|
||||
if (cal && cal.offsetWidth > 0) return false;
|
||||
return true;
|
||||
})()`);
|
||||
if (!(await findOpenPopup())) break;
|
||||
}
|
||||
}
|
||||
// Ensure we are in an editable INPUT for this cell
|
||||
// Ensure we are in an editable INPUT for this cell
|
||||
const inInput = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA');
|
||||
const inInput = await isInputFocused({ allowTextarea: true });
|
||||
if (!inInput) {
|
||||
const cellRect = await page.evaluate(`(() => {
|
||||
const el = document.getElementById(${JSON.stringify(cell.id)});
|
||||
@@ -816,11 +688,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
// Poll for INPUT focus
|
||||
for (let fw = 0; fw < 4; fw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
const ok = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA');
|
||||
})()`);
|
||||
if (await isInputFocused({ allowTextarea: true })) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -982,16 +850,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
if (clickedShowAll) {
|
||||
await waitForStable(formNum);
|
||||
// Check if selection form opened
|
||||
// Check if selection form opened
|
||||
const selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('input.editInput[id], a.press[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
const selForm = await helperDetectNewForm(formNum, { strict: true });
|
||||
|
||||
if (selForm !== null) {
|
||||
const pickResult = await pickFromSelectionForm(selForm, matchedKey, text, formNum);
|
||||
@@ -1037,48 +896,23 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await pickFromTypeDialog(newForm, info.type);
|
||||
await waitForStable(newForm);
|
||||
// After type selection, the actual selection form should open
|
||||
// After type selection, the actual selection form should open
|
||||
const selForm = await page.evaluate(`(() => {
|
||||
const forms = {};
|
||||
document.querySelectorAll('[id]').forEach(el => {
|
||||
if (el.offsetWidth === 0 && el.offsetHeight === 0) return;
|
||||
const m = el.id.match(/^form(\\d+)_/);
|
||||
if (m) forms[m[1]] = true;
|
||||
});
|
||||
const nums = Object.keys(forms).map(Number).filter(n => n > ${formNum});
|
||||
return nums.length > 0 ? Math.max(...nums) : null;
|
||||
const selForm = await helperDetectNewForm(formNum);
|
||||
if (selForm === null) {
|
||||
// Primitive type — poll for calculator/calendar popup or settle on INPUT
|
||||
let hasPopup = null;
|
||||
for (let pw = 0; pw < 5; pw++) {
|
||||
await page.waitForTimeout(200);
|
||||
await page.waitForTimeout(200);
|
||||
hasPopup = await page.evaluate(`(() => {
|
||||
const calc = document.querySelector('.calculate');
|
||||
if (calc && calc.offsetWidth > 0) return 'calculator';
|
||||
const cal = document.querySelector('.frameCalendar');
|
||||
if (cal && cal.offsetWidth > 0) return 'calendar';
|
||||
return null;
|
||||
hasPopup = await findOpenPopup();
|
||||
if (hasPopup) break;
|
||||
}
|
||||
if (hasPopup) {
|
||||
await page.keyboard.press('Escape');
|
||||
for (let dw = 0; dw < 4; dw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
const gone = await page.evaluate(`(() => {
|
||||
const calc = document.querySelector('.calculate');
|
||||
if (calc && calc.offsetWidth > 0) return false;
|
||||
const cal = document.querySelector('.frameCalendar');
|
||||
if (cal && cal.offsetWidth > 0) return false;
|
||||
return true;
|
||||
})()`);
|
||||
if (!(await findOpenPopup())) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const inInput = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA');
|
||||
const inInput = await isInputFocused({ allowTextarea: true });
|
||||
if (!inInput) {
|
||||
const cellRect = await page.evaluate(`(() => {
|
||||
const el = document.getElementById(${JSON.stringify(cell.id)});
|
||||
@@ -1090,11 +924,7 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
||||
await page.mouse.dblclick(cellRect.x, cellRect.y);
|
||||
for (let fw = 0; fw < 4; fw++) {
|
||||
await page.waitForTimeout(150);
|
||||
await page.waitForTimeout(150);
|
||||
const ok = await page.evaluate(`(() => {
|
||||
const f = document.activeElement;
|
||||
return f && (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA');
|
||||
})()`);
|
||||
if (await isInputFocused({ allowTextarea: true })) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user