From 55ab172ba4caed360f590d3d6088301c012f8e8c Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 14 Mar 2026 18:47:52 +0300 Subject: [PATCH] feat(web-test): clickElement on tree nodes defaults to select, toggle is explicit Previously clickElement always toggled expand/collapse on tree nodes. Now default = select (click text), and { toggle: true } = expand/collapse (click tree icon). Hint in response guides the model to use toggle when needed. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/web-test/scripts/browser.mjs | 63 ++++++++++++--------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 4579cf02..e52f8119 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -1441,7 +1441,7 @@ export async function fillFields(fields) { } /** 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 } = {}) { +export async function clickElement(text, { dblclick, table, toggle } = {}) { ensureConnected(); await dismissPendingErrors(); if (highlightMode) try { await highlight(text, { table }); await page.waitForTimeout(500); await unhighlight(); } catch {} @@ -1557,39 +1557,48 @@ export async function clickElement(text, { dblclick, table } = {}) { return state; } if (target.kind === 'gridTreeNode') { - // Tree node: click the tree expand/collapse icon [tree="true"] for toggle - const treeIconCoords = await page.evaluate(`(() => { - const p = ${JSON.stringify(`form${formNum}_`)}; - const gridSel = ${JSON.stringify(target.gridId ? '#' + target.gridId : null)}; - const grid = gridSel ? document.querySelector(gridSel) : document.querySelector('[id^="' + p + '"].grid'); - const body = grid?.querySelector('.gridBody'); - if (!body) return null; - const lines = [...body.querySelectorAll('.gridLine')]; - for (const line of lines) { - const textBoxes = [...line.querySelectorAll('.gridBoxText')].filter(b => b.offsetWidth > 0); - const text = textBoxes[0]?.innerText?.trim() || ''; - if (text.toLowerCase().replace(/ё/gi, 'е') === ${JSON.stringify(target.name.toLowerCase().replace(/ё/gi, 'е'))}) { - const treeIcon = line.querySelector('.gridBoxImg [tree="true"]'); - if (treeIcon) { - const r = treeIcon.getBoundingClientRect(); - return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + if (toggle) { + // Toggle: click the tree expand/collapse icon [tree="true"] + const treeIconCoords = await page.evaluate(`(() => { + const p = ${JSON.stringify(`form${formNum}_`)}; + const gridSel = ${JSON.stringify(target.gridId ? '#' + target.gridId : null)}; + const grid = gridSel ? document.querySelector(gridSel) : document.querySelector('[id^="' + p + '"].grid'); + const body = grid?.querySelector('.gridBody'); + if (!body) return null; + const lines = [...body.querySelectorAll('.gridLine')]; + for (const line of lines) { + const textBoxes = [...line.querySelectorAll('.gridBoxText')].filter(b => b.offsetWidth > 0); + const text = textBoxes[0]?.innerText?.trim() || ''; + if (text.toLowerCase().replace(/ё/gi, 'е') === ${JSON.stringify(target.name.toLowerCase().replace(/ё/gi, 'е'))}) { + const treeIcon = line.querySelector('.gridBoxImg [tree="true"]'); + if (treeIcon) { + const r = treeIcon.getBoundingClientRect(); + return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + } } } + return null; + })()`); + 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'); } - return null; - })()`); - 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'); + await waitForStable(formNum); + const state = await getFormState(); + state.clicked = { kind: 'gridTreeNode', name: target.name, toggled: true }; + state.hint = 'Tree node toggled. Use readTable to see updated tree.'; + return state; } + // Default: select row (click text, no expand/collapse) + await page.mouse.click(target.x, target.y); await waitForStable(formNum); const state = await getFormState(); state.clicked = { kind: 'gridTreeNode', name: target.name }; - state.hint = 'Tree node toggled. Use web_table to see updated tree.'; + state.hint = 'Row selected. Use { toggle: true } to expand/collapse.'; return state; } if (target.kind === 'gridRow') {