feat(web-test): expose formCount, openForms, modal in getFormState; closed in closeForm

When the open-windows tab bar is hidden in 1C settings, the model had no
way to know how many forms are open or whether a form is modal. Now
getFormState returns openForms/formCount/modal derived from DOM form
elements (independent of tab bar), and closeForm compares form number
before/after Escape to return closed: true/false.

Tested on ncc (tab bar hidden) and bpdemo (tab bar visible).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-03-21 17:23:31 +03:00
co-authored by Claude Opus 4.6
parent 6f36e36166
commit f037324ee9
4 changed files with 55 additions and 13 deletions
+9 -4
View File
@@ -1,4 +1,4 @@
// web-test browser v1.4 — Playwright browser management for 1C web client
// web-test browser v1.5 — Playwright browser management for 1C web client
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
/**
* Playwright browser management for 1C web client.
@@ -1893,8 +1893,9 @@ export async function clickElement(text, { dblclick, table, toggle, expand } = {
export async function closeForm({ save } = {}) {
ensureConnected();
await dismissPendingErrors();
const beforeForm = await page.evaluate(detectFormScript());
await page.keyboard.press('Escape');
await waitForStable();
await waitForStable(beforeForm);
const state = await getFormState();
const err = await checkForErrors();
if (err?.confirmation) {
@@ -1906,15 +1907,19 @@ export async function closeForm({ save } = {}) {
const txt = (await b.textContent()).trim();
if (txt === label) {
await b.click({ force: true });
await waitForStable();
await waitForStable(beforeForm);
break;
}
}
return await getFormState();
const afterState = await getFormState();
afterState.closed = afterState.form !== beforeForm;
return afterState;
}
state.confirmation = err.confirmation;
state.hint = 'Confirmation dialog shown. Click "Да" to confirm or "Нет" to cancel';
return state;
}
state.closed = state.form !== beforeForm;
return state;
}
+24 -4
View File
@@ -1,4 +1,4 @@
// web-test dom v1.1 — DOM selectors and semantic mapping for 1C web client
// web-test dom v1.2 — DOM selectors and semantic mapping for 1C web client
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
/**
* DOM selectors and semantic mapping for 1C:Enterprise web client.
@@ -32,6 +32,21 @@ const DETECT_FORM_FN = `function detectForm() {
return candidates.reduce((best, n) => counts[n] > counts[best] ? n : best);
}`;
/** Detect all open forms + modal state. Returns { activeForm, allForms, formCount, modal }.
* Works even when the open-windows tab bar is hidden. */
const DETECT_FORMS_FN = `function detectForms() {
const counts = {};
document.querySelectorAll('input.editInput[id], a.press[id]').forEach(el => {
if (el.offsetWidth === 0) return;
const m = el.id.match(/^form(\\d+)_/);
if (m) counts[m[1]] = (counts[m[1]] || 0) + 1;
});
const nums = Object.keys(counts).map(Number);
const modal = document.getElementById('modalSurface');
const isModal = !!(modal && modal.offsetWidth > 0);
return { allForms: nums.sort((a, b) => a - b), formCount: nums.length, modal: isModal };
}`;
/** Read form state given prefix p. Returns { fields, buttons, tabs, texts, hyperlinks, table, iframes }. */
const READ_FORM_FN = `function readForm(p) {
const result = {};
@@ -586,12 +601,14 @@ export function readTableScript(formNum, { maxRows = 20, offset = 0, gridSelecto
export function getFormStateScript() {
return `(() => {
${DETECT_FORM_FN}
${DETECT_FORMS_FN}
${READ_FORM_FN}
const formNum = detectForm();
if (formNum === null) return { form: null, message: 'No form detected' };
const meta = detectForms();
if (formNum === null) return { form: null, formCount: 0, message: 'No form detected' };
const p = 'form' + formNum + '_';
const formData = readForm(p);
// Open tabs bar
// Open tabs bar (present only when tab panel is enabled in 1C settings)
const openTabs = [];
document.querySelectorAll('[id^="openedCell_cmd_"]').forEach(el => {
const text = el.innerText?.trim();
@@ -601,7 +618,10 @@ export function getFormStateScript() {
openTabs.push(entry);
});
const activeTab = openTabs.find(t => t.active)?.name || null;
return { form: formNum, activeTab, ...formData };
const result = { form: formNum, activeTab, openForms: meta.allForms, formCount: meta.formCount, ...formData };
if (meta.modal) result.modal = true;
if (openTabs.length) result.openTabs = openTabs;
return result;
})()`;
}