Fix snapshot evaluate: scope queries to container element, handle Page fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 14:42:41 -08:00
parent aeb41b42c2
commit 23eb5284fa

View File

@@ -329,14 +329,10 @@ Answer:`;
* Returns a plain JSON object describing every field, avoiding per-element CDP round-trips.
*/
async _snapshotFields(container) {
return await container.evaluate((extractLabelSrc, checkRequiredSrc, normalizeLegendSrc) => {
// Reconstruct functions inside browser context
const extractLabel = new Function('node', `
${extractLabelSrc}
return extractLabel(node);
`);
// Can't pass functions directly — inline the logic instead
// We'll inline extractLabel and checkRequired logic directly
return await container.evaluate((rootOrUndefined) => {
// When container is an ElementHandle, root is the element.
// When container is a Page, root is undefined — use document.
const root = rootOrUndefined || document;
function _extractLabel(node) {
const id = node.id;
@@ -419,7 +415,7 @@ Answer:`;
};
// Resume radios
const resumeInputs = document.querySelectorAll('input[type="radio"][aria-label*="resume"], input[type="radio"][aria-label*="Resume"]');
const resumeInputs = root.querySelectorAll('input[type="radio"][aria-label*="resume"], input[type="radio"][aria-label*="Resume"]');
let resumeChecked = false;
resumeInputs.forEach((r, i) => {
if (r.checked) resumeChecked = true;
@@ -428,10 +424,10 @@ Answer:`;
result.resumeChecked = resumeChecked;
// File input
result.hasFileInput = !!document.querySelector('input[type="file"]');
result.hasFileInput = !!root.querySelector('input[type="file"]');
// Text / number / url / email / tel inputs
const inputEls = document.querySelectorAll('input[type="text"], input[type="number"], input[type="url"], input[type="email"], input[type="tel"]');
const inputEls = root.querySelectorAll('input[type="text"], input[type="number"], input[type="url"], input[type="email"], input[type="tel"]');
inputEls.forEach((inp, i) => {
if (!isVisible(inp)) return;
const label = _extractLabel(inp);
@@ -442,7 +438,7 @@ Answer:`;
});
// Textareas
const taEls = document.querySelectorAll('textarea');
const taEls = root.querySelectorAll('textarea');
taEls.forEach((ta, i) => {
if (!isVisible(ta)) return;
const label = _extractLabel(ta);
@@ -452,7 +448,7 @@ Answer:`;
});
// Fieldsets
const fsEls = document.querySelectorAll('fieldset');
const fsEls = root.querySelectorAll('fieldset');
fsEls.forEach((fs, i) => {
const legend = fs.querySelector('legend');
if (!legend) return;
@@ -489,7 +485,7 @@ Answer:`;
});
// Selects (standalone, not inside fieldsets we already handle)
const selEls = document.querySelectorAll('select');
const selEls = root.querySelectorAll('select');
selEls.forEach((sel, i) => {
if (!isVisible(sel)) return;
const label = _extractLabel(sel);
@@ -507,7 +503,7 @@ Answer:`;
});
// Checkboxes (standalone)
const cbEls = document.querySelectorAll('input[type="checkbox"]');
const cbEls = root.querySelectorAll('input[type="checkbox"]');
cbEls.forEach((cb, i) => {
if (!isVisible(cb)) return;
// Skip if inside a fieldset with a legend (handled in fieldsets section)