fix(hierarchy): reach a web test tag through testTag and testID

Compose for Web writes a test tag as data-testid, which is what the web
runtime resolves both names against. testTag aliased onto the three
identifier keys and not that one, and testID aliased onto nothing at
all, so a tag the web runtime found on every row of a list named no
element here and every property over it passed vacuously.
This commit is contained in:
pj committed 2026-08-19 10:07:00 +05:30
1 parent ae629d9d7d
commit 1bcbb8b7e8
2 files changed
+42 -2

No files matched your search

+5 -2
View File
@@ -274,8 +274,11 @@ var attributeAliases = map[string][]string{
// iOS identifier names // iOS identifier names
"identifier": {"resource-id", "accessibilityIdentifier"}, "identifier": {"resource-id", "accessibilityIdentifier"},
"accessibilityIdentifier": {"resource-id", "identifier"}, "accessibilityIdentifier": {"resource-id", "identifier"},
// Compose testTag surfaces as resource-id on Android, accessibilityIdentifier on iOS // Compose testTag surfaces as resource-id on Android, accessibilityIdentifier
"testTag": {"resource-id", "identifier", "accessibilityIdentifier"}, // on iOS and data-testid on web, which is the key the web runtime resolves
// both names against.
"testTag": {"resource-id", "identifier", "accessibilityIdentifier", "data-testid"},
"testID": {"data-testid"},
// iOS AXElement raw name for hintText // iOS AXElement raw name for hintText
"placeholderValue": {"hintText"}, "placeholderValue": {"hintText"},
// iOS AXElement raw name for class // iOS AXElement raw name for class
+37
View File
@@ -653,6 +653,43 @@ func TestAccessibilityLabelAliasesStillReachAccessibilityText(t *testing.T) {
} }
} }
// Compose for Web writes a test tag as data-testid, the key the web runtime
// resolves testTag and testID against. testTag reached the three identifier
// keys and not that one, and testID aliased onto nothing at all, so a tag the
// web runtime found on both rows named no element here.
func TestTestTagAliasesReachDataTestID(t *testing.T) {
tree, _ := Parse(webAttrDump)
for _, key := range []string{"testTag", "testID"} {
element := tree.Find(key + ":customer-row")
if element == nil {
t.Fatalf("expected %s: to match the data-testid attribute via alias", key)
}
if element.ResourceID != "customer_row_a1" {
t.Fatalf("%s: matched %q, want customer_row_a1", key, element.ResourceID)
}
object := tree.FindBySelector(Selector{Filters: []AttrFilter{
{Attr: key, Value: "customer-row"},
}})
if object == nil {
t.Fatalf("expected the object form of %s to match data-testid via alias", key)
}
if object.ResourceID != element.ResourceID {
t.Fatalf("the object form of %s matched %q, want %q",
key, object.ResourceID, element.ResourceID)
}
}
}
// testTag keeps reaching the identifier keys android and ios write it under.
func TestTestTagStillReachesTheIdentifierKeys(t *testing.T) {
if tree, _ := Parse(androidAttrDump); tree.Find("testTag:row1") == nil {
t.Fatal("expected testTag: to match the resource-id attribute via alias")
}
if tree, _ := Parse(iosAttrDump); tree.Find("testTag:Feed") == nil {
t.Fatal("expected testTag: to match the identifier attribute via alias")
}
}
func TestContentDescAliasOnIOS(t *testing.T) { func TestContentDescAliasOnIOS(t *testing.T) {
tree, _ := Parse(iosAttrDump) tree, _ := Parse(iosAttrDump)
el := tree.Find("content-desc:Close") el := tree.Find("content-desc:Close")