Skip to content

Commit 88c8b4e

Browse files
committed
fix(replay): infer iOS app selection without platform
1 parent 7d40552 commit 88c8b4e

5 files changed

Lines changed: 41 additions & 12 deletions

File tree

src/core/__tests__/dispatch-resolve.test.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,33 @@ test('resolveTargetDevice selects the unique booted simulator with the requested
185185
);
186186
});
187187

188-
test('resolveTargetDevice reuses an app-aware selection for later request resolution', async () => {
188+
test('resolveTargetDevice uses app-aware iOS selection when platform is omitted', async () => {
189+
mockListAppleDevices.mockResolvedValue([bootedSimulator, secondBootedSimulator]);
190+
mockFindIosSimulatorInstalledApp.mockImplementation(async (device) =>
191+
device.id === secondBootedSimulator.id ? 'com.example.demo' : undefined,
192+
);
193+
194+
const result = await resolveTargetDevice({}, { appleSimulatorAppTarget: 'com.example.demo' });
195+
196+
assert.equal(result.id, secondBootedSimulator.id);
197+
assert.deepEqual(
198+
mockFindIosSimulatorInstalledApp.mock.calls.map(([device, app]) => [device.id, app]),
199+
[
200+
[bootedSimulator.id, 'com.example.demo'],
201+
[secondBootedSimulator.id, 'com.example.demo'],
202+
],
203+
);
204+
});
205+
206+
test('resolveTargetDevice reuses an app-aware selection for later platform-inferred resolution', async () => {
189207
mockListAppleDevices.mockResolvedValue([bootedSimulator, secondBootedSimulator]);
190208
mockFindIosSimulatorInstalledApp.mockImplementation(async (device) =>
191209
device.id === secondBootedSimulator.id ? 'com.example.demo' : undefined,
192210
);
193211

194212
const [appAware, laterResolution] = await withResolveTargetDeviceCacheScope(async () => [
195-
await resolveTargetDevice({ platform: 'ios' }, { appleSimulatorAppTarget: 'com.example.demo' }),
196-
await resolveTargetDevice({ platform: 'ios' }),
213+
await resolveTargetDevice({}, { appleSimulatorAppTarget: 'com.example.demo' }),
214+
await resolveTargetDevice({}),
197215
]);
198216

199217
assert.equal(appAware.id, secondBootedSimulator.id);
@@ -233,11 +251,11 @@ test('resolveTargetDevice refuses ambiguous booted simulator app matches', async
233251
assert.equal(error.details?.hint, 'Pass --udid to select the intended simulator explicitly.');
234252
});
235253

236-
test('resolveTargetDevice does not probe when an Apple device is explicitly selected', async () => {
254+
test('resolveTargetDevice preserves an explicit device selector when platform is omitted', async () => {
237255
mockListAppleDevices.mockResolvedValue([bootedSimulator, secondBootedSimulator]);
238256

239257
const result = await resolveTargetDevice(
240-
{ platform: 'ios', udid: bootedSimulator.id },
258+
{ udid: bootedSimulator.id },
241259
{ appleSimulatorAppTarget: 'com.example.demo' },
242260
);
243261

src/core/dispatch-resolve.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ export async function resolveTargetDevice(
214214
}
215215
const injectedDevices = await readInjectedDeviceInventory(inventoryRequest);
216216
if (injectedDevices) {
217-
if (isAppleResolutionSelector(selector)) {
217+
if (shouldUseAppleResolution(selector, options)) {
218218
return cacheResolvedTargetDevice(
219219
cacheKey,
220220
await resolveAppleDevice(injectedDevices, selector as AppleDeviceSelector, {
@@ -232,7 +232,7 @@ export async function resolveTargetDevice(
232232

233233
const devices = await listLocalDeviceInventory(inventoryRequest);
234234

235-
if (isAppleResolutionSelector(selector)) {
235+
if (shouldUseAppleResolution(selector, options)) {
236236
return cacheResolvedTargetDevice(
237237
cacheKey,
238238
await resolveAppleDevice(devices, selector as AppleDeviceSelector, {
@@ -328,6 +328,14 @@ function isAppleResolutionSelector(selector: {
328328
return isApplePlatform(selector.platform);
329329
}
330330

331+
function shouldUseAppleResolution(
332+
selector: { platform?: PlatformSelector; target?: DeviceTarget },
333+
options: ResolveTargetDeviceOptions,
334+
): boolean {
335+
if (isAppleResolutionSelector(selector)) return true;
336+
return selector.platform === undefined && options.appleSimulatorAppTarget !== undefined;
337+
}
338+
331339
function readResolveTargetDeviceCache(cacheKey: string): DeviceInfo | undefined {
332340
const cache = resolveTargetDeviceCacheScope.getStore();
333341
const cached = cache?.get(cacheKey);

src/daemon/__tests__/request-router-open.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ test('fresh replay reserves its authored app simulator before any replay step',
143143
session: 'fresh-replay',
144144
command: 'replay',
145145
positionals: [replayPath],
146-
flags: { platform: 'ios' },
146+
flags: {},
147147
meta: { cwd: root },
148148
},
149149
sessionName: 'fresh-replay',
@@ -152,7 +152,7 @@ test('fresh replay reserves its authored app simulator before any replay step',
152152

153153
expect(keys).toEqual(['session:fresh-replay', 'device:SIM-WITH-APP']);
154154
expect(mockResolveTargetDevice).toHaveBeenCalledWith(
155-
{ platform: 'ios' },
155+
{},
156156
{ appleSimulatorAppTarget: 'com.example.demo' },
157157
);
158158
});

src/daemon/handlers/__tests__/session-replay-runtime-plan.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ test('fresh typed Maestro replay resolves its configured app before runtime defa
410410
const response = await runReplayScriptFile({
411411
req: baseReq({
412412
positionals: [flowPath],
413-
flags: { replayBackend: 'maestro', platform: 'ios' },
413+
flags: { replayBackend: 'maestro' },
414414
runtime: { metroPort: 8081 },
415415
}),
416416
sessionName: 'default',
@@ -421,7 +421,7 @@ test('fresh typed Maestro replay resolves its configured app before runtime defa
421421

422422
expect(response.ok).toBe(true);
423423
expect(mockResolveTargetDevice).toHaveBeenCalledWith(
424-
{ replayBackend: 'maestro', platform: 'ios' },
424+
{ replayBackend: 'maestro' },
425425
{ appleSimulatorAppTarget: 'com.example.demo' },
426426
);
427427
expect(invoke).toHaveBeenCalledWith(

src/daemon/handlers/session-replay-maestro-runtime.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ async function resolveMaestroReplayBinding(params: {
214214
session?.device ??
215215
(requestedPlatform === 'android' || requestedPlatform === 'ios'
216216
? undefined
217-
: await resolveTargetDevice(req.flags ?? {}));
217+
: await resolveTargetDevice(
218+
req.flags ?? {},
219+
buildMaestroReplayTargetDeviceResolutionOptions(program),
220+
));
218221
const platform = resolveMaestroPlatform(req, device);
219222
const runtimeHints = resolveEffectiveOpenRuntimeHints({
220223
req,

0 commit comments

Comments
 (0)