From 31792a8a2dc58e319bfc53da1b3a793142f8b898 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 14 Mar 2026 18:10:25 +0300 Subject: [PATCH] feat(web-test): search all visible grids for clickElement row targets Previously findClickTargetScript used querySelector (first grid only), making clickElement unable to find rows in second+ grids on multi-grid forms (e.g. system composition wizard). Now uses querySelectorAll to search all visible grids, and returns gridId so the tree node handler can locate the correct grid for expand/collapse. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/web-test/scripts/browser.mjs | 3 +- .claude/skills/web-test/scripts/dom.mjs | 54 ++++++++++++--------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index d327f534..4579cf02 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -1560,7 +1560,8 @@ export async function clickElement(text, { dblclick, table } = {}) { // Tree node: click the tree expand/collapse icon [tree="true"] for toggle const treeIconCoords = await page.evaluate(`(() => { const p = ${JSON.stringify(`form${formNum}_`)}; - const grid = document.querySelector('[id^="' + p + '"].grid'); + 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')]; diff --git a/.claude/skills/web-test/scripts/dom.mjs b/.claude/skills/web-test/scripts/dom.mjs index b99c0dc2..93ac9a61 100644 --- a/.claude/skills/web-test/scripts/dom.mjs +++ b/.claude/skills/web-test/scripts/dom.mjs @@ -685,31 +685,37 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector } } // Grid rows — fallback: search in table rows (for hierarchical/tree navigation) - const grid = document.querySelector('[id^="' + p + '"].grid'); - if (grid) { + // Search ALL visible grids (or specific grid when table parameter is set) + let grids; + if (gridSelector) { + const g = document.querySelector(gridSelector); + grids = g ? [g] : []; + } else { + grids = [...document.querySelectorAll('[id^="' + p + '"].grid')].filter(g => g.offsetWidth > 0); + } + for (const grid of grids) { const body = grid.querySelector('.gridBody'); - if (body) { - const lines = [...body.querySelectorAll('.gridLine')]; - for (const line of lines) { - const textBoxes = [...line.querySelectorAll('.gridBoxText')].filter(b => b.offsetWidth > 0); - const rowTexts = textBoxes.map(b => b.innerText?.trim() || '').filter(Boolean); - const firstCell = rowTexts[0]?.toLowerCase() || ''; - const rowText = rowTexts.join(' ').toLowerCase(); - if (firstCell === target || rowText === target || (target.length >= 4 && (firstCell.includes(target) || rowText.includes(target)))) { - const imgBox = line.querySelector('.gridBoxImg'); - 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; - let kind; - if (isGroup) kind = 'gridGroup'; - else if (isParent) kind = 'gridParent'; - else if (isTreeNode && hasChildren) kind = 'gridTreeNode'; - else kind = 'gridRow'; - const r = line.getBoundingClientRect(); - return { id: '', kind, name: rowTexts[0] || '', - x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; - } + if (!body) continue; + const lines = [...body.querySelectorAll('.gridLine')]; + for (const line of lines) { + const textBoxes = [...line.querySelectorAll('.gridBoxText')].filter(b => b.offsetWidth > 0); + const rowTexts = textBoxes.map(b => b.innerText?.trim() || '').filter(Boolean); + const firstCell = rowTexts[0]?.toLowerCase() || ''; + const rowText = rowTexts.join(' ').toLowerCase(); + if (firstCell === target || rowText === target || (target.length >= 4 && (firstCell.includes(target) || rowText.includes(target)))) { + const imgBox = line.querySelector('.gridBoxImg'); + 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; + let kind; + if (isGroup) kind = 'gridGroup'; + else if (isParent) kind = 'gridParent'; + else if (isTreeNode && hasChildren) kind = 'gridTreeNode'; + else kind = 'gridRow'; + const r = line.getBoundingClientRect(); + return { id: '', kind, name: rowTexts[0] || '', gridId: grid.id, + x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; } } }