diff --git a/internal/hierarchy/hierarchy.go b/internal/hierarchy/hierarchy.go index 4a70f60..6a7a2ad 100644 --- a/internal/hierarchy/hierarchy.go +++ b/internal/hierarchy/hierarchy.go @@ -302,7 +302,6 @@ var selectorKeys = []string{ "accessibilityText", "aria-label", "ariaLabel", - "bounds", "checked", "class", "className", diff --git a/internal/hierarchy/hierarchy_test.go b/internal/hierarchy/hierarchy_test.go index 34ace6b..02e6bd5 100644 --- a/internal/hierarchy/hierarchy_test.go +++ b/internal/hierarchy/hierarchy_test.go @@ -690,6 +690,31 @@ func TestTestTagStillReachesTheIdentifierKeys(t *testing.T) { } } +// bounds is a raw driver attribute rather than a cross-platform key: every dump +// writes the rectangle out as a string and no DOM element carries an attribute +// of that name, so the key resolved here and matched nothing on web on every +// page there is, with no unknown-key error to say so and no mapping to invent +// for it. Off the accepted list the web runtime raises that error, and the +// escape hatch for an attribute the tree carries is what keeps it resolving +// where a producer writes it. +func TestBoundsIsAReachableRawAttributeAndNotAnAcceptedKey(t *testing.T) { + if slices.Contains(SelectorKeys(), "bounds") { + t.Error("bounds names no fact a DOM carries, so it cannot be a cross-platform key") + } + tree, _ := Parse(androidAttrDump) + selector := Selector{Filters: []AttrFilter{{Attr: "bounds", Value: "[0,0,1080,200]"}}} + if unknown := tree.UnknownSelectorKeys(selector); len(unknown) != 0 { + t.Errorf("bounds is an attribute this dump carries, got unknown %v", unknown) + } + node := tree.FindBySelector(selector) + if node == nil { + t.Fatal("expected bounds to match the raw attribute the dump writes") + } + if node.ResourceID != "com.app:id/row1" { + t.Fatalf("bounds matched %q, want row1", node.ResourceID) + } +} + func TestContentDescAliasOnIOS(t *testing.T) { tree, _ := Parse(iosAttrDump) el := tree.Find("content-desc:Close") diff --git a/pkg/spec/src/web-runtime.ts b/pkg/spec/src/web-runtime.ts index a8b5764..fbd609b 100644 --- a/pkg/spec/src/web-runtime.ts +++ b/pkg/spec/src/web-runtime.ts @@ -248,7 +248,6 @@ const SELECTOR_KEYS: readonly string[] = [ "accessibilityText", "aria-label", "ariaLabel", - "bounds", "checked", "class", "className", diff --git a/pkg/spec/test/fixtures/selector-keys.json b/pkg/spec/test/fixtures/selector-keys.json index 47ebb39..30628da 100644 --- a/pkg/spec/test/fixtures/selector-keys.json +++ b/pkg/spec/test/fixtures/selector-keys.json @@ -5,7 +5,6 @@ "accessibilityText", "aria-label", "ariaLabel", - "bounds", "checked", "class", "className", @@ -41,5 +40,5 @@ "unknownKeyExample": [ "descripton" ], - "unknownKeyMessage": "selector key \"descripton\" cannot match: no element carries that attribute, and it is not one of the accepted keys: accessibilityIdentifier, accessibilityLabel, accessibilityText, aria-label, ariaLabel, bounds, checked, class, className, clickable, content-desc, contentDescription, data-testid, desc, descPrefix, editable, elementType, enabled, focused, hintText, id, idPrefix, identifier, label, package, placeholder, placeholderValue, resource-id, scrollable, secure, selected, tag, testID, testTag, text, title, value" + "unknownKeyMessage": "selector key \"descripton\" cannot match: no element carries that attribute, and it is not one of the accepted keys: accessibilityIdentifier, accessibilityLabel, accessibilityText, aria-label, ariaLabel, checked, class, className, clickable, content-desc, contentDescription, data-testid, desc, descPrefix, editable, elementType, enabled, focused, hintText, id, idPrefix, identifier, label, package, placeholder, placeholderValue, resource-id, scrollable, secure, selected, tag, testID, testTag, text, title, value" } diff --git a/pkg/spec/test/web-runtime.test.ts b/pkg/spec/test/web-runtime.test.ts index caa56c9..8ac8541 100644 --- a/pkg/spec/test/web-runtime.test.ts +++ b/pkg/spec/test/web-runtime.test.ts @@ -597,6 +597,27 @@ test("selectorFromObject accepts a raw attribute the page carries", () => { }); }); +// bounds is a raw driver attribute rather than a cross-platform key: every +// native dump writes the rectangle out as a string and no DOM element carries an +// attribute of that name, so the key resolved against the dump and matched +// nothing here on every page there is. Being on the accepted list is what kept +// that silent, and no mapping can be invented for it. Off the list it raises the +// unknown-key error here, and it still resolves wherever a producer writes it, +// through the escape hatch every other raw attribute uses. +test("bounds is a raw attribute rather than an accepted key", () => { + assert.equal(__testing__.SELECTOR_KEYS.includes("bounds"), false); + withDocumentCarrying([], () => { + assert.throws( + () => selectorFromObject({ bounds: "[0,0,120,40]" }), + (error: Error) => + error.message.includes('"bounds"') && error.message.includes("accepted keys"), + ); + }); + withDocumentCarrying(["bounds"], () => { + assert.doesNotThrow(() => selectorFromObject({ bounds: "[0,0,120,40]" })); + }); +}); + // The string form's kind space stays open on both sides: ":" is // the documented way to reach a raw driver attribute, and internal/hierarchy // resolves an unknown kind to an empty result rather than an error.