@react-native-harness/cli 1.0.0-alpha.15 → 1.0.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/commands/test.d.ts.map +1 -1
  2. package/dist/commands/test.js +4 -2
  3. package/dist/errors/errorHandler.d.ts +1 -1
  4. package/dist/errors/errorHandler.d.ts.map +1 -1
  5. package/dist/errors/errorHandler.js +101 -123
  6. package/dist/errors/errors.d.ts +2 -7
  7. package/dist/errors/errors.d.ts.map +1 -1
  8. package/dist/errors/errors.js +0 -17
  9. package/dist/external.d.ts +2 -0
  10. package/dist/external.d.ts.map +1 -0
  11. package/dist/external.js +1 -0
  12. package/dist/index.js +58 -49
  13. package/dist/jest.d.ts +2 -0
  14. package/dist/jest.d.ts.map +1 -0
  15. package/dist/jest.js +7 -0
  16. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  17. package/package.json +25 -8
  18. package/src/external.ts +0 -0
  19. package/src/index.ts +65 -72
  20. package/tsconfig.json +0 -3
  21. package/tsconfig.lib.json +1 -12
  22. package/src/bundlers/metro.ts +0 -102
  23. package/src/commands/test.ts +0 -228
  24. package/src/discovery/index.ts +0 -2
  25. package/src/discovery/testDiscovery.ts +0 -50
  26. package/src/errors/errorHandler.ts +0 -226
  27. package/src/errors/errors.ts +0 -131
  28. package/src/platforms/android/build.ts +0 -49
  29. package/src/platforms/android/device.ts +0 -48
  30. package/src/platforms/android/emulator.ts +0 -137
  31. package/src/platforms/android/index.ts +0 -87
  32. package/src/platforms/ios/build.ts +0 -71
  33. package/src/platforms/ios/device.ts +0 -79
  34. package/src/platforms/ios/index.ts +0 -66
  35. package/src/platforms/ios/simulator.ts +0 -171
  36. package/src/platforms/platform-adapter.ts +0 -11
  37. package/src/platforms/platform-registry.ts +0 -26
  38. package/src/platforms/vega/build.ts +0 -85
  39. package/src/platforms/vega/device.ts +0 -258
  40. package/src/platforms/vega/index.ts +0 -107
  41. package/src/platforms/web/index.ts +0 -16
  42. package/src/process.ts +0 -33
  43. package/src/utils.ts +0 -29
@@ -1,171 +0,0 @@
1
- import { spawn, SubprocessError } from '@react-native-harness/tools';
2
-
3
- export type IOSSimulatorStatus = 'stopped' | 'loading' | 'running';
4
-
5
- export const getSimulatorDeviceId = async (
6
- simulatorName: string,
7
- systemVersion: string
8
- ): Promise<string | null> => {
9
- try {
10
- const { stdout } = await spawn('xcrun', [
11
- 'simctl',
12
- 'list',
13
- 'devices',
14
- '--json',
15
- ]);
16
- const devices = JSON.parse(stdout);
17
- const expectedRuntimeId = `com.apple.CoreSimulator.SimRuntime.iOS-${systemVersion.replace(
18
- /\./,
19
- '-'
20
- )}`;
21
-
22
- const runtime = devices.devices[expectedRuntimeId];
23
-
24
- if (!runtime) {
25
- return null;
26
- }
27
-
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- const device = runtime.find((d: any) => d.name === simulatorName);
30
-
31
- if (device) {
32
- return device.udid;
33
- }
34
-
35
- return null;
36
- } catch {
37
- return null;
38
- }
39
- };
40
-
41
- export const getAvailableSimulators = async (): Promise<
42
- Array<{ name: string; udid: string; runtime: string }>
43
- > => {
44
- try {
45
- const { stdout } = await spawn('xcrun', [
46
- 'simctl',
47
- 'list',
48
- 'devices',
49
- '--json',
50
- ]);
51
- const devices = JSON.parse(stdout);
52
- const simulators: Array<{
53
- name: string;
54
- udid: string;
55
- runtime: string;
56
- }> = [];
57
-
58
- for (const runtime in devices.devices) {
59
- if (runtime.includes('iOS')) {
60
- const runtimeDevices = devices.devices[runtime];
61
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
- runtimeDevices.forEach((device: any) => {
63
- if (device.isAvailable) {
64
- simulators.push({
65
- name: device.name,
66
- udid: device.udid,
67
- runtime: runtime,
68
- });
69
- }
70
- });
71
- }
72
- }
73
-
74
- return simulators;
75
- } catch {
76
- return [];
77
- }
78
- };
79
-
80
- export const getSimulatorStatus = async (
81
- udid: string
82
- ): Promise<IOSSimulatorStatus> => {
83
- try {
84
- const { stdout } = await spawn('xcrun', [
85
- 'simctl',
86
- 'list',
87
- 'devices',
88
- '--json',
89
- ]);
90
- const devices = JSON.parse(stdout);
91
-
92
- for (const runtime in devices.devices) {
93
- if (runtime.includes('iOS')) {
94
- const runtimeDevices = devices.devices[runtime];
95
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
- const device = runtimeDevices.find((d: any) => d.udid === udid);
97
-
98
- if (device) {
99
- switch (device.state) {
100
- case 'Booted':
101
- return 'running';
102
- case 'Booting':
103
- return 'loading';
104
- default:
105
- return 'stopped';
106
- }
107
- }
108
- }
109
- }
110
-
111
- return 'stopped';
112
- } catch {
113
- return 'stopped';
114
- }
115
- };
116
-
117
- export const runSimulator = async (udid: string): Promise<void> => {
118
- try {
119
- await spawn('xcrun', ['simctl', 'boot', udid]);
120
- } catch (bootError) {
121
- // Ignore if simulator is already booted
122
- if (
123
- bootError instanceof SubprocessError &&
124
- !bootError.stderr?.includes(
125
- 'Unable to boot device in current state: Booted'
126
- )
127
- ) {
128
- throw bootError;
129
- }
130
- }
131
-
132
- await spawn('open', ['-a', 'Simulator']);
133
-
134
- let attempts = 0;
135
-
136
- while (true) {
137
- attempts++;
138
-
139
- const status = await getSimulatorStatus(udid);
140
-
141
- if (status === 'running') {
142
- break;
143
- }
144
-
145
- if (attempts > 10) {
146
- throw new Error('Simulator not running');
147
- }
148
-
149
- await new Promise((resolve) => setTimeout(resolve, 1000));
150
- }
151
- };
152
-
153
- export const stopSimulator = async (udid: string): Promise<void> => {
154
- await stopSimulatorById(udid);
155
- };
156
-
157
- const stopSimulatorById = async (udid: string): Promise<void> => {
158
- try {
159
- await spawn('xcrun', ['simctl', 'shutdown', udid]);
160
- } catch (shutdownError) {
161
- // Ignore if simulator is already shut down
162
- if (
163
- shutdownError instanceof SubprocessError &&
164
- !shutdownError.stderr?.includes(
165
- 'Unable to shutdown device in current state: Shutdown'
166
- )
167
- ) {
168
- throw shutdownError;
169
- }
170
- }
171
- };
@@ -1,11 +0,0 @@
1
- import { TestRunnerConfig } from '@react-native-harness/config';
2
-
3
- export type Environment = {
4
- restart: () => Promise<void>;
5
- dispose: () => Promise<void>;
6
- };
7
-
8
- export type PlatformAdapter = {
9
- name: string;
10
- getEnvironment: (runner: TestRunnerConfig) => Promise<Environment>;
11
- };
@@ -1,26 +0,0 @@
1
- import { PlatformAdapter } from './platform-adapter.js';
2
- import androidPlatformAdapter from './android/index.js';
3
- import iosPlatformAdapter from './ios/index.js';
4
- import webPlatformAdapter from './web/index.js';
5
- import vegaPlatformAdapter from './vega/index.js';
6
-
7
- const platformAdapters = {
8
- android: androidPlatformAdapter,
9
- ios: iosPlatformAdapter,
10
- web: webPlatformAdapter,
11
- vega: vegaPlatformAdapter,
12
- };
13
-
14
- export const getPlatformAdapter = async (
15
- platformName: string
16
- ): Promise<PlatformAdapter> => {
17
- if (!(platformName in platformAdapters)) {
18
- throw new Error(`Platform adapter for ${platformName} not found`);
19
- }
20
-
21
- try {
22
- return platformAdapters[platformName as keyof typeof platformAdapters];
23
- } catch {
24
- throw new Error(`Platform adapter for ${platformName} not found`);
25
- }
26
- };
@@ -1,85 +0,0 @@
1
- import path from 'node:path';
2
- import { spawn } from '@react-native-harness/tools';
3
-
4
- export type VegaBuildTarget = 'sim_tv_x86_64' | 'sim_tv_aarch64';
5
- export type VegaBuildType = 'Debug' | 'Release';
6
-
7
- /**
8
- * Build Vega app and produce .vpkg file
9
- */
10
- export const buildVegaApp = async (
11
- buildType: VegaBuildType = 'Release',
12
- target?: VegaBuildTarget
13
- ): Promise<void> => {
14
- const args = ['run', 'build:app'];
15
-
16
- if (buildType) {
17
- args.push('-b', buildType);
18
- }
19
-
20
- if (target) {
21
- args.push('-t', target);
22
- }
23
-
24
- await spawn('npm', args);
25
- };
26
-
27
- /**
28
- * Clean build artifacts
29
- */
30
- export const cleanBuild = async (): Promise<void> => {
31
- await spawn('kepler', ['clean']);
32
- };
33
-
34
- /**
35
- * Get the expected .vpkg file path based on build configuration
36
- */
37
- export const getVpkgPath = (
38
- appName: string,
39
- buildType: VegaBuildType = 'Release',
40
- target: VegaBuildTarget = 'sim_tv_x86_64'
41
- ): string => {
42
- const buildTypeStr = buildType.toLowerCase();
43
- const vpkgFileName = `${appName}_${target}.vpkg`;
44
-
45
- return path.join(
46
- process.cwd(),
47
- 'build',
48
- `${target}-${buildTypeStr}`,
49
- vpkgFileName
50
- );
51
- };
52
-
53
- /**
54
- * Launch an already installed app on specified Vega virtual device
55
- */
56
- export const runApp = async (
57
- deviceId: string,
58
- bundleId: string
59
- ): Promise<void> => {
60
- await spawn('kepler', [
61
- 'device',
62
- 'launch-app',
63
- '--device',
64
- deviceId,
65
- '--appName',
66
- bundleId,
67
- ]);
68
- };
69
-
70
- /**
71
- * Kill/terminate app on specified Vega virtual device
72
- */
73
- export const killApp = async (
74
- deviceId: string,
75
- bundleId: string
76
- ): Promise<void> => {
77
- await spawn('kepler', [
78
- 'device',
79
- 'terminate-app',
80
- '--device',
81
- deviceId,
82
- '--appName',
83
- bundleId,
84
- ]);
85
- };
@@ -1,258 +0,0 @@
1
- import { spawn } from '@react-native-harness/tools';
2
-
3
- export type VegaVirtualDeviceStatus = 'running' | 'stopped';
4
-
5
- /**
6
- * List all available Vega virtual devices
7
- * Returns array of device identifiers that can be used with kepler commands
8
- */
9
- export const listVegaDevices = async (): Promise<string[]> => {
10
- try {
11
- const { stdout } = await spawn('kepler', ['device', 'list']);
12
- const lines = stdout.trim().split('\n');
13
- const devices: string[] = [];
14
-
15
- for (const line of lines) {
16
- if (line.trim()) {
17
- // Parse device line format: "VirtualDevice : tv - x86_64 - OS - hostname"
18
- // or potentially "VegaTV_1 : tv - x86_64 - OS - hostname" for named instances
19
- const deviceId = line.split(' : ')[0].trim();
20
- if (
21
- deviceId &&
22
- (deviceId === 'VirtualDevice' || deviceId.startsWith('Vega'))
23
- ) {
24
- devices.push(deviceId);
25
- }
26
- }
27
- }
28
-
29
- return devices;
30
- } catch {
31
- return [];
32
- }
33
- };
34
-
35
- /**
36
- * Check if a specific Vega virtual device is connected/available
37
- */
38
- export const isVegaDeviceConnected = async (
39
- deviceId: string
40
- ): Promise<boolean> => {
41
- try {
42
- const { stdout } = await spawn('kepler', [
43
- 'device',
44
- 'is-connected',
45
- '--device',
46
- deviceId,
47
- ]);
48
- return stdout.includes('is connected');
49
- } catch {
50
- return false;
51
- }
52
- };
53
-
54
- /**
55
- * Check if an app is installed on the specified Vega virtual device
56
- */
57
- export const isAppInstalled = async (
58
- deviceId: string,
59
- bundleId: string
60
- ): Promise<boolean> => {
61
- try {
62
- await spawn('kepler', [
63
- 'device',
64
- 'is-app-installed',
65
- '--device',
66
- deviceId,
67
- '--appName',
68
- bundleId,
69
- ]);
70
- return true;
71
- } catch {
72
- return false;
73
- }
74
- };
75
-
76
- /**
77
- * Check if an app is currently running on the specified Vega virtual device
78
- */
79
- export const isAppRunning = async (
80
- deviceId: string,
81
- bundleId: string
82
- ): Promise<boolean> => {
83
- try {
84
- await spawn('kepler', [
85
- 'device',
86
- 'is-app-running',
87
- '--device',
88
- deviceId,
89
- '--appName',
90
- bundleId,
91
- ]);
92
- return true;
93
- } catch {
94
- return false;
95
- }
96
- };
97
-
98
- /**
99
- * Install app on specified Vega virtual device using .vpkg file
100
- */
101
- export const installApp = async (
102
- deviceId: string,
103
- vpkgPath: string
104
- ): Promise<void> => {
105
- await spawn('kepler', [
106
- 'device',
107
- 'install-app',
108
- '-p',
109
- vpkgPath,
110
- '--device',
111
- deviceId,
112
- ]);
113
- };
114
-
115
- /**
116
- * Terminate app on specified Vega virtual device
117
- */
118
- export const terminateApp = async (
119
- deviceId: string,
120
- bundleId: string
121
- ): Promise<void> => {
122
- await spawn('kepler', [
123
- 'device',
124
- 'terminate-app',
125
- '--device',
126
- deviceId,
127
- '--appName',
128
- bundleId,
129
- ]);
130
- };
131
-
132
- /**
133
- * Uninstall app from specified Vega virtual device
134
- */
135
- export const uninstallApp = async (
136
- deviceId: string,
137
- bundleId: string
138
- ): Promise<void> => {
139
- await spawn('kepler', [
140
- 'device',
141
- 'uninstall-app',
142
- '--device',
143
- deviceId,
144
- '--appName',
145
- bundleId,
146
- ]);
147
- };
148
-
149
- /**
150
- * Start port forwarding for debugging on specified Vega virtual device
151
- */
152
- export const startPortForwarding = async (
153
- deviceId: string,
154
- port: number,
155
- forward = true
156
- ): Promise<void> => {
157
- await spawn('kepler', [
158
- 'device',
159
- 'start-port-forwarding',
160
- '--device',
161
- deviceId,
162
- '--port',
163
- port.toString(),
164
- '--forward',
165
- forward.toString(),
166
- ]);
167
- };
168
-
169
- /**
170
- * Stop port forwarding on specified Vega virtual device
171
- */
172
- export const stopPortForwarding = async (
173
- deviceId: string,
174
- port: number,
175
- forward = true
176
- ): Promise<void> => {
177
- await spawn('kepler', [
178
- 'device',
179
- 'stop-port-forwarding',
180
- '--device',
181
- deviceId,
182
- '--port',
183
- port.toString(),
184
- '--forward',
185
- forward.toString(),
186
- ]);
187
- };
188
-
189
- /**
190
- * Get status of a specific Vega virtual device
191
- * Note: Vega CLI might manage virtual devices globally, so this checks if the device is available
192
- */
193
- export const getVegaDeviceStatus = async (
194
- deviceId: string
195
- ): Promise<VegaVirtualDeviceStatus> => {
196
- try {
197
- // First check if the device is connected/available
198
- const isConnected = await isVegaDeviceConnected(deviceId);
199
- if (isConnected) {
200
- return 'running';
201
- }
202
-
203
- // Check general virtual device status
204
- const { stdout } = await spawn('kepler', ['virtual-device', 'status']);
205
- // Parse the status output to determine if VVD is running
206
- return stdout.toLowerCase().includes('running') ||
207
- stdout.toLowerCase().includes('ready')
208
- ? 'running'
209
- : 'stopped';
210
- } catch {
211
- return 'stopped';
212
- }
213
- };
214
-
215
- /**
216
- * Start Vega Virtual Device
217
- * Note: Vega might manage virtual devices globally, this starts the virtual device system
218
- */
219
- export const startVirtualDevice = async (): Promise<void> => {
220
- await spawn('kepler', ['virtual-device', 'start']);
221
-
222
- // Poll for VVD status until it's running
223
- let attempts = 0;
224
- const maxAttempts = 30; // 30 seconds timeout
225
-
226
- while (attempts < maxAttempts) {
227
- const { stdout } = await spawn('kepler', ['virtual-device', 'status']);
228
- if (
229
- stdout.toLowerCase().includes('running') ||
230
- stdout.toLowerCase().includes('ready')
231
- ) {
232
- return;
233
- }
234
-
235
- await new Promise((resolve) => setTimeout(resolve, 1000));
236
- attempts++;
237
- }
238
-
239
- throw new Error('Vega Virtual Device failed to start within timeout');
240
- };
241
-
242
- /**
243
- * Stop Vega Virtual Device
244
- */
245
- export const stopVirtualDevice = async (): Promise<void> => {
246
- await spawn('kepler', ['virtual-device', 'stop']);
247
- };
248
-
249
- /**
250
- * Combined install and run command for specified Vega virtual device (Vega-specific convenience method)
251
- */
252
- export const runKepler = async (
253
- deviceId: string,
254
- vpkgPath: string,
255
- bundleId: string
256
- ): Promise<void> => {
257
- await spawn('kepler', ['run-kepler', vpkgPath, bundleId, '-d', deviceId]);
258
- };
@@ -1,107 +0,0 @@
1
- import {
2
- assertVegaRunnerConfig,
3
- TestRunnerConfig,
4
- } from '@react-native-harness/config';
5
- import { logger } from '@react-native-harness/tools';
6
-
7
- import { type PlatformAdapter } from '../platform-adapter.js';
8
- import {
9
- isAppInstalled,
10
- getVegaDeviceStatus,
11
- isVegaDeviceConnected,
12
- startVirtualDevice,
13
- stopVirtualDevice,
14
- startPortForwarding,
15
- stopPortForwarding,
16
- } from './device.js';
17
- import { runApp, killApp } from './build.js';
18
- import { killWithAwait } from '../../process.js';
19
- import { runMetro } from '../../bundlers/metro.js';
20
- import { AppNotInstalledError } from '../../errors/errors.js';
21
-
22
- const vegaPlatformAdapter: PlatformAdapter = {
23
- name: 'vega',
24
- getEnvironment: async (runner: TestRunnerConfig) => {
25
- assertVegaRunnerConfig(runner);
26
-
27
- let shouldStopVirtualDevice = false;
28
-
29
- // Check if the specific Vega device is available
30
- const deviceStatus = await getVegaDeviceStatus(runner.deviceId);
31
- logger.debug(`Vega device ${runner.deviceId} status: ${deviceStatus}`);
32
-
33
- if (deviceStatus === 'stopped') {
34
- logger.debug('Starting Vega Virtual Device system');
35
- await startVirtualDevice();
36
- shouldStopVirtualDevice = true;
37
-
38
- // Wait a bit for the device to become available
39
- await new Promise((resolve) => setTimeout(resolve, 2000));
40
- }
41
-
42
- // Verify device is now connected
43
- const isConnected = await isVegaDeviceConnected(runner.deviceId);
44
- if (!isConnected) {
45
- throw new Error(
46
- `Vega device ${runner.deviceId} is not available. Make sure the virtual device is configured and running.`
47
- );
48
- }
49
-
50
- // Start Metro bundler
51
- const metroPromise = runMetro();
52
-
53
- // Set up port forwarding for debugging (similar to Android)
54
- await Promise.all([
55
- startPortForwarding(runner.deviceId, 8081, false), // reverse port forwarding for JS debugging
56
- startPortForwarding(runner.deviceId, 8080, false),
57
- startPortForwarding(runner.deviceId, 3001, false),
58
- ]);
59
- logger.debug('Port forwarding established');
60
-
61
- // Check if app is installed
62
- const isInstalled = await isAppInstalled(runner.deviceId, runner.bundleId);
63
- logger.debug(`App is installed: ${isInstalled}`);
64
-
65
- if (!isInstalled) {
66
- throw new AppNotInstalledError(runner.deviceId, runner.bundleId, 'vega');
67
- }
68
-
69
- logger.debug('Waiting for Metro to start');
70
- const metro = await metroPromise;
71
- logger.debug('Metro started');
72
-
73
- logger.debug('Running Vega app');
74
- await runApp(runner.deviceId, runner.bundleId);
75
- logger.debug('Vega app running');
76
-
77
- return {
78
- restart: async () => {
79
- await runApp(runner.deviceId, runner.bundleId);
80
- },
81
- dispose: async () => {
82
- // Kill the app
83
- await killApp(runner.deviceId, runner.bundleId);
84
-
85
- // Stop port forwarding
86
- await Promise.all([
87
- stopPortForwarding(runner.deviceId, 8081, false),
88
- stopPortForwarding(runner.deviceId, 8080, false),
89
- stopPortForwarding(runner.deviceId, 3001, false),
90
- ]).catch((error) => {
91
- // Don't fail disposal if port forwarding cleanup fails
92
- logger.debug(`Port forwarding cleanup failed: ${error.message}`);
93
- });
94
-
95
- // Stop Virtual Device if we started it
96
- if (shouldStopVirtualDevice) {
97
- await stopVirtualDevice();
98
- }
99
-
100
- // Kill Metro
101
- await killWithAwait(metro);
102
- },
103
- };
104
- },
105
- };
106
-
107
- export default vegaPlatformAdapter;
@@ -1,16 +0,0 @@
1
- import {
2
- TestRunnerConfig,
3
- assertWebRunnerConfig,
4
- } from '@react-native-harness/config';
5
- import { PlatformAdapter } from '../platform-adapter.js';
6
-
7
- export const webPlatformAdapter: PlatformAdapter = {
8
- name: 'web',
9
- getEnvironment: async (runner: TestRunnerConfig) => {
10
- assertWebRunnerConfig(runner);
11
-
12
- throw new Error('Web platform is currently disabled.');
13
- },
14
- };
15
-
16
- export default webPlatformAdapter;
package/src/process.ts DELETED
@@ -1,33 +0,0 @@
1
- import { type ChildProcess } from 'node:child_process';
2
-
3
- export const killWithAwait = (child: ChildProcess): Promise<void> => {
4
- return new Promise((resolve) => {
5
- const timeout = setTimeout(() => {
6
- child.kill('SIGTERM');
7
- resolve();
8
- }, 10000);
9
-
10
- if (child.killed || child.exitCode !== null) {
11
- clearTimeout(timeout);
12
- resolve();
13
- return;
14
- }
15
-
16
- child.on('exit', () => {
17
- clearTimeout(timeout);
18
- resolve();
19
- });
20
-
21
- child.on('error', () => {
22
- clearTimeout(timeout);
23
- resolve();
24
- });
25
-
26
- try {
27
- child.kill();
28
- } catch {
29
- clearTimeout(timeout);
30
- resolve();
31
- }
32
- });
33
- };