fix(hierarchy): reach the class attribute through className

className is an accepted selector key that no producer writes: android
reports the view class, ios the element type and the chrome dump
el.className, all of them under `class`. With no alias onto that key the
selector matched NOTHING here on every platform while the web runtime
resolved it against the live DOM, so {className: "status"} named the row
and the badge on one host and no element at all on the other.

The failure is silent: the key is accepted, so no unknown-key error
fires, and a property over the element that was never found passes
having checked nothing.
This commit is contained in:
pj committed 2026-08-19 09:55:22 +05:30
1 parent 31f9c8de11
commit 5f66e5fb3e
2 files changed
+38

No files matched your search

+2
View File
@@ -275,6 +275,8 @@ var attributeAliases = map[string][]string{
"placeholderValue": {"hintText"},
// iOS AXElement raw name for class
"elementType": {"class"},
// DOM property name for class; every producer writes the attribute as class
"className": {"class"},
}
// selectorKeys is every key an object selector may use. It is the union of the
+36
View File
@@ -540,6 +540,18 @@ const iosAttrDump = `{
]
}`
const classAttrDump = `{
"attributes": {"resource-id": "com.app:id/list", "class": "android.widget.FrameLayout", "bounds": "[0,0,1080,2340]"},
"children": [
{
"attributes": {"resource-id": "com.app:id/row1", "class": "android.widget.Button", "bounds": "[0,0,1080,200]"},
"children": [],
"clickable": true,
"enabled": true
}
]
}`
func TestRawResourceIDSubstringMatch(t *testing.T) {
tree, _ := Parse(androidAttrDump)
el := tree.Find("resource-id:row1")
@@ -556,6 +568,30 @@ func TestLabelAliasMatchesAccessibilityText(t *testing.T) {
}
}
// className is a spec key no producer writes: android reports the view class,
// ios the element type and the chrome dump el.className, all under `class`. The
// key matched nothing at all here while the web runtime resolved it against the
// live DOM, and the key being accepted meant no unknown-key error said so.
func TestClassNameAliasMatchesClass(t *testing.T) {
tree, _ := Parse(classAttrDump)
el := tree.Find("className:Button")
if el == nil {
t.Fatal("expected className: to match the class attribute via alias")
}
if el.ResourceID != "com.app:id/row1" {
t.Fatalf("got %q, want row1", el.ResourceID)
}
object := tree.FindBySelector(Selector{Filters: []AttrFilter{
{Attr: "className", Value: "Button"},
}})
if object == nil {
t.Fatal("expected the object form to match the class attribute via alias")
}
if object.ResourceID != el.ResourceID {
t.Fatalf("the object form matched %q, want %q", object.ResourceID, el.ResourceID)
}
}
func TestContentDescAliasOnIOS(t *testing.T) {
tree, _ := Parse(iosAttrDump)
el := tree.Find("content-desc:Close")