diff --git a/internal/hierarchy/hierarchy_test.go b/internal/hierarchy/hierarchy_test.go index e016a30..a2d5635 100644 --- a/internal/hierarchy/hierarchy_test.go +++ b/internal/hierarchy/hierarchy_test.go @@ -141,6 +141,61 @@ func TestBoolFieldsFromNode(t *testing.T) { } } +func TestEditableDerivation(t *testing.T) { + cases := []struct { + name string + node string + want bool + }{ + {"driver flag", `{"attributes": {"bounds": "[0,0,10,10]"}, "editable": true}`, true}, + {"driver flag false", `{"attributes": {"bounds": "[0,0,10,10]"}, "editable": false}`, false}, + {"native EditText class", `{"attributes": {"class": "android.widget.EditText", "bounds": "[0,0,10,10]"}}`, true}, + {"hintText attr", `{"attributes": {"hintText": "Enter amount", "bounds": "[0,0,10,10]"}}`, true}, + {"plain button", `{"attributes": {"class": "android.widget.Button", "bounds": "[0,0,10,10]"}}`, false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + tree, err := Parse(tc.node) + if err != nil { + t.Fatalf("Parse: %v", err) + } + el := tree.Elements[0] + if el.Editable != tc.want { + t.Errorf("Editable = %v, want %v", el.Editable, tc.want) + } + if got := el.Attributes["editable"]; got != boolString(tc.want) { + t.Errorf("attrs[editable] = %q, want %q", got, boolString(tc.want)) + } + }) + } +} + +func boolString(b bool) string { + if b { + return "true" + } + return "false" +} + +// TestFindByEditableSelector confirms find({editable:true}) matches via the +// mirrored attrs entry, the same path clickable uses. +func TestFindByEditableSelector(t *testing.T) { + input := `{ + "attributes": {"resource-id": "root", "bounds": "[0,0,100,100]"}, + "children": [ + {"attributes": {"class": "android.widget.EditText", "bounds": "[0,0,100,40]"}, "children": []}, + {"attributes": {"class": "android.widget.Button", "bounds": "[0,40,100,80]"}, "children": []} + ] + }` + tree, _ := Parse(input) + if el := tree.Find("editable:true"); el == nil { + t.Fatal("expected to find the EditText via editable:true") + } + if matches := tree.FindAll("editable:true"); len(matches) != 1 { + t.Fatalf("editable:true matched %d elements, want 1", len(matches)) + } +} + func TestIdentifierFallback(t *testing.T) { input := `{ "attributes": {"identifier": "my-button", "bounds": "[0,0,100,100]"},