diff --git a/internal/driver/ioscompanion/transport/runner.go b/internal/driver/ioscompanion/transport/runner.go index a2093e1..be64387 100644 --- a/internal/driver/ioscompanion/transport/runner.go +++ b/internal/driver/ioscompanion/transport/runner.go @@ -282,7 +282,11 @@ func (c *runnerCompanion) Uninstall(ctx context.Context, bundleID string) error } func (c *runnerCompanion) InputText(ctx context.Context, text string) error { - _, err := c.call(ctx, "typeText", map[string]any{"text": text, "replace": true}) + return c.TypeText(ctx, text, true) +} + +func (c *runnerCompanion) TypeText(ctx context.Context, text string, replace bool) error { + _, err := c.call(ctx, "typeText", map[string]any{"text": text, "replace": replace}) return err } diff --git a/internal/driver/ioscompanion/transport/runner_test.go b/internal/driver/ioscompanion/transport/runner_test.go index 3e767a0..5788c5f 100644 --- a/internal/driver/ioscompanion/transport/runner_test.go +++ b/internal/driver/ioscompanion/transport/runner_test.go @@ -467,3 +467,22 @@ func TestResponseIDMismatchIsSentinel(t *testing.T) { t.Fatalf("id mismatch must wrap the sentinel: %v", err) } } + +func TestTypeTextAppendsWithoutReplace(t *testing.T) { + server := startFakeServer(t, nil) + companion := dialFake(t, server, "com.example.app") + typer := companion.(TextTyper) + + if err := typer.TypeText(context.Background(), "héllo 🌟", false); err != nil { + t.Fatalf("TypeText: %v", err) + } + + requests := server.recorded() + if len(requests) != 1 { + t.Fatalf("recorded %d requests, want 1", len(requests)) + } + request := requests[0] + if request.Method != "typeText" || request.Params["text"] != "héllo 🌟" || request.Params["replace"] != false { + t.Fatalf("typeText request = %+v", request) + } +} diff --git a/internal/driver/ioscompanion/transport/transport.go b/internal/driver/ioscompanion/transport/transport.go index 6a678bb..7412ebc 100644 --- a/internal/driver/ioscompanion/transport/transport.go +++ b/internal/driver/ioscompanion/transport/transport.go @@ -62,6 +62,14 @@ type TextEditor interface { PressKey(ctx context.Context, key string) error } +// TextTyper is an optional companion capability: the transport types text +// natively into whatever holds keyboard focus. Unlike TextEditor it exposes +// the replace flag, so a caller can clear the field through another channel +// and append with replace false. +type TextTyper interface { + TypeText(ctx context.Context, text string, replace bool) error +} + // ErrCompanionUnavailable marks a connection-level failure that a companion // restart can recover from. Transports wrap dropped-connection errors with it. var ErrCompanionUnavailable = errors.New("companion connection unavailable")