From 139ca958f632539acc3bccc5cba5869207098a6d Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 26 Apr 2026 23:56:52 +0700 Subject: [PATCH] fix(hierarchy): populate ResourceID from accessibilityIdentifier iOS Compose surfaces testTag as accessibilityIdentifier. Previously only resource-id and identifier seeded element.ResourceID, leaving element.id empty for iOS Compose nodes and forcing specs to walk attrs to recover stable identifiers. --- internal/hierarchy/hierarchy.go | 3 +++ internal/hierarchy/hierarchy_test.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/internal/hierarchy/hierarchy.go b/internal/hierarchy/hierarchy.go index 37f4abc..f662a4d 100644 --- a/internal/hierarchy/hierarchy.go +++ b/internal/hierarchy/hierarchy.go @@ -193,6 +193,9 @@ func elementFromNode(node *treeNodeJSON) *Element { if element.ResourceID == "" { element.ResourceID = attrs["identifier"] } + if element.ResourceID == "" { + element.ResourceID = attrs["accessibilityIdentifier"] + } element.Text = attrs["text"] element.Description = attrs["content-desc"] if element.Description == "" { diff --git a/internal/hierarchy/hierarchy_test.go b/internal/hierarchy/hierarchy_test.go index 667dab1..4f6daf4 100644 --- a/internal/hierarchy/hierarchy_test.go +++ b/internal/hierarchy/hierarchy_test.go @@ -514,6 +514,20 @@ func TestResourceIDFallsBackToAccessibilityIdentifier(t *testing.T) { } } +func TestElementResourceIDPopulatesFromAccessibilityIdentifier(t *testing.T) { + input := `{ + "attributes": {"accessibilityIdentifier": "LoginEmail", "bounds": "[0,0,100,100]"}, + "children": [] + }` + tree, _ := Parse(input) + if len(tree.Elements) == 0 { + t.Fatal("no elements parsed") + } + if got := tree.Elements[0].ResourceID; got != "LoginEmail" { + t.Fatalf("ResourceID = %q, want LoginEmail (iOS Compose accessibilityIdentifier path)", got) + } +} + func TestNodeFindDoesNotReturnSiblings(t *testing.T) { tree, _ := Parse(pathDump) a2Node := tree.FindNode("id:A2")