diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 0a761c2..bb0cb6a 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -335,10 +335,18 @@ func collectLogs(ctx context.Context, drv driver.DeviceDriver, since time.Time) } func resolveCoordinates(action verifier.Action, tree *hierarchy.Tree) (int, int, bool) { - if action.X > 0 && action.Y > 0 { - return action.X, action.Y, true + // When On is empty, X/Y are authoritative (web V8 path emits coordinates + // directly from getBoundingClientRect; the runtime nullifies unresolved + // actions upstream so a non-null InputText here always has real coords, + // even at (0,0)). When On is set, prefer the tree lookup so stale coords + // don't leak from earlier ticks. + if action.On == "" { + if action.X >= 0 && action.Y >= 0 { + return action.X, action.Y, true + } + return 0, 0, false } - if tree != nil && action.On != "" { + if tree != nil { if element := tree.Find(action.On); element != nil { x, y := element.Bounds.Center() if x > 0 && y > 0 { @@ -346,6 +354,9 @@ func resolveCoordinates(action verifier.Action, tree *hierarchy.Tree) (int, int, } } } + if action.X > 0 && action.Y > 0 { + return action.X, action.Y, true + } return 0, 0, false } diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index e962ecb..2f7c54c 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -263,6 +263,37 @@ func TestApplyAction_InputTextSurfacesFocusTapError(t *testing.T) { }) } +func TestApplyAction_V8InputTextTapsAtCoordinates(t *testing.T) { + driverMock := mockdriver.New() + action := verifier.Action{Kind: verifier.ActionKindInputText, X: 50, Y: 100, Text: "alice"} + + if err := applyAction(context.Background(), driverMock, action, nil); err != nil { + t.Fatalf("apply action: %v", err) + } + actions := driverMock.Actions() + if !containsAction(actions, mockdriver.ActionTap, "") { + t.Errorf("expected focus Tap before InputText, got %v", actions) + } + if !containsAction(actions, mockdriver.ActionInputText, "") { + t.Errorf("expected InputText after focus Tap, got %v", actions) + } +} + +func TestApplyAction_V8InputTextAtOriginStillTaps(t *testing.T) { + driverMock := mockdriver.New() + // V8 emits real (0,0) coordinates for an element at viewport top-left + // (post-#15 the runtime nullifies unresolved actions, so a non-null + // InputText with (0,0) is a deliberate edge tap, not a sentinel). + action := verifier.Action{Kind: verifier.ActionKindInputText, X: 0, Y: 0, Text: "alice"} + + if err := applyAction(context.Background(), driverMock, action, nil); err != nil { + t.Fatalf("apply action: %v", err) + } + if !containsAction(driverMock.Actions(), mockdriver.ActionTap, "") { + t.Errorf("expected focus Tap at (0,0), got %v", driverMock.Actions()) + } +} + func TestRunner_ParallelFetchCallsAllDriverMethods(t *testing.T) { state := newHarness(t) state.mock.MetricsData = driver.Metrics{CPUPercent: 5.0, HeapBytes: 1024, TotalMemoryBytes: 4096}