Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func init() {
ioKeyboardHideCmd.Flags().StringVar(&deviceId, "device", "", "ID of the device to hide keyboard on")
ioPinchCmd.Flags().StringVar(&deviceId, "device", "", "ID of the device to pinch on")
ioPinchCmd.Flags().StringVar(&pinchDirection, "direction", "", "\"in\" to zoom out or \"out\" to zoom in (required)")
ioPinchCmd.Flags().IntVar(&pinchDistance, "distance", 200, "pixels each finger travels")
ioPinchCmd.Flags().IntVar(&pinchDistance, "distance", 0, "pixels each finger travels (0 = auto, scaled to the screen)")
ioPinchCmd.Flags().IntVar(&pinchDuration, "duration", 300, "duration of the finger movement in milliseconds")
cobra.CheckErr(ioPinchCmd.MarkFlagRequired("direction"))
}
22 changes: 20 additions & 2 deletions commands/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,13 @@ const (
PinchDirectionIn = "in"
PinchDirectionOut = "out"

defaultPinchDistance = 200
defaultPinchDurationMs = 300

// minPinchDistance is the smallest travel an auto-scaled pinch uses, so a
// pinch centered close to the left edge still moves a little rather than
// collapsing to nothing.
minPinchDistance = 20

// pinchStartGap is how far from the center each finger touches down (or
// lifts, for "in"), so the two fingers never share a point.
pinchStartGap = 30
Expand All @@ -84,6 +88,20 @@ const (
pinchPressHoldMs = 50
)

// autoPinchDistance picks a travel that keeps the inner finger on-screen when no
// distance is given. The binding constraint is the left edge (far = pinchStartGap
// + distance must not exceed x), so a fixed default like 200 overflowed a pinch
// centered at x≈200 on a narrow phone. Deriving it from x scales with the screen
// without needing the screen size (which mobilecli only knows in the natural
// orientation), and leaves the far edges to the device like the rest of pinch.
func autoPinchDistance(x int) int {
d := (x - pinchStartGap) * 4 / 5
if d < minPinchDistance {
d = minPinchDistance
}
return d
}

// pinchActions lays two fingers on the horizontal line through (x, y), each
// pinchStartGap from the center, and moves them distance pixels away from it
// ("out", zooms in) or toward it ("in", zooms out) over duration milliseconds.
Expand All @@ -97,7 +115,7 @@ const (
// screen.
func pinchActions(x, y int, direction string, distance, duration int) ([]devicekit.TapAction, error) {
if distance <= 0 {
distance = defaultPinchDistance
distance = autoPinchDistance(x)
}
if duration <= 0 {
duration = defaultPinchDurationMs
Expand Down
25 changes: 25 additions & 0 deletions commands/input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,28 @@ func TestNormalizeButtonNameIsCaseInsensitiveAndTrimmed(t *testing.T) {
}
}
}

func TestAutoPinchDistanceKeepsInnerFingerOnScreen(t *testing.T) {
// For a centered pinch, far = pinchStartGap + distance must not pass the
// left edge (x - far >= 0). The old fixed default of 200 failed at x≈201.
for _, x := range []int{201, 180, 300, 540, 640} {
d := autoPinchDistance(x)
far := pinchStartGap + d
if x-far < 0 {
t.Errorf("autoPinchDistance(%d)=%d -> far=%d puts the inner finger at x=%d (off-screen left)", x, d, far, x-far)
}
if d < minPinchDistance {
t.Errorf("autoPinchDistance(%d)=%d is below the minimum %d", x, d, minPinchDistance)
}
}
}

func TestPinchActionsAutoDistanceSucceedsAtNarrowCenter(t *testing.T) {
// A default pinch centered on a 402pt-wide screen (x=201) used to error with
// "past the left edge"; with the auto distance it must build valid actions.
for _, dir := range []string{PinchDirectionOut, PinchDirectionIn} {
if _, err := pinchActions(201, 437, dir, 0, 0); err != nil {
t.Errorf("pinchActions at a narrow center (%s) returned error: %v", dir, err)
}
}
}
4 changes: 3 additions & 1 deletion commands/pinch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ func TestPinchAppliesDefaultDistanceAndDuration(t *testing.T) {
actions, err := pinchActions(640, 1428, PinchDirectionOut, 0, 0)
require.NoError(t, err)

// with no distance given, the travel auto-scales to the center so the inner
// finger stays on-screen
leftStart, leftEnd := fingerPath(t, actions, 0)
assert.Equal(t, defaultPinchDistance, leftStart-leftEnd)
assert.Equal(t, autoPinchDistance(640), leftStart-leftEnd)

for _, a := range actions {
if a.Type == pointerMove && a.Duration != 0 {
Expand Down
9 changes: 9 additions & 0 deletions test/simulator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,15 @@ test.describe('iOS Simulator Tests', () => {
}
});

test('should pinch at the screen center with the default distance', async () => {
test.skip(!simulatorId, 'simulator not found');

// the default distance used to overflow a narrow screen's left edge
// ("would put the left finger at x=-29"); it now scales to the center
mobilecli(['io', 'pinch', '--direction', 'out', '--device', simulatorId]);
mobilecli(['io', 'pinch', '--direction', 'in', '--device', simulatorId]);
});

test.skip('should test device lifecycle: boot, reboot, shutdown', async () => {
// shutdown simulator using simctl to get it offline
shutdownSimulator(simulatorId);
Expand Down
Loading