diff --git a/.claude/skills/web-test/SKILL.md b/.claude/skills/web-test/SKILL.md index 487e16d4..d089c512 100644 --- a/.claude/skills/web-test/SKILL.md +++ b/.claude/skills/web-test/SKILL.md @@ -198,7 +198,7 @@ Sections + all open tabs. ### Actions -#### `clickElement(text, { dblclick?, table?, toggle? })` → form state +#### `clickElement(text, { dblclick?, table?, expand? })` → form state Click button, hyperlink, tab, navigation panel link, or grid row (fuzzy match). - `table` — scope button search to a specific grid's command panel (by name from `tables[]`): @@ -215,10 +215,10 @@ Click button, hyperlink, tab, navigation panel link, or grid row (fuzzy match). // r.submenu = ['Расширенный поиск', 'Настройки', ...] await clickElement('Расширенный поиск'); ``` -- **Tree nodes**: default click = **select** (highlight row). Use `{ toggle: true }` to **expand/collapse**: +- **Tree nodes**: default click = **select** (highlight row). Use `{ expand: true }` to **expand/collapse**: ```js - await clickElement('ИСУ ФХД'); // select row - await clickElement('ИСУ ФХД', { toggle: true }); // expand/collapse + await clickElement('ИСУ ФХД'); // select row + await clickElement('ИСУ ФХД', { expand: true }); // expand/collapse ``` #### `fillFields({ name: value })` → `{ filled, form }` diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index d82e9e2c..e9d8d63b 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -1,4 +1,4 @@ -// web-test browser v1.3 — Playwright browser management for 1C web client +// web-test browser v1.4 — Playwright browser management for 1C web client // Source: https://github.com/Nikolay-Shirokov/cc-1c-skills /** * Playwright browser management for 1C web client. @@ -1446,7 +1446,7 @@ export async function fillField(name, value) { } /** Click a button/hyperlink/tab on the current form. Use {dblclick: true} to double-click (open items from lists). */ -export async function clickElement(text, { dblclick, table, toggle } = {}) { +export async function clickElement(text, { dblclick, table, toggle, expand } = {}) { ensureConnected(); await dismissPendingErrors(); if (highlightMode) try { await highlight(text, { table }); await page.waitForTimeout(500); await unhighlight(); } catch {} @@ -1562,7 +1562,7 @@ export async function clickElement(text, { dblclick, table, toggle } = {}) { return state; } if (target.kind === 'gridTreeNode') { - if (toggle) { + if (expand || toggle) { // Toggle: click the tree expand/collapse icon [tree="true"] const treeIconCoords = await page.evaluate(`(() => { const p = ${JSON.stringify(`form${formNum}_`)}; @@ -1587,10 +1587,8 @@ export async function clickElement(text, { dblclick, table, toggle } = {}) { if (treeIconCoords) { await page.mouse.click(treeIconCoords.x, treeIconCoords.y); } else { - // Fallback: select row and use +/- keys - await page.mouse.click(target.x, target.y); - await page.waitForTimeout(300); - await page.keyboard.press('NumpadAdd'); + // Fallback: dblclick on row (works for trees without clickable +/- icons) + await page.mouse.dblclick(target.x, target.y); } await waitForStable(formNum); const state = await getFormState(); @@ -1603,7 +1601,7 @@ export async function clickElement(text, { dblclick, table, toggle } = {}) { await waitForStable(formNum); const state = await getFormState(); state.clicked = { kind: 'gridTreeNode', name: target.name }; - state.hint = 'Row selected. Use { toggle: true } to expand/collapse.'; + state.hint = 'Row selected. Use { expand: true } to expand/collapse.'; return state; } if (target.kind === 'gridRow') { @@ -2188,6 +2186,29 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) { // Click first (tree grids enter edit on single click; dblclick toggles expand/collapse). // Then escalate: dblclick → F4 if needed. await page.mouse.click(cellCoords.x, cellCoords.y); + + // Check if clicked cell is a checkbox (toggle-on-click, no edit mode) + const checkboxState = await page.evaluate(`(() => { + const el = document.elementFromPoint(${cellCoords.x}, ${cellCoords.y}); + const line = el?.closest('.gridLine'); + if (!line) return null; + const chk = line.querySelector('.checkbox'); + if (!chk) return null; + return { checked: chk.classList.contains('select') }; + })()`); + if (checkboxState !== null) { + // Checkbox cell — click already toggled it + const desired = ['true', 'да', '1', 'yes'].includes(String(firstVal0).toLowerCase().trim()); + if (checkboxState.checked !== desired) { + // Click toggled to wrong state — click again to undo + await page.mouse.click(cellCoords.x, cellCoords.y); + await page.waitForTimeout(300); + } + const results = [{ field: firstKey0, ok: true, method: 'toggle', value: desired }]; + await waitForStable(formNum); + return results; + } + let inEdit = false; let directEditForm = null; for (let dw = 0; dw < 4; dw++) { diff --git a/.claude/skills/web-test/scripts/dom.mjs b/.claude/skills/web-test/scripts/dom.mjs index a610c0f9..d5951f4e 100644 --- a/.claude/skills/web-test/scripts/dom.mjs +++ b/.claude/skills/web-test/scripts/dom.mjs @@ -743,7 +743,7 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector } const isGroup = imgBox?.querySelector('.gridListH') !== null; const isParent = imgBox?.querySelector('.gridListV') !== null; const isTreeNode = line.querySelector('.gridBoxTree') !== null; - const hasChildren = imgBox?.querySelector('[tree="true"]') !== null; + const hasChildren = line.querySelector('[tree="true"]') !== null; let kind; if (isGroup) kind = 'gridGroup'; else if (isParent) kind = 'gridParent';