From a15e7a14f927441ae90c97d64365dff12e549a7b Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 7 Jun 2026 00:28:40 +0530 Subject: [PATCH] test(ioscompanion): cover paste threshold, retry flow, and sentinel detection --- .../driver/ioscompanion/hierarchymap_test.go | 21 ++++++++++++++++ internal/driver/ioscompanion/input_test.go | 25 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/internal/driver/ioscompanion/hierarchymap_test.go b/internal/driver/ioscompanion/hierarchymap_test.go index e633669..f87409d 100644 --- a/internal/driver/ioscompanion/hierarchymap_test.go +++ b/internal/driver/ioscompanion/hierarchymap_test.go @@ -222,3 +222,24 @@ func TestRootIsFlatWithAllChildren(t *testing.T) { t.Fatalf("root bounds = %+v, want %+v", tree.Root.Bounds, wantRoot) } } + +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) + } + }) + } +} diff --git a/internal/driver/ioscompanion/input_test.go b/internal/driver/ioscompanion/input_test.go index 86fa0ee..02d4035 100644 --- a/internal/driver/ioscompanion/input_test.go +++ b/internal/driver/ioscompanion/input_test.go @@ -285,7 +285,9 @@ func TestInputTextPasteLandsImmediately(t *testing.T) { func TestInputTextPasteDismissesDialogThenLands(t *testing.T) { dialog := string(loadDialogDump(t)) landed := `[{"type":"TextField","AXUniqueId":"TxnNoteField","AXValue":"Café ☕ 😀"}]` - fake := &fakeRunner{dumps: [][]byte{[]byte(dialog), []byte(landed)}} + // Quick post-chord check and post-sleep check both see the dialog, the + // retried chord's quick check sees the landed value. + fake := &fakeRunner{dumps: [][]byte{[]byte(dialog), []byte(dialog), []byte(landed)}} field := fieldTarget{identifier: "TxnNoteField", centerX: 195, centerY: 222} if err := inputText(context.Background(), fake, "Café ☕ 😀", field); err != nil { t.Fatalf("inputText: %v", err) @@ -361,3 +363,24 @@ func TestWarmUpPasteNoDialog(t *testing.T) { t.Fatalf("expected only the paste chord, got %d streams", len(fake.hidStreams)) } } + +func TestUsesPasteboardThreshold(t *testing.T) { + cases := []struct { + text string + want bool + }{ + {"", false}, + {"a", false}, + {"abc", false}, + {"abcd", true}, + {"-1", false}, + {"%s%n", true}, + {"😀", true}, + {"Café", true}, + } + for _, c := range cases { + if got := usesPasteboard(c.text); got != c.want { + t.Errorf("usesPasteboard(%q) = %v, want %v", c.text, got, c.want) + } + } +}