From a6229afda7a422732edfed3c9981ca5781b6d005 Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 7 Jun 2026 01:12:04 +0530 Subject: [PATCH] refactor(ioscompanion): name the empty-editable-field sentinel for what it is --- internal/driver/ioscompanion/hierarchymap.go | 27 ++++++------------- .../driver/ioscompanion/hierarchymap_test.go | 21 --------------- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/internal/driver/ioscompanion/hierarchymap.go b/internal/driver/ioscompanion/hierarchymap.go index 205ccd2..c87a822 100644 --- a/internal/driver/ioscompanion/hierarchymap.go +++ b/internal/driver/ioscompanion/hierarchymap.go @@ -14,25 +14,14 @@ type rawFrame struct { Height float64 `json:"height"` } +// emptyFieldValueSentinel is what the accessibility bridge reports as the +// AXValue of an empty editable field. It is bridge state, not app content, so +// it maps to an empty value rather than surfacing as literal field text. +const emptyFieldValueSentinel = "Invalid" + // rawElement is one entry in the flat describe-all dump returned by the // simulator companion. Only the fields the mapper consumes are declared; // unknown fields are ignored. -// unresolvedValueSentinel is what the accessibility bridge reports for a value -// it cannot resolve yet (typically during app cold start). It is bridge state, -// not app content. -const unresolvedValueSentinel = "Invalid" - -// hasUnresolvedValues reports whether any element in the flat dump still -// carries the bridge's unresolved-value sentinel. -func hasUnresolvedValues(dump []byte) bool { - for _, element := range decodeDump(dump) { - if stringValue(element.AXValue) == unresolvedValueSentinel { - return true - } - } - return false -} - type rawElement struct { Frame rawFrame `json:"frame"` AXUniqueID *string `json:"AXUniqueId"` @@ -108,9 +97,9 @@ func mapElement(element *rawElement) (treeNode, bool) { } value := stringValue(element.AXValue) - if value == unresolvedValueSentinel { - // Bridge state, not app content: surfacing it would show phantom - // field values in snapshots taken while the bridge catches up. + if value == emptyFieldValueSentinel { + // An empty editable field reads as this sentinel through the bridge; + // it is not app content, so treat the field as empty. value = "" } label := stringValue(element.AXLabel) diff --git a/internal/driver/ioscompanion/hierarchymap_test.go b/internal/driver/ioscompanion/hierarchymap_test.go index e6268b5..6ecc6d8 100644 --- a/internal/driver/ioscompanion/hierarchymap_test.go +++ b/internal/driver/ioscompanion/hierarchymap_test.go @@ -224,27 +224,6 @@ func TestRootIsFlatWithAllChildren(t *testing.T) { } } -func TestHasUnresolvedValues(t *testing.T) { - cases := []struct { - name string - dump string - want bool - }{ - {name: "sentinel value", dump: `[{"type":"TextField","AXValue":"Invalid","frame":{"x":0,"y":0,"width":1,"height":1}}]`, want: true}, - {name: "clean values", dump: `[{"type":"TextField","AXValue":"hello","frame":{"x":0,"y":0,"width":1,"height":1}}]`, want: false}, - {name: "empty value", dump: `[{"type":"TextField","AXValue":"","frame":{"x":0,"y":0,"width":1,"height":1}}]`, want: false}, - {name: "empty dump", dump: `[]`, want: false}, - {name: "malformed dump", dump: `nope`, want: false}, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := hasUnresolvedValues([]byte(c.dump)); got != c.want { - t.Fatalf("got %v, want %v", got, c.want) - } - }) - } -} - func TestMapHierarchySentinelValueMapsAsEmpty(t *testing.T) { dump := `[{"type":"TextField","AXUniqueId":"F","AXLabel":"Email","AXValue":"Invalid","frame":{"x":0,"y":0,"width":10,"height":10},"enabled":true}]` mapped, err := MapHierarchy([]byte(dump), 100, 100)