mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-06 19:50:20 +03:00
fix(web-test): fillTableRow enum support + clickElement tab ambiguity
Three fixes: 1. fillTableRow: match cells by column header text (headerText fallback) when INPUT id-based fuzzy match fails due to metadata typos 2. fillTableRow: EDD filter preserves standalone enum values like "Создать" by only filtering "Создать элемент/группу/:" patterns (was: startsWith) 3. clickElement: coordinate-based click for tabs without ID, avoiding global [data-content] selector that picks invisible duplicates from background forms Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1da43109fc
commit
d9e7d9c107
@@ -1619,31 +1619,32 @@ export async function clickElement(text, { dblclick, table, toggle, expand } = {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build selector: tabs without ID use [data-content], others use [id]
|
// Tabs without ID — use coordinate click to avoid global [data-content] ambiguity
|
||||||
const selector = (target.kind === 'tab' && !target.id)
|
if (target.kind === 'tab' && !target.id && target.x && target.y) {
|
||||||
? `[data-content="${target.name}"]`
|
await page.mouse.click(target.x, target.y);
|
||||||
: `[id="${target.id}"]`;
|
} else {
|
||||||
|
const selector = `[id="${target.id}"]`;
|
||||||
// Use Playwright click for proper mousedown/mouseup events
|
// Use Playwright click for proper mousedown/mouseup events
|
||||||
try {
|
try {
|
||||||
await page.click(selector, { timeout: 5000 });
|
await page.click(selector, { timeout: 5000 });
|
||||||
} catch (clickErr) {
|
} catch (clickErr) {
|
||||||
if (clickErr.message.includes('intercepts pointer events')) {
|
if (clickErr.message.includes('intercepts pointer events')) {
|
||||||
// Surface overlay intercepts — try force click first (no side effects),
|
// Surface overlay intercepts — try force click first (no side effects),
|
||||||
// then Escape + retry as fallback (Escape can trigger save dialogs on forms)
|
// then Escape + retry as fallback (Escape can trigger save dialogs on forms)
|
||||||
try {
|
try {
|
||||||
await page.click(selector, { force: true, timeout: 5000 });
|
await page.click(selector, { force: true, timeout: 5000 });
|
||||||
} catch (clickErr2) {
|
} catch (clickErr2) {
|
||||||
if (clickErr2.message.includes('intercepts pointer events')) {
|
if (clickErr2.message.includes('intercepts pointer events')) {
|
||||||
await page.keyboard.press('Escape');
|
await page.keyboard.press('Escape');
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.click(selector, { timeout: 5000 });
|
await page.click(selector, { timeout: 5000 });
|
||||||
} else {
|
} else {
|
||||||
throw clickErr2;
|
throw clickErr2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw clickErr;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw clickErr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2526,10 +2527,29 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
|||||||
if (!f) return { tag: 'none' };
|
if (!f) return { tag: 'none' };
|
||||||
if (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA') {
|
if (f.tagName === 'INPUT' || f.tagName === 'TEXTAREA') {
|
||||||
const inGrid = (() => { let n = f; while (n) { if (n.classList?.contains('grid') || n.classList?.contains('gridContent')) return true; n = n.parentElement; } return false; })();
|
const inGrid = (() => { let n = f; while (n) { if (n.classList?.contains('grid') || n.classList?.contains('gridContent')) return true; n = n.parentElement; } return false; })();
|
||||||
if (inGrid) return {
|
if (inGrid) {
|
||||||
tag: 'INPUT', id: f.id,
|
let headerText = '';
|
||||||
fullName: f.id.replace(/^form\\d+_/, '').replace(/_i\\d+$/, '')
|
let grid = f; while (grid && !grid.classList?.contains('grid')) grid = grid.parentElement;
|
||||||
};
|
if (grid) {
|
||||||
|
const fr = f.getBoundingClientRect();
|
||||||
|
const head = grid.querySelector('.gridHead');
|
||||||
|
const hl = head?.querySelector('.gridLine') || head;
|
||||||
|
if (hl) for (const h of hl.children) {
|
||||||
|
if (h.offsetWidth === 0) continue;
|
||||||
|
const hr = h.getBoundingClientRect();
|
||||||
|
if (fr.x >= hr.x && fr.x < hr.x + hr.width) {
|
||||||
|
const t = h.querySelector('.gridBoxText');
|
||||||
|
headerText = (t || h).innerText?.trim() || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tag: 'INPUT', id: f.id,
|
||||||
|
fullName: f.id.replace(/^form\\d+_/, '').replace(/_i\\d+$/, ''),
|
||||||
|
headerText
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return { tag: f.tagName || 'none' };
|
return { tag: f.tagName || 'none' };
|
||||||
})()`);
|
})()`);
|
||||||
@@ -2578,6 +2598,19 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback: match by column header text (handles metadata typos in cell id)
|
||||||
|
if (!matchedKey && cell.headerText) {
|
||||||
|
const htLower = cell.headerText.toLowerCase();
|
||||||
|
for (const [key, info] of pending) {
|
||||||
|
if (info.filled) continue;
|
||||||
|
const kl = key.toLowerCase();
|
||||||
|
if (htLower === kl || htLower.endsWith(kl) || htLower.includes(kl)) {
|
||||||
|
matchedKey = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!matchedKey) {
|
if (!matchedKey) {
|
||||||
// Skip this cell
|
// Skip this cell
|
||||||
await page.keyboard.press('Tab');
|
await page.keyboard.press('Tab');
|
||||||
@@ -2739,7 +2772,9 @@ export async function fillTableRow(fields, { tab, add, row, table } = {}) {
|
|||||||
|
|
||||||
if (eddItems && eddItems.length > 0) {
|
if (eddItems && eddItems.length > 0) {
|
||||||
// Reference field with autocomplete — click best match
|
// Reference field with autocomplete — click best match
|
||||||
const realItems = eddItems.filter(i => !i.startsWith('Создать'));
|
// Filter out reference field "create" actions (Создать элемент, Создать группу, Создать: ...)
|
||||||
|
// but keep standalone enum values like "Создать" (no space/colon after)
|
||||||
|
const realItems = eddItems.filter(i => !/^Создать[\s:]/.test(i));
|
||||||
|
|
||||||
if (realItems.length > 0) {
|
if (realItems.length > 0) {
|
||||||
const tgt = normYo(text.toLowerCase());
|
const tgt = normYo(text.toLowerCase());
|
||||||
|
|||||||
@@ -686,7 +686,9 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}).forEach(el => {
|
}).forEach(el => {
|
||||||
items.push({ id: el.id, name: el.dataset.content, label: '', kind: 'tab' });
|
const r = el.getBoundingClientRect();
|
||||||
|
items.push({ id: el.id, name: el.dataset.content, label: '', kind: 'tab',
|
||||||
|
x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Navigation panel items (FormNavigationPanel) — in parent page{N}
|
// Navigation panel items (FormNavigationPanel) — in parent page{N}
|
||||||
@@ -724,7 +726,7 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
|||||||
if (!cf) cf = containerItems.find(i => i.label && i.label.toLowerCase() === target);
|
if (!cf) cf = containerItems.find(i => i.label && i.label.toLowerCase() === target);
|
||||||
if (!cf && target.length >= 4) cf = containerItems.find(i => i.name.toLowerCase().includes(target));
|
if (!cf && target.length >= 4) cf = containerItems.find(i => i.name.toLowerCase().includes(target));
|
||||||
if (!cf && target.length >= 4) cf = containerItems.find(i => i.label && i.label.toLowerCase().includes(target));
|
if (!cf && target.length >= 4) cf = containerItems.find(i => i.label && i.label.toLowerCase().includes(target));
|
||||||
if (cf) return { id: cf.id, kind: cf.kind, name: cf.name };
|
if (cf) { const res = { id: cf.id, kind: cf.kind, name: cf.name }; if (cf.x != null) { res.x = cf.x; res.y = cf.y; } return res; }
|
||||||
// Fallback: filter by gridName id-prefix (e.g. ИсходящиеКоманднаяПанель_Добавить)
|
// Fallback: filter by gridName id-prefix (e.g. ИсходящиеКоманднаяПанель_Добавить)
|
||||||
const gridName = gridEl.id ? gridEl.id.replace(p, '') : '';
|
const gridName = gridEl.id ? gridEl.id.replace(p, '') : '';
|
||||||
if (gridName) {
|
if (gridName) {
|
||||||
@@ -732,7 +734,7 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
|||||||
let pf = prefixItems.find(i => i.name.toLowerCase() === target);
|
let pf = prefixItems.find(i => i.name.toLowerCase() === target);
|
||||||
if (!pf && target.length >= 4) pf = prefixItems.find(i => i.label && i.label.toLowerCase().includes(target));
|
if (!pf && target.length >= 4) pf = prefixItems.find(i => i.label && i.label.toLowerCase().includes(target));
|
||||||
if (!pf && target.length >= 4) pf = prefixItems.find(i => i.name.toLowerCase().includes(target));
|
if (!pf && target.length >= 4) pf = prefixItems.find(i => i.name.toLowerCase().includes(target));
|
||||||
if (pf) return { id: pf.id, kind: pf.kind, name: pf.name };
|
if (pf) { const res = { id: pf.id, kind: pf.kind, name: pf.name }; if (pf.x != null) { res.x = pf.x; res.y = pf.y; } return res; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fall through to unscoped search
|
// Fall through to unscoped search
|
||||||
@@ -749,7 +751,9 @@ export function findClickTargetScript(formNum, text, { tableName, gridSelector }
|
|||||||
if (!found && target.length >= 4) found = items.find(i => i.label && i.label.toLowerCase().includes(target));
|
if (!found && target.length >= 4) found = items.find(i => i.label && i.label.toLowerCase().includes(target));
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
return { id: found.id, kind: found.kind, name: found.name };
|
const res = { id: found.id, kind: found.kind, name: found.name };
|
||||||
|
if (found.x != null) { res.x = found.x; res.y = found.y; }
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid rows — fallback: search in table rows (for hierarchical/tree navigation)
|
// Grid rows — fallback: search in table rows (for hierarchical/tree navigation)
|
||||||
|
|||||||
Reference in New Issue
Block a user