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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ extension RunnerTests {
min(max((durationMs / 5.0) / 1000.0, 0.016), 0.120)
}

private func coordinateDragHoldDuration() -> TimeInterval {
0.050
}

func unsupportedResponse(for outcome: RunnerInteractionOutcome) -> Response? {
switch outcome {
case .performed:
Expand Down Expand Up @@ -657,7 +661,7 @@ extension RunnerTests {
}
let holdDuration = command.synthesized == true
? synthesizedSwipeFallbackHoldDuration(durationMs: command.durationMs ?? 250)
: min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
: coordinateDragHoldDuration()
let (timing, outcome) = performGesture(activeApp) {
dragAt(
app: activeApp,
Expand Down Expand Up @@ -715,7 +719,7 @@ extension RunnerTests {
}
let holdDuration = command.synthesized == true
? synthesizedSwipeFallbackHoldDuration(durationMs: command.durationMs ?? 250)
: min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
: coordinateDragHoldDuration()
let (timing, outcome) = performGesture(activeApp) {
performDragSeries(
count: count,
Expand Down
3 changes: 1 addition & 2 deletions src/core/__tests__/dispatch-interactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ test('handleSwipeCommand preserves iOS swipe duration through dispatch', async (
});
});

test('handleSwipeCommand uses synthesized iOS runner drag series for repeated swipes', async () => {
test('handleSwipeCommand uses XCTest iOS runner drag series for repeated swipes', async () => {
mockRunIosRunnerCommand.mockResolvedValueOnce({
gestureStartUptimeMs: 100,
gestureEndUptimeMs: 720,
Expand Down Expand Up @@ -194,7 +194,6 @@ test('handleSwipeCommand uses synthesized iOS runner drag series for repeated sw
count: 2,
pauseMs: 50,
pattern: 'ping-pong',
synthesized: true,
appBundleId: 'com.example.App',
});
assert.equal(result.timingMode, 'runner-series');
Expand Down
18 changes: 0 additions & 18 deletions src/core/__tests__/dispatch-series.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
requireIntInRange,
shouldUseIosTapSeries,
shouldUseIosDragSeries,
shouldUseSynthesizedIosDrag,
} from '../dispatch-series.ts';
import { AppError } from '../../utils/errors.ts';
import type { DeviceInfo } from '../../utils/device.ts';
Expand Down Expand Up @@ -66,23 +65,6 @@ test('shouldUseIosDragSeries returns false when count is 1', () => {
assert.equal(shouldUseIosDragSeries(iosDevice, 1), false);
});

// --- shouldUseSynthesizedIosDrag ---

test('shouldUseSynthesizedIosDrag returns true only for non-tvOS iOS targets', () => {
assert.equal(shouldUseSynthesizedIosDrag(iosDevice), true);
assert.equal(shouldUseSynthesizedIosDrag({ ...iosDevice, target: 'tv' }), false);
assert.equal(
shouldUseSynthesizedIosDrag({
platform: 'macos',
id: 'mac',
name: 'Mac',
kind: 'device',
target: 'desktop',
}),
false,
);
});

// --- computeDeterministicJitter ---

// --- runRepeatedSeries ---
2 changes: 0 additions & 2 deletions src/core/dispatch-interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
requireIntInRange,
shouldUseIosTapSeries,
shouldUseIosDragSeries,
shouldUseSynthesizedIosDrag,
computeDeterministicJitter,
runRepeatedSeries,
} from './dispatch-series.ts';
Expand Down Expand Up @@ -488,7 +487,6 @@ async function runSwipeCoordinates(params: {
count,
pauseMs,
pattern,
...(shouldUseSynthesizedIosDrag(device) ? { synthesized: true } : {}),
appBundleId: context?.appBundleId,
},
{
Expand Down
4 changes: 0 additions & 4 deletions src/core/dispatch-series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ export function shouldUseIosDragSeries(device: DeviceInfo, count: number): boole
return isApplePlatform(device.platform) && count > 1;
}

export function shouldUseSynthesizedIosDrag(device: DeviceInfo): boolean {
return device.platform === 'ios' && device.target !== 'tv';
}

export function computeDeterministicJitter(index: number, jitterPx: number): [number, number] {
if (jitterPx <= 0) return [0, 0];
const [dx, dy] = DETERMINISTIC_JITTER_PATTERN[index % DETERMINISTIC_JITTER_PATTERN.length]!;
Expand Down
9 changes: 3 additions & 6 deletions src/platforms/ios/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ for (const [name, device] of [
});
}

test('iosRunnerOverrides maps swipe to synthesized iOS drag duration', async () => {
test('iosRunnerOverrides maps swipe to XCTest iOS drag duration', async () => {
mockRunIosRunnerCommand.mockResolvedValue({});

const { overrides } = iosRunnerOverrides(IOS_TEST_SIMULATOR, {
Expand All @@ -235,7 +235,6 @@ test('iosRunnerOverrides maps swipe to synthesized iOS drag duration', async ()
x2: 180,
y2: 200,
durationMs: 300,
synthesized: true,
appBundleId: 'com.example.App',
});
assert.deepEqual(mockRunIosRunnerCommand.mock.calls[1]?.[1], {
Expand All @@ -245,7 +244,6 @@ test('iosRunnerOverrides maps swipe to synthesized iOS drag duration', async ()
x2: 180,
y2: 200,
durationMs: 250,
synthesized: true,
appBundleId: 'com.example.App',
});
assert.deepEqual(mockRunIosRunnerCommand.mock.calls[2]?.[1], {
Expand All @@ -255,7 +253,6 @@ test('iosRunnerOverrides maps swipe to synthesized iOS drag duration', async ()
x2: 180,
y2: 200,
durationMs: 300,
synthesized: true,
appBundleId: 'com.example.App',
});
});
Expand Down Expand Up @@ -286,7 +283,7 @@ for (const [name, device] of [
}

for (const [name, device, expectedGestureFields] of [
['iOS', IOS_TEST_SIMULATOR, { durationMs: 250, synthesized: true }],
['iOS', IOS_TEST_SIMULATOR, { durationMs: 250 }],
['macOS', MACOS_TEST_DEVICE, {}],
] as const) {
test(`iosRunnerOverrides maps ${name} scroll to the expected drag path`, async () => {
Expand Down Expand Up @@ -938,7 +935,7 @@ test('screenshotIos retries simulator capture timeouts and eventually succeeds',
else process.env.AGENT_DEVICE_TEST_SCREENSHOT_COUNT_FILE = previousScreenshotCountFile;
await fs.rm(tmpDir, { recursive: true, force: true });
}
});
}, 10_000);

test('openIosApp web URL on iOS device without app falls back to Safari', async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-device-ios-safari-test-'));
Expand Down
18 changes: 8 additions & 10 deletions src/platforms/ios/interactions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AppError } from '../../utils/errors.ts';
import type { DeviceInfo } from '../../utils/device.ts';
import { shouldUseSynthesizedIosDrag } from '../../core/dispatch-series.ts';
import { buildScrollGesturePlan, type ScrollDirection } from '../../core/scroll-gesture.ts';
import { runIosRunnerCommand } from './runner-client.ts';
import type { RunnerCommand } from './runner-contract.ts';
Expand Down Expand Up @@ -108,7 +107,7 @@ export function iosRunnerOverrides(
return await runIosRunnerCommand(
device,
iosDragCommand(device, ctx, x1, y1, x2, y2, durationMs, {
synthesizedDefaultDurationMs: IOS_SWIPE_DEFAULT_DURATION_MS,
defaultDurationMs: IOS_SWIPE_DEFAULT_DURATION_MS,
}),
runnerOpts,
);
Expand All @@ -117,7 +116,7 @@ export function iosRunnerOverrides(
return await runIosRunnerCommand(
device,
iosDragCommand(device, ctx, x1, y1, x2, y2, durationMs, {
synthesizedDefaultDurationMs: 500,
defaultDurationMs: 500,
legacyDefaultDurationMs: 500,
}),
runnerOpts,
Expand Down Expand Up @@ -274,22 +273,21 @@ function iosDragCommand(
y2: number,
durationMs: number | undefined,
options: {
synthesizedDefaultDurationMs: number;
defaultDurationMs: number;
legacyDefaultDurationMs?: number;
},
): RunnerCommand {
const useSynthesizedDrag = shouldUseSynthesizedIosDrag(device);
const normalizedDurationMs = useSynthesizedDrag
? iosGestureDurationMs(durationMs, options.synthesizedDefaultDurationMs)
: (durationMs ?? options.legacyDefaultDurationMs);
const normalizedDurationMs =
device.platform === 'ios' && device.target !== 'tv'
? iosGestureDurationMs(durationMs, options.defaultDurationMs)
: (durationMs ?? options.legacyDefaultDurationMs);
return {
command: 'drag',
x,
y,
x2,
y2,
...(normalizedDurationMs !== undefined ? { durationMs: normalizedDurationMs } : {}),
...(useSynthesizedDrag ? { synthesized: true } : {}),
appBundleId: ctx.appBundleId,
};
}
Expand Down Expand Up @@ -354,7 +352,7 @@ async function runAppleScroll(
frame.originX + plan.x2,
frame.originY + plan.y2,
undefined,
{ synthesizedDefaultDurationMs: IOS_SWIPE_DEFAULT_DURATION_MS },
{ defaultDurationMs: IOS_SWIPE_DEFAULT_DURATION_MS },
),
runnerOpts,
);
Expand Down
1 change: 0 additions & 1 deletion test/integration/provider-scenarios/ios-world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export async function createIosSettingsWorld(): Promise<IosSettingsWorld> {
x2: 276,
y2: 122,
durationMs: 500,
synthesized: true,
appBundleId: 'com.apple.Preferences',
},
result: { dragged: true },
Expand Down
Loading