fix(spec): keep a selector out of the head subtree

the head renders nothing, so the hierarchy dump drops it and so does the
enumeration the picker walks, but a selector still resolved into it: a
whole-page findAll answered with <head> and <title> here and with
neither on the goja host, which is a divergence the moment a state
selector asks a question every element has an answer to.
This commit is contained in:
pj committed 2026-08-18 20:44:15 +05:30
1 parent 5404909e12
commit 84baf18ee9
2 files changed
+27 -1

No files matched your search

@@ -149,6 +149,22 @@ func TestSelectors_ResolveTheSameElementsAsTheWebRuntime(t *testing.T) {
object: objectSelector("secure", "false"),
want: []string{"login_email", "login_note", "login_terms"},
},
{
// The head subtree renders nothing, so the hierarchy dump drops it
// (buildTree in driver.go) and so does the enumeration the picker
// walks (targetElements in web-runtime.ts). A selector resolving
// into it names an element the goja host cannot see at all.
name: "tag naming the head element",
selector: "tag:head",
object: objectSelector("tag", "head"),
want: nil,
},
{
name: "tag naming an element inside the head",
selector: "tag:title",
object: objectSelector("tag", "title"),
want: nil,
},
{
// The root element answers a selector like any other: the string
// form scans from the root down, and the object form used to start
+11 -1
View File
@@ -426,12 +426,22 @@ function deepQueryAll(selector: string, root: ParentNode): Element[] {
// matchedElements resolves one compiled selector: the document query first, then
// the states it named, which no query can express.
//
// The head subtree is dropped here the way targetElements drops it from the
// enumeration and buildTree (internal/driver/chrome/driver.go) drops it from the
// dump: it renders nothing, so a selector reaching into it names an element the
// goja host cannot see at all. document.head is absent only from the small fake
// documents the unit tests install.
function matchedElements(root: ParentNode, compiled: CompiledSelector): Element[] {
const { css, xpath, match } = compiled;
let found: Element[] = [];
if (css) found = deepQueryAll(css, root);
else if (xpath) found = evaluateXPathAll(xpath, root as Node);
return match === undefined ? found : found.filter(match);
const head: Element | undefined = document.head;
return found.filter(
(element) =>
!(head !== undefined && head.contains(element)) && (match === undefined || match(element)),
);
}
function queryElement(