Skip to content

Commit 057a7ae

Browse files
committed
fix: iOS platform integration
1 parent ae3b8ff commit 057a7ae

File tree

6 files changed

+32
-23
lines changed

6 files changed

+32
-23
lines changed

packages/cli/src/bundlers/metro.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export const runMetro = async (): Promise<ChildProcess> => {
88
...process.env,
99
RN_HARNESS: 'true',
1010
},
11-
ignoreErrors: true,
1211
});
1312
const nodeChildProcess = await metro.nodeChildProcess;
1413

@@ -38,7 +37,7 @@ export const waitForMetro = async (
3837
return;
3938
}
4039
}
41-
} catch {}
40+
} catch { }
4241

4342
if (attempts < maxRetries) {
4443
await new Promise((resolve) => setTimeout(resolve, retryDelay));

packages/cli/src/platforms/android/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,14 @@ const androidPlatformAdapter: PlatformAdapter = {
5656
await runApp(runner.deviceId, runner.bundleId);
5757
},
5858
dispose: async () => {
59+
await killApp(deviceId, runner.bundleId);
60+
5961
if (emulator) {
6062
await killWithAwait(emulator);
6163
}
6264

6365
await interactionEngine.close();
64-
metro.kill();
65-
await killApp(deviceId, runner.bundleId);
66+
await killWithAwait(metro);
6667
},
6768
interactionEngine,
6869
};

packages/cli/src/platforms/ios/build.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { spawn } from '@react-native-harness/tools';
1+
import { spawn, spawnAndForget } from '@react-native-harness/tools';
22
import { reloadApp } from '../../bundlers/metro.js';
33

44
export const listDevices = async (): Promise<any> => {
@@ -33,8 +33,9 @@ export const installPods = async (): Promise<void> => {
3333
};
3434

3535
export const listApps = async (udid: string): Promise<string[]> => {
36-
const { stdout } = await spawn('xcrun', ['simctl', 'listapps', udid]);
37-
return stdout.split('\n').map((line) => line.trim());
36+
const { stdout: plistOutput } = await spawn('xcrun', ['simctl', 'listapps', udid]);
37+
const { stdout: jsonOutput } = await spawn('plutil', ['-convert', 'json', '-o', '-', '-'], { stdin: { string: plistOutput } });
38+
return Object.keys(JSON.parse(jsonOutput));
3839
};
3940

4041
export const isAppInstalled = async (
@@ -66,13 +67,13 @@ export const runApp = async (
6667
simulatorName: string,
6768
appName: string
6869
): Promise<void> => {
69-
await spawn('xcrun', ['simctl', 'terminate', simulatorName, appName]);
70+
await killApp(simulatorName, appName);
7071
await spawn('xcrun', ['simctl', 'launch', simulatorName, appName]);
7172
};
7273

7374
export const killApp = async (
7475
simulatorName: string,
7576
appName: string
7677
): Promise<void> => {
77-
await spawn('xcrun', ['simctl', 'terminate', simulatorName, appName]);
78+
await spawnAndForget('xcrun', ['simctl', 'terminate', simulatorName, appName]);
7879
};

packages/cli/src/platforms/ios/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getInteractionEngine } from '@react-native-harness/interaction-engine';
22
import { TestRunnerConfig } from '@react-native-harness/config';
33
import { type PlatformAdapter } from '../platform-adapter.js';
4-
import { runSimulator } from './simulator.js';
4+
import { getSimulatorStatus, runSimulator, stopSimulator } from './simulator.js';
55
import { isAppInstalled, runApp, killApp } from './build.js';
66
import { killWithAwait } from '../../process.js';
77
import { runMetro } from '../../bundlers/metro.js';
@@ -10,10 +10,15 @@ import { AppNotInstalledError } from '../../errors/appNotInstalledError.js';
1010
const iosPlatformAdapter: PlatformAdapter = {
1111
name: 'ios',
1212
getEnvironment: async (runner: TestRunnerConfig) => {
13+
let shouldStopSimulator = false;
14+
const simulatorStatus = await getSimulatorStatus(runner.deviceId);
1315
const metroPromise = runMetro();
1416
const interactionEnginePromise = getInteractionEngine(runner);
1517

16-
await runSimulator(runner.deviceId);
18+
if (simulatorStatus === 'stopped') {
19+
await runSimulator(runner.deviceId);
20+
shouldStopSimulator = true;
21+
}
1722

1823
const isInstalled = await isAppInstalled(
1924
runner.deviceId,
@@ -33,9 +38,12 @@ const iosPlatformAdapter: PlatformAdapter = {
3338
await runApp(runner.deviceId, runner.bundleId);
3439
},
3540
dispose: async () => {
41+
await killApp(runner.deviceId, runner.bundleId);
42+
if (shouldStopSimulator) {
43+
await stopSimulator(runner.deviceId);
44+
}
3645
await interactionEngine.close();
3746
await killWithAwait(metro);
38-
await killApp(runner.deviceId, runner.bundleId);
3947
},
4048
interactionEngine,
4149
};

packages/interaction-engine/src/backends/appium/server.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { spawn } from '@react-native-harness/tools';
1+
import { spawnAndForget } from '@react-native-harness/tools';
22
import * as net from 'node:net';
33

44
export type RunAppiumServerOptions = {
@@ -7,9 +7,7 @@ export type RunAppiumServerOptions = {
77
};
88

99
export const runAppiumServer = async (options: RunAppiumServerOptions) => {
10-
spawn('appium', ['--port', options.port.toString()], {
11-
ignoreErrors: true,
12-
});
10+
spawnAndForget('appium', ['--port', options.port.toString()]);
1311

1412
await waitForServer({
1513
port: options.port,

packages/tools/src/spawn.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Options, Subprocess } from 'nano-spawn';
22
import nanoSpawn, { SubprocessError } from 'nano-spawn';
33
import logger from './logger.js';
44

5-
export type SpawnOptions = Options & { ignoreErrors?: boolean };
5+
export type SpawnOptions = Options;
66

77
export const spawn = (
88
file: string,
@@ -19,16 +19,18 @@ export const spawn = (
1919
logger.debug(`Running: ${file}`, ...(args ?? []));
2020
const childProcess = nanoSpawn(file, args, { ...defaultOptions, ...options });
2121

22-
if (options?.ignoreErrors) {
23-
childProcess.catch(() => {
24-
// We don't care about the error here.
25-
});
26-
}
27-
2822
setupChildProcessCleanup(childProcess);
2923
return childProcess;
3024
};
3125

26+
export const spawnAndForget = async (file: string, args?: readonly string[], options?: SpawnOptions): Promise<void> => {
27+
try {
28+
await spawn(file, args, options);
29+
} catch {
30+
// We don't care about the error here.
31+
}
32+
};
33+
3234
export { Subprocess, SubprocessError };
3335

3436
const setupChildProcessCleanup = (childProcess: Subprocess) => {

0 commit comments

Comments
 (0)