@react-native-harness/cli 1.0.0-canary.1764675030942 → 1.0.0-canary.1766225407244

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 (84) hide show
  1. package/dist/bundlers/metro.d.ts +2 -2
  2. package/dist/bundlers/metro.d.ts.map +1 -1
  3. package/dist/bundlers/metro.js +24 -47
  4. package/dist/platforms/android/build.d.ts +2 -3
  5. package/dist/platforms/android/build.d.ts.map +1 -1
  6. package/dist/platforms/android/build.js +49 -24
  7. package/dist/platforms/android/emulator.d.ts +1 -0
  8. package/dist/platforms/android/emulator.d.ts.map +1 -1
  9. package/dist/platforms/android/emulator.js +132 -101
  10. package/dist/platforms/android/index.d.ts.map +1 -1
  11. package/dist/platforms/android/index.js +19 -23
  12. package/dist/platforms/ios/build.d.ts +7 -5
  13. package/dist/platforms/ios/build.d.ts.map +1 -1
  14. package/dist/platforms/ios/build.js +116 -41
  15. package/dist/platforms/ios/index.d.ts.map +1 -1
  16. package/dist/platforms/ios/index.js +32 -24
  17. package/dist/platforms/ios/simulator.d.ts +4 -4
  18. package/dist/platforms/ios/simulator.d.ts.map +1 -1
  19. package/dist/platforms/ios/simulator.js +131 -104
  20. package/dist/platforms/platform-adapter.d.ts +4 -2
  21. package/dist/platforms/platform-adapter.d.ts.map +1 -1
  22. package/dist/platforms/platform-registry.d.ts.map +1 -1
  23. package/dist/platforms/platform-registry.js +1 -5
  24. package/dist/platforms/web/index.d.ts +1 -3
  25. package/dist/platforms/web/index.d.ts.map +1 -1
  26. package/dist/platforms/web/index.js +50 -9
  27. package/dist/process.js +1 -1
  28. package/dist/reporters/default-reporter.d.ts.map +1 -1
  29. package/dist/reporters/default-reporter.js +17 -22
  30. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  31. package/package.json +3 -3
  32. package/dist/commands/test.d.ts +0 -3
  33. package/dist/commands/test.d.ts.map +0 -1
  34. package/dist/commands/test.js +0 -140
  35. package/dist/discovery/index.d.ts +0 -3
  36. package/dist/discovery/index.d.ts.map +0 -1
  37. package/dist/discovery/index.js +0 -1
  38. package/dist/discovery/testDiscovery.d.ts +0 -11
  39. package/dist/discovery/testDiscovery.d.ts.map +0 -1
  40. package/dist/discovery/testDiscovery.js +0 -29
  41. package/dist/errors/appNotInstalledError.d.ts +0 -7
  42. package/dist/errors/appNotInstalledError.d.ts.map +0 -1
  43. package/dist/errors/appNotInstalledError.js +0 -12
  44. package/dist/errors/bridgeTimeoutError.d.ts +0 -7
  45. package/dist/errors/bridgeTimeoutError.d.ts.map +0 -1
  46. package/dist/errors/bridgeTimeoutError.js +0 -12
  47. package/dist/errors/errorHandler.d.ts +0 -2
  48. package/dist/errors/errorHandler.d.ts.map +0 -1
  49. package/dist/errors/errorHandler.js +0 -152
  50. package/dist/errors/errors.d.ts +0 -45
  51. package/dist/errors/errors.d.ts.map +0 -1
  52. package/dist/errors/errors.js +0 -89
  53. package/dist/jest.d.ts +0 -2
  54. package/dist/jest.d.ts.map +0 -1
  55. package/dist/jest.js +0 -7
  56. package/dist/platforms/android/device.d.ts +0 -5
  57. package/dist/platforms/android/device.d.ts.map +0 -1
  58. package/dist/platforms/android/device.js +0 -36
  59. package/dist/platforms/ios/device.d.ts +0 -11
  60. package/dist/platforms/ios/device.d.ts.map +0 -1
  61. package/dist/platforms/ios/device.js +0 -51
  62. package/dist/platforms/vega/build.d.ts +0 -23
  63. package/dist/platforms/vega/build.d.ts.map +0 -1
  64. package/dist/platforms/vega/build.js +0 -55
  65. package/dist/platforms/vega/device.d.ts +0 -57
  66. package/dist/platforms/vega/device.d.ts.map +0 -1
  67. package/dist/platforms/vega/device.js +0 -206
  68. package/dist/platforms/vega/index.d.ts +0 -4
  69. package/dist/platforms/vega/index.d.ts.map +0 -1
  70. package/dist/platforms/vega/index.js +0 -75
  71. package/dist/reporters/junit-reporter.d.ts +0 -3
  72. package/dist/reporters/junit-reporter.d.ts.map +0 -1
  73. package/dist/reporters/junit-reporter.js +0 -119
  74. package/dist/reporters/live-reporter.d.ts +0 -20
  75. package/dist/reporters/live-reporter.d.ts.map +0 -1
  76. package/dist/reporters/live-reporter.js +0 -176
  77. package/dist/src/reporters/default-reporter.js +0 -135
  78. package/dist/test-reporter-demo.js +0 -95
  79. package/dist/utils/status-formatter.d.ts +0 -27
  80. package/dist/utils/status-formatter.d.ts.map +0 -1
  81. package/dist/utils/status-formatter.js +0 -54
  82. package/dist/utils.d.ts +0 -6
  83. package/dist/utils.d.ts.map +0 -1
  84. package/dist/utils.js +0 -26
@@ -1,5 +1,5 @@
1
1
  import { type ChildProcess } from 'node:child_process';
2
- export declare const runMetro: (isExpo?: boolean) => Promise<ChildProcess>;
2
+ export declare const runMetro: () => Promise<ChildProcess>;
3
3
  export declare const waitForMetro: (port?: number, maxRetries?: number, retryDelay?: number) => Promise<void>;
4
- export declare const reloadApp: () => Promise<void>;
4
+ export declare const reloadApp: (port?: number) => Promise<void>;
5
5
  //# sourceMappingURL=metro.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metro.d.ts","sourceRoot":"","sources":["../../src/bundlers/metro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAcvD,eAAO,MAAM,QAAQ,GAAU,gBAAc,KAAG,OAAO,CAAC,YAAY,CAiDnE,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,aAAW,EACX,mBAAe,EACf,mBAAiB,KAChB,OAAO,CAAC,IAAI,CA4Bd,CAAC;AAEF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,IAAI,CAE9C,CAAC"}
1
+ {"version":3,"file":"metro.d.ts","sourceRoot":"","sources":["../../src/bundlers/metro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAQ9D,eAAO,MAAM,QAAQ,QAAa,OAAO,CAAC,YAAY,CAmBrD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,OAAM,MAAa,EACnB,aAAY,MAAW,EACvB,aAAY,MAAa,KACxB,OAAO,CAAC,IAAI,CA0Bd,CAAC;AAEF,eAAO,MAAM,SAAS,GAAU,aAAW,KAAG,OAAO,CAAC,IAAI,CAEzD,CAAC"}
@@ -1,48 +1,27 @@
1
- import { getReactNativeCliPath, getExpoCliPath, getTimeoutSignal, spawn, logger, SubprocessError, } from '@react-native-harness/tools';
2
- import { isPortAvailable } from '../utils.js';
3
- import { MetroPortUnavailableError } from '../errors/errors.js';
4
- const METRO_PORT = 8081;
5
- export const runMetro = async (isExpo = false) => {
6
- const metro = spawn('node', [
7
- isExpo ? getExpoCliPath() : getReactNativeCliPath(),
8
- 'start',
9
- '--port',
10
- METRO_PORT.toString(),
11
- ], {
12
- env: {
13
- ...process.env,
14
- RN_HARNESS: 'true',
15
- ...(isExpo && { EXPO_NO_METRO_WORKSPACE_ROOT: 'true' }),
16
- },
17
- });
18
- // Forward metro output to logger
19
- metro.nodeChildProcess.then((childProcess) => {
20
- if (childProcess.stdout) {
21
- childProcess.stdout.on('data', (data) => {
22
- logger.debug(data.toString().trim());
23
- });
24
- }
25
- if (childProcess.stderr) {
26
- childProcess.stderr.on('data', (data) => {
27
- logger.debug(data.toString().trim());
28
- });
29
- }
30
- });
31
- metro.catch((error) => {
32
- // This process is going to be killed by us, so we don't need to throw an error
33
- if (error instanceof SubprocessError && error.signalName === 'SIGTERM') {
34
- return;
35
- }
36
- logger.error('Metro crashed unexpectedly', error);
1
+ import { spawn } from 'node:child_process';
2
+ const getTimeoutSignal = (timeout) => {
3
+ const controller = new AbortController();
4
+ setTimeout(() => controller.abort(), timeout);
5
+ return controller.signal;
6
+ };
7
+ export const runMetro = async () => {
8
+ const metro = await new Promise((resolve, reject) => {
9
+ const metroProcess = spawn('npx', ['react-native', 'start'], {
10
+ stdio: 'ignore',
11
+ env: {
12
+ ...process.env,
13
+ RN_HARNESS: 'true',
14
+ },
15
+ });
16
+ metroProcess.on('error', (error) => {
17
+ reject(new Error(`Failed to start Metro: ${error.message}`));
18
+ });
19
+ resolve(metroProcess);
37
20
  });
38
- const isDefaultPortAvailable = await isPortAvailable(METRO_PORT);
39
- if (!isDefaultPortAvailable) {
40
- throw new MetroPortUnavailableError(METRO_PORT);
41
- }
42
21
  await waitForMetro();
43
- return metro.nodeChildProcess;
22
+ return metro;
44
23
  };
45
- export const waitForMetro = async (port = 8081, maxRetries = 20, retryDelay = 1000) => {
24
+ export const waitForMetro = async (port = 8081, maxRetries = 10, retryDelay = 1000) => {
46
25
  let attempts = 0;
47
26
  while (attempts < maxRetries) {
48
27
  attempts++;
@@ -57,15 +36,13 @@ export const waitForMetro = async (port = 8081, maxRetries = 20, retryDelay = 10
57
36
  }
58
37
  }
59
38
  }
60
- catch {
61
- // Errors are expected here, we're just waiting for the process to be ready
62
- }
39
+ catch { }
63
40
  if (attempts < maxRetries) {
64
41
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
65
42
  }
66
43
  }
67
44
  throw new Error(`Metro bundler is not ready after ${maxRetries} attempts`);
68
45
  };
69
- export const reloadApp = async () => {
70
- await fetch(`http://localhost:${METRO_PORT}/reload`);
46
+ export const reloadApp = async (port = 8081) => {
47
+ await fetch(`http://localhost:${port}/reload`);
71
48
  };
@@ -1,5 +1,4 @@
1
- export declare const buildAndroidApp: () => Promise<void>;
2
- export declare const installApp: (deviceId: string) => Promise<void>;
1
+ export declare const buildAndroidApp: (deviceId: string, maxRetries?: number) => Promise<void>;
3
2
  export declare const killApp: (deviceId: string, bundleId: string) => Promise<void>;
4
- export declare const runApp: (deviceId: string, bundleId: string, activityName: string) => Promise<void>;
3
+ export declare const runApp: (deviceId: string, bundleId: string) => Promise<void>;
5
4
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/build.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,IAAI,CAEpD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAiB/D,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,UAAU,MAAM,EAChB,UAAU,MAAM,EAChB,cAAc,MAAM,KACnB,OAAO,CAAC,IAAI,CAWd,CAAC"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/build.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,mBAAc,KACb,OAAO,CAAC,IAAI,CAiCd,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAWd,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAgBd,CAAC"}
@@ -1,29 +1,54 @@
1
- import path from 'node:path';
2
- import { spawn } from '@react-native-harness/tools';
3
- export const buildAndroidApp = async () => {
4
- await spawn('react-native', ['build-android', '--tasks', 'assembleDebug']);
5
- };
6
- export const installApp = async (deviceId) => {
7
- await spawn('adb', [
8
- '-s',
9
- deviceId,
10
- 'install',
11
- '-r',
12
- path.join(process.cwd(), 'android', 'app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk'),
13
- ]);
1
+ import { exec } from 'node:child_process';
2
+ import { reloadApp } from '../../bundlers/metro.js';
3
+ export const buildAndroidApp = async (deviceId, maxRetries = 2) => {
4
+ return new Promise((resolve, reject) => {
5
+ let attempts = 0;
6
+ const attemptBuild = () => {
7
+ attempts++;
8
+ exec(`react-native run-android --deviceId=${deviceId} --no-packager`, async (error) => {
9
+ if (error) {
10
+ if (attempts <= maxRetries) {
11
+ try {
12
+ await reloadApp(8081);
13
+ // Wait a bit before retrying
14
+ setTimeout(attemptBuild, 3000);
15
+ }
16
+ catch (reloadError) {
17
+ console.warn('Reload failed, retrying build anyway...');
18
+ setTimeout(attemptBuild, 3000);
19
+ }
20
+ }
21
+ else {
22
+ reject(error);
23
+ }
24
+ return;
25
+ }
26
+ resolve();
27
+ });
28
+ };
29
+ attemptBuild();
30
+ });
14
31
  };
15
32
  export const killApp = async (deviceId, bundleId) => {
16
- await spawn('adb', ['-s', deviceId, 'shell', 'am', 'force-stop', bundleId]);
33
+ return new Promise((resolve, reject) => {
34
+ exec(`adb -s ${deviceId} shell am force-stop ${bundleId}`, (error) => {
35
+ if (error) {
36
+ reject(error);
37
+ return;
38
+ }
39
+ resolve();
40
+ });
41
+ });
17
42
  };
18
- export const runApp = async (deviceId, bundleId, activityName) => {
43
+ export const runApp = async (deviceId, bundleId) => {
19
44
  await killApp(deviceId, bundleId);
20
- await spawn('adb', [
21
- '-s',
22
- deviceId,
23
- 'shell',
24
- 'am',
25
- 'start',
26
- '-n',
27
- `${bundleId}/${activityName}`,
28
- ]);
45
+ return new Promise((resolve, reject) => {
46
+ exec(`adb -s ${deviceId} shell am start -n ${bundleId}/.MainActivity`, (error) => {
47
+ if (error) {
48
+ reject(error);
49
+ return;
50
+ }
51
+ resolve();
52
+ });
53
+ });
29
54
  };
@@ -7,4 +7,5 @@ export declare const runEmulator: (name: string) => Promise<ChildProcess>;
7
7
  export declare const stopEmulator: (avdName: string) => Promise<void>;
8
8
  export declare const isAppInstalled: (emulatorId: string, bundleId: string) => Promise<boolean>;
9
9
  export declare const reversePort: (port: number) => Promise<void>;
10
+ export declare const getEmulatorScreenshot: (emulatorId: string, name?: string) => Promise<string>;
10
11
  //# sourceMappingURL=emulator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/emulator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,KACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAcvB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAmBvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,KACd,OAAO,CAAC,qBAAqB,CAoB/B,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,YAAY,CA2BpE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAQhE,CAAC;AAOF,eAAO,MAAM,cAAc,GACzB,YAAY,MAAM,EAClB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAejB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAE5D,CAAC"}
1
+ {"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/emulator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAQ,MAAM,oBAAoB,CAAC;AAExD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,KACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAYvB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAwBvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,KACd,OAAO,CAAC,qBAAqB,CAuB/B,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,OAAO,CAAC,YAAY,CAgC9D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAY1D,CAAC;AAiBF,eAAO,MAAM,cAAc,GACzB,YAAY,MAAM,EAClB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAejB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAW5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,EAClB,OAAM,MAGsB,KAC3B,OAAO,CAAC,MAAM,CAchB,CAAC"}
@@ -1,116 +1,147 @@
1
- import { spawn } from '@react-native-harness/tools';
1
+ import { exec } from 'node:child_process';
2
2
  export const getEmulatorNameFromId = async (emulatorId) => {
3
- try {
4
- const { stdout } = await spawn('adb', [
5
- '-s',
6
- emulatorId,
7
- 'emu',
8
- 'avd',
9
- 'name',
10
- ]);
11
- const avdName = stdout.split('\n')[0].trim();
12
- return avdName || null;
13
- }
14
- catch {
15
- return null;
16
- }
3
+ return new Promise((resolve) => {
4
+ exec(`adb -s ${emulatorId} emu avd name`, (error, stdout) => {
5
+ if (error) {
6
+ resolve(null);
7
+ return;
8
+ }
9
+ const avdName = stdout.split('\n')[0].trim();
10
+ resolve(avdName || null);
11
+ });
12
+ });
17
13
  };
18
14
  export const getEmulatorDeviceId = async (avdName) => {
19
- try {
20
- const { stdout } = await spawn('adb', ['devices']);
21
- const lines = stdout.split('\n');
22
- for (const line of lines) {
23
- const parts = line.trim().split('\t');
24
- if (parts.length === 2 && parts[0].startsWith('emulator-')) {
25
- const emulatorId = parts[0].trim();
26
- const name = await getEmulatorNameFromId(emulatorId);
27
- if (name === avdName) {
28
- return emulatorId;
15
+ return new Promise((resolve) => {
16
+ exec('adb devices', async (error, stdout) => {
17
+ if (error) {
18
+ resolve(null);
19
+ return;
20
+ }
21
+ const lines = stdout.split('\n');
22
+ for (const line of lines) {
23
+ const parts = line.trim().split('\t');
24
+ if (parts.length === 2 && parts[0].startsWith('emulator-')) {
25
+ const emulatorId = parts[0].trim();
26
+ const name = await getEmulatorNameFromId(emulatorId);
27
+ if (name === avdName) {
28
+ resolve(emulatorId);
29
+ return;
30
+ }
29
31
  }
30
32
  }
31
- }
32
- return null;
33
- }
34
- catch {
35
- return null;
36
- }
33
+ resolve(null);
34
+ });
35
+ });
37
36
  };
38
- export const getEmulatorStatus = async (avdName) => {
39
- const emulatorId = await getEmulatorDeviceId(avdName);
40
- if (!emulatorId) {
41
- return 'stopped';
42
- }
43
- try {
44
- // Check if device is fully booted by checking boot completion
45
- const { stdout } = await spawn('adb', [
46
- '-s',
47
- emulatorId,
48
- 'shell',
49
- 'getprop',
50
- 'sys.boot_completed',
51
- ]);
52
- const bootCompleted = stdout.trim() === '1';
53
- return bootCompleted ? 'running' : 'loading';
54
- }
55
- catch {
56
- return 'loading';
57
- }
37
+ export const getEmulatorStatus = (avdName) => {
38
+ return new Promise((resolve) => {
39
+ getEmulatorDeviceId(avdName).then((emulatorId) => {
40
+ if (!emulatorId) {
41
+ resolve('stopped');
42
+ return;
43
+ }
44
+ // Check if device is fully booted by checking boot completion
45
+ exec(`adb -s ${emulatorId} shell getprop sys.boot_completed`, (bootError, bootStdout) => {
46
+ if (bootError) {
47
+ resolve('loading');
48
+ return;
49
+ }
50
+ const bootCompleted = bootStdout.trim() === '1';
51
+ resolve(bootCompleted ? 'running' : 'loading');
52
+ });
53
+ });
54
+ });
58
55
  };
59
- export const runEmulator = async (name) => {
60
- // Start the emulator
61
- const process = spawn('emulator', ['-avd', name]);
62
- const nodeChildProcess = await process.nodeChildProcess;
63
- // Poll for emulator status until it's fully running
64
- const checkStatus = async () => {
65
- const status = await getEmulatorStatus(name);
66
- if (status === 'running') {
67
- return;
68
- }
69
- else if (status === 'loading') {
70
- // Check again in 2 seconds
71
- await new Promise((resolve) => setTimeout(resolve, 2000));
72
- await checkStatus();
73
- }
74
- else {
75
- // Still stopped, check again in 1 second
76
- await new Promise((resolve) => setTimeout(resolve, 1000));
77
- await checkStatus();
78
- }
79
- };
80
- // Start checking status after a brief delay to allow emulator to start
81
- await new Promise((resolve) => setTimeout(resolve, 3000));
82
- await checkStatus();
83
- return nodeChildProcess;
56
+ export const runEmulator = (name) => {
57
+ return new Promise((resolve, reject) => {
58
+ // Start the emulator
59
+ const process = exec(`emulator -avd ${name}`, (error) => {
60
+ if (error) {
61
+ reject(error);
62
+ return;
63
+ }
64
+ });
65
+ // Poll for emulator status until it's fully running
66
+ const checkStatus = async () => {
67
+ try {
68
+ const status = await getEmulatorStatus(name);
69
+ if (status === 'running') {
70
+ resolve(process);
71
+ }
72
+ else if (status === 'loading') {
73
+ // Check again in 2 seconds
74
+ setTimeout(checkStatus, 2000);
75
+ }
76
+ else {
77
+ // Still stopped, check again in 1 second
78
+ setTimeout(checkStatus, 1000);
79
+ }
80
+ }
81
+ catch (error) {
82
+ reject(error);
83
+ }
84
+ };
85
+ // Start checking status after a brief delay to allow emulator to start
86
+ setTimeout(checkStatus, 3000);
87
+ });
84
88
  };
85
- export const stopEmulator = async (avdName) => {
86
- // First, get the emulator device ID
87
- const emulatorId = await getEmulatorDeviceId(avdName);
88
- if (!emulatorId) {
89
- return; // No emulator running, nothing to stop
90
- }
91
- await stopEmulatorById(emulatorId);
89
+ export const stopEmulator = (avdName) => {
90
+ return new Promise((resolve, reject) => {
91
+ // First, get the emulator device ID
92
+ getEmulatorDeviceId(avdName).then((emulatorId) => {
93
+ if (!emulatorId) {
94
+ resolve(); // No emulator running, nothing to stop
95
+ return;
96
+ }
97
+ stopEmulatorById(emulatorId, resolve, reject);
98
+ });
99
+ });
92
100
  };
93
- const stopEmulatorById = async (emulatorId) => {
101
+ const stopEmulatorById = (emulatorId, resolve, reject) => {
94
102
  // Stop the emulator using the found ID
95
- await spawn('adb', ['-s', emulatorId, 'emu', 'kill']);
103
+ exec(`adb -s ${emulatorId} emu kill`, (killError) => {
104
+ if (killError) {
105
+ reject(killError);
106
+ return;
107
+ }
108
+ resolve();
109
+ });
96
110
  };
97
111
  export const isAppInstalled = async (emulatorId, bundleId) => {
98
- try {
99
- const { stdout } = await spawn('adb', [
100
- '-s',
101
- emulatorId,
102
- 'shell',
103
- 'pm',
104
- 'list',
105
- 'packages',
106
- bundleId,
107
- ]);
108
- return stdout.trim() !== '';
109
- }
110
- catch {
111
- return false;
112
- }
112
+ return new Promise((resolve) => {
113
+ exec(`adb -s ${emulatorId} shell pm list packages ${bundleId}`, (error, stdout) => {
114
+ if (error) {
115
+ resolve(false);
116
+ return;
117
+ }
118
+ const installed = stdout.trim() !== '';
119
+ resolve(installed);
120
+ });
121
+ });
113
122
  };
114
123
  export const reversePort = async (port) => {
115
- await spawn('adb', ['reverse', `tcp:${port}`, `tcp:${port}`]);
124
+ return new Promise((resolve, reject) => {
125
+ exec(`adb reverse tcp:${port} tcp:${port}`, (error) => {
126
+ if (error) {
127
+ reject(error);
128
+ return;
129
+ }
130
+ resolve();
131
+ });
132
+ });
133
+ };
134
+ export const getEmulatorScreenshot = async (emulatorId, name = `${emulatorId}-${new Date()
135
+ .toISOString()
136
+ .replace(/:/g, '-')
137
+ .replace(/\//g, '-')}.png`) => {
138
+ return new Promise((resolve, reject) => {
139
+ exec(`adb -s ${emulatorId} exec-out screencap -p > ${name}`, (error) => {
140
+ if (error) {
141
+ reject(error);
142
+ return;
143
+ }
144
+ resolve(name);
145
+ });
146
+ });
116
147
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAa9D,QAAA,MAAM,sBAAsB,EAAE,eAgE7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAY9D,QAAA,MAAM,sBAAsB,EAAE,eAuD7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -1,24 +1,21 @@
1
- import { assertAndroidRunnerConfig, } from '@react-native-harness/config';
2
- import { logger } from '@react-native-harness/tools';
1
+ import { getAppiumInteractionEngine } from '@react-native-harness/interaction-engine';
2
+ import { assertNativeRunner } from '@react-native-harness/config';
3
3
  import { runEmulator, getEmulatorDeviceId, reversePort, isAppInstalled, getEmulatorStatus, } from './emulator.js';
4
- import { runApp, killApp } from './build.js';
4
+ import { buildAndroidApp, runApp, killApp } from './build.js';
5
5
  import { killWithAwait } from '../../process.js';
6
6
  import { runMetro } from '../../bundlers/metro.js';
7
- import { AppNotInstalledError } from '../../errors/errors.js';
8
7
  const androidPlatformAdapter = {
9
8
  name: 'android',
10
- getEnvironment: async (runner) => {
11
- assertAndroidRunnerConfig(runner);
9
+ getEnvironment: async (config) => {
10
+ assertNativeRunner(config);
12
11
  let emulator = null;
13
- const emulatorStatus = await getEmulatorStatus(runner.deviceId);
14
- logger.debug(`Emulator status: ${emulatorStatus}`);
12
+ const emulatorStatus = await getEmulatorStatus(config.runner.deviceId);
15
13
  const metroPromise = runMetro();
16
14
  if (emulatorStatus === 'stopped') {
17
- logger.debug(`Emulator ${runner.deviceId} is stopped, starting it`);
18
- emulator = await runEmulator(runner.deviceId);
15
+ emulator = await runEmulator(config.runner.deviceId);
19
16
  }
20
- const deviceId = await getEmulatorDeviceId(runner.deviceId);
21
- logger.debug(`Device ID: ${deviceId}`);
17
+ const interactionEnginePromise = getAppiumInteractionEngine(config);
18
+ const deviceId = await getEmulatorDeviceId(config.runner.deviceId);
22
19
  if (!deviceId) {
23
20
  throw new Error('Emulator not found');
24
21
  }
@@ -27,29 +24,28 @@ const androidPlatformAdapter = {
27
24
  reversePort(8080),
28
25
  reversePort(3001),
29
26
  ]);
30
- logger.debug('Ports reversed');
31
- const isInstalled = await isAppInstalled(deviceId, runner.bundleId);
32
- logger.debug(`App is installed: ${isInstalled}`);
27
+ const isInstalled = await isAppInstalled(deviceId, config.runner.bundleId);
33
28
  if (!isInstalled) {
34
- throw new AppNotInstalledError(runner.deviceId, runner.bundleId, 'android');
29
+ await buildAndroidApp(deviceId);
35
30
  }
36
- logger.debug('Waiting for Metro to start');
31
+ else {
32
+ await runApp(deviceId, config.runner.bundleId);
33
+ }
34
+ const interactionEngine = await interactionEnginePromise;
37
35
  const metro = await metroPromise;
38
- logger.debug('Metro started');
39
- logger.debug('Running app');
40
- await runApp(deviceId, runner.bundleId, runner.activityName);
41
- logger.debug('App running');
42
36
  return {
43
37
  restart: async () => {
44
- await runApp(deviceId, runner.bundleId, runner.activityName);
38
+ await runApp(config.runner.deviceId, config.runner.bundleId);
45
39
  },
46
40
  dispose: async () => {
47
- await killApp(deviceId, runner.bundleId);
48
41
  if (emulator) {
49
42
  await killWithAwait(emulator);
50
43
  }
44
+ await interactionEngine.close();
51
45
  await killWithAwait(metro);
46
+ await killApp(deviceId, config.runner.bundleId);
52
47
  },
48
+ interactionEngine,
53
49
  };
54
50
  },
55
51
  };
@@ -1,7 +1,9 @@
1
1
  export declare const listDevices: () => Promise<any>;
2
- export declare const getDeviceByName: (simulatorName: string, systemVersion: string) => Promise<any | null>;
3
- export declare const listApps: (udid: string) => Promise<string[]>;
4
- export declare const isAppInstalled: (udid: string, bundleId: string) => Promise<boolean>;
5
- export declare const runApp: (udid: string, appName: string) => Promise<void>;
6
- export declare const killApp: (udid: string, appName: string) => Promise<void>;
2
+ export declare const getDeviceByName: (simulatorName: string) => Promise<any | null>;
3
+ export declare const installPods: () => Promise<void>;
4
+ export declare const listApps: (udid: string) => Promise<string>;
5
+ export declare const isAppInstalled: (simulatorName: string, bundleId: string) => Promise<boolean>;
6
+ export declare const buildIOSApp: (simulatorName: string, maxRetries?: number) => Promise<void>;
7
+ export declare const runApp: (simulatorName: string, appName: string) => Promise<void>;
8
+ export declare const killApp: (simulatorName: string, appName: string) => Promise<void>;
7
9
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/build.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,GAAG,CAQ/C,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,eAAe,MAAM,EACrB,eAAe,MAAM,KAEpB,OAAO,CAAC,GAAG,GAAG,IAAI,CAsBpB,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAY7D,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAGjB,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAGxE,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAEzE,CAAC"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/build.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,GAAG,CAgB/C,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,eAAe,MAAM,KACpB,OAAO,CAAC,GAAG,GAAG,IAAI,CAiBpB,CAAC;AAEF,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAYhD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,CAW3D,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,eAAe,MAAM,EACrB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAajB,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,eAAe,MAAM,EACrB,aAAY,MAAU,KACrB,OAAO,CAAC,IAAI,CAoCd,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAgBd,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAMd,CAAC"}