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

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 (123) 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 +47 -24
  4. package/dist/commands/test.d.ts +3 -0
  5. package/dist/commands/test.d.ts.map +1 -0
  6. package/dist/commands/test.js +140 -0
  7. package/dist/discovery/index.d.ts +3 -0
  8. package/dist/discovery/index.d.ts.map +1 -0
  9. package/dist/discovery/index.js +1 -0
  10. package/dist/discovery/testDiscovery.d.ts +11 -0
  11. package/dist/discovery/testDiscovery.d.ts.map +1 -0
  12. package/dist/discovery/testDiscovery.js +29 -0
  13. package/dist/errors/appNotInstalledError.d.ts +7 -0
  14. package/dist/errors/appNotInstalledError.d.ts.map +1 -0
  15. package/dist/errors/appNotInstalledError.js +12 -0
  16. package/dist/errors/bridgeTimeoutError.d.ts +7 -0
  17. package/dist/errors/bridgeTimeoutError.d.ts.map +1 -0
  18. package/dist/errors/bridgeTimeoutError.js +12 -0
  19. package/dist/errors/errorHandler.d.ts +2 -0
  20. package/dist/errors/errorHandler.d.ts.map +1 -0
  21. package/dist/errors/errorHandler.js +152 -0
  22. package/dist/errors/errors.d.ts +45 -0
  23. package/dist/errors/errors.d.ts.map +1 -0
  24. package/dist/errors/errors.js +89 -0
  25. package/dist/index.js +21 -4
  26. package/dist/jest.d.ts +2 -0
  27. package/dist/jest.d.ts.map +1 -0
  28. package/dist/jest.js +7 -0
  29. package/dist/platforms/android/build.d.ts +3 -2
  30. package/dist/platforms/android/build.d.ts.map +1 -1
  31. package/dist/platforms/android/build.js +24 -49
  32. package/dist/platforms/android/device.d.ts +5 -0
  33. package/dist/platforms/android/device.d.ts.map +1 -0
  34. package/dist/platforms/android/device.js +36 -0
  35. package/dist/platforms/android/emulator.d.ts +0 -1
  36. package/dist/platforms/android/emulator.d.ts.map +1 -1
  37. package/dist/platforms/android/emulator.js +101 -132
  38. package/dist/platforms/android/index.d.ts.map +1 -1
  39. package/dist/platforms/android/index.js +23 -19
  40. package/dist/platforms/ios/build.d.ts +5 -7
  41. package/dist/platforms/ios/build.d.ts.map +1 -1
  42. package/dist/platforms/ios/build.js +41 -116
  43. package/dist/platforms/ios/device.d.ts +11 -0
  44. package/dist/platforms/ios/device.d.ts.map +1 -0
  45. package/dist/platforms/ios/device.js +51 -0
  46. package/dist/platforms/ios/index.d.ts.map +1 -1
  47. package/dist/platforms/ios/index.js +24 -32
  48. package/dist/platforms/ios/simulator.d.ts +4 -4
  49. package/dist/platforms/ios/simulator.d.ts.map +1 -1
  50. package/dist/platforms/ios/simulator.js +104 -131
  51. package/dist/platforms/platform-adapter.d.ts +2 -4
  52. package/dist/platforms/platform-adapter.d.ts.map +1 -1
  53. package/dist/platforms/platform-registry.d.ts.map +1 -1
  54. package/dist/platforms/platform-registry.js +5 -1
  55. package/dist/platforms/vega/build.d.ts +23 -0
  56. package/dist/platforms/vega/build.d.ts.map +1 -0
  57. package/dist/platforms/vega/build.js +55 -0
  58. package/dist/platforms/vega/device.d.ts +57 -0
  59. package/dist/platforms/vega/device.d.ts.map +1 -0
  60. package/dist/platforms/vega/device.js +206 -0
  61. package/dist/platforms/vega/index.d.ts +4 -0
  62. package/dist/platforms/vega/index.d.ts.map +1 -0
  63. package/dist/platforms/vega/index.js +75 -0
  64. package/dist/platforms/web/index.d.ts +3 -1
  65. package/dist/platforms/web/index.d.ts.map +1 -1
  66. package/dist/platforms/web/index.js +9 -50
  67. package/dist/process.js +1 -1
  68. package/dist/reporters/default-reporter.d.ts.map +1 -1
  69. package/dist/reporters/default-reporter.js +22 -17
  70. package/dist/reporters/junit-reporter.d.ts +3 -0
  71. package/dist/reporters/junit-reporter.d.ts.map +1 -0
  72. package/dist/reporters/junit-reporter.js +119 -0
  73. package/dist/reporters/live-reporter.d.ts +20 -0
  74. package/dist/reporters/live-reporter.d.ts.map +1 -0
  75. package/dist/reporters/live-reporter.js +176 -0
  76. package/dist/src/reporters/default-reporter.js +135 -0
  77. package/dist/test-reporter-demo.js +95 -0
  78. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  79. package/dist/utils/status-formatter.d.ts +27 -0
  80. package/dist/utils/status-formatter.d.ts.map +1 -0
  81. package/dist/utils/status-formatter.js +54 -0
  82. package/dist/utils.d.ts +6 -0
  83. package/dist/utils.d.ts.map +1 -0
  84. package/dist/utils.js +26 -0
  85. package/dist/wizard/bundleId.d.ts +2 -0
  86. package/dist/wizard/bundleId.d.ts.map +1 -0
  87. package/dist/wizard/bundleId.js +68 -0
  88. package/dist/wizard/configGenerator.d.ts +4 -0
  89. package/dist/wizard/configGenerator.d.ts.map +1 -0
  90. package/dist/wizard/configGenerator.js +89 -0
  91. package/dist/wizard/index.d.ts +2 -0
  92. package/dist/wizard/index.d.ts.map +1 -0
  93. package/dist/wizard/index.js +40 -0
  94. package/dist/wizard/jestConfig.d.ts +2 -0
  95. package/dist/wizard/jestConfig.d.ts.map +1 -0
  96. package/dist/wizard/jestConfig.js +22 -0
  97. package/dist/wizard/jestIntegration.d.ts +2 -0
  98. package/dist/wizard/jestIntegration.d.ts.map +1 -0
  99. package/dist/wizard/jestIntegration.js +15 -0
  100. package/dist/wizard/packageManager.d.ts +2 -0
  101. package/dist/wizard/packageManager.d.ts.map +1 -0
  102. package/dist/wizard/packageManager.js +10 -0
  103. package/dist/wizard/platforms.d.ts +2 -0
  104. package/dist/wizard/platforms.d.ts.map +1 -0
  105. package/dist/wizard/platforms.js +34 -0
  106. package/dist/wizard/projectType.d.ts +6 -0
  107. package/dist/wizard/projectType.d.ts.map +1 -0
  108. package/dist/wizard/projectType.js +63 -0
  109. package/dist/wizard/targets.d.ts +3 -0
  110. package/dist/wizard/targets.d.ts.map +1 -0
  111. package/dist/wizard/targets.js +62 -0
  112. package/eslint.config.mjs +1 -1
  113. package/package.json +9 -4
  114. package/src/index.ts +33 -4
  115. package/src/wizard/bundleId.ts +70 -0
  116. package/src/wizard/configGenerator.ts +139 -0
  117. package/src/wizard/index.ts +72 -0
  118. package/src/wizard/jestConfig.ts +28 -0
  119. package/src/wizard/platforms.ts +44 -0
  120. package/src/wizard/projectType.ts +77 -0
  121. package/src/wizard/targets.ts +78 -0
  122. package/tsconfig.json +15 -0
  123. package/tsconfig.lib.json +15 -0
@@ -0,0 +1,89 @@
1
+ export class NoRunnerSpecifiedError extends Error {
2
+ constructor(availableRunners) {
3
+ super('No runner specified');
4
+ this.name = 'NoRunnerSpecifiedError';
5
+ this.availableRunners = availableRunners;
6
+ }
7
+ availableRunners;
8
+ }
9
+ export class RunnerNotFoundError extends Error {
10
+ constructor(runnerName, availableRunners) {
11
+ super(`Runner "${runnerName}" not found`);
12
+ this.name = 'RunnerNotFoundError';
13
+ this.runnerName = runnerName;
14
+ this.availableRunners = availableRunners;
15
+ }
16
+ runnerName;
17
+ availableRunners;
18
+ }
19
+ export class EnvironmentInitializationError extends Error {
20
+ constructor(message, runnerName, platform, details) {
21
+ super(`Failed to initialize environment for "${runnerName}" (${platform}): ${message}`);
22
+ this.name = 'EnvironmentInitializationError';
23
+ this.runnerName = runnerName;
24
+ this.platform = platform;
25
+ this.details = details;
26
+ }
27
+ runnerName;
28
+ platform;
29
+ details;
30
+ }
31
+ export class TestExecutionError extends Error {
32
+ constructor(testFile, error, testSuite, testName) {
33
+ const errorMessage = error instanceof Error ? error.message : String(error);
34
+ const suiteInfo = testSuite ? ` in suite "${testSuite}"` : '';
35
+ const testInfo = testName ? ` test "${testName}"` : '';
36
+ super(`Test execution failed for ${testFile}${suiteInfo}${testInfo}: ${errorMessage}`);
37
+ this.name = 'TestExecutionError';
38
+ this.testFile = testFile;
39
+ this.testSuite = testSuite;
40
+ this.testName = testName;
41
+ this.originalError = error;
42
+ }
43
+ testFile;
44
+ testSuite;
45
+ testName;
46
+ originalError;
47
+ }
48
+ export class RpcClientError extends Error {
49
+ constructor(message, bridgePort, connectionStatus) {
50
+ const portInfo = bridgePort ? ` (port ${bridgePort})` : '';
51
+ const statusInfo = connectionStatus ? ` - Status: ${connectionStatus}` : '';
52
+ super(`RPC client error${portInfo}: ${message}${statusInfo}`);
53
+ this.name = 'RpcClientError';
54
+ this.bridgePort = bridgePort;
55
+ this.connectionStatus = connectionStatus;
56
+ }
57
+ bridgePort;
58
+ connectionStatus;
59
+ }
60
+ export class BridgeTimeoutError extends Error {
61
+ timeout;
62
+ runnerName;
63
+ platform;
64
+ constructor(timeout, runnerName, platform) {
65
+ super(`Bridge connection timed out after ${timeout}ms while waiting for "${runnerName}" (${platform}) runner to be ready`);
66
+ this.timeout = timeout;
67
+ this.runnerName = runnerName;
68
+ this.platform = platform;
69
+ this.name = 'BridgeTimeoutError';
70
+ }
71
+ }
72
+ export class BundlingFailedError extends Error {
73
+ modulePath;
74
+ reason;
75
+ constructor(modulePath, reason) {
76
+ super(`Bundling of ${modulePath} failed: ${reason}`);
77
+ this.modulePath = modulePath;
78
+ this.reason = reason;
79
+ this.name = 'BundlingFailedError';
80
+ }
81
+ }
82
+ export class MetroPortUnavailableError extends Error {
83
+ port;
84
+ constructor(port) {
85
+ super(`Metro port ${port} is not available`);
86
+ this.port = port;
87
+ this.name = 'MetroPortUnavailableError';
88
+ }
89
+ }
package/dist/index.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import { run, yargsOptions } from 'jest-cli';
2
2
  import { getConfig } from '@react-native-harness/config';
3
+ import { runInitWizard } from './wizard/index.js';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ const JEST_CONFIG_EXTENSIONS = ['.mjs', '.js', '.cjs'];
7
+ const JEST_HARNESS_CONFIG_BASE = 'jest.harness.config';
3
8
  const checkForOldConfig = async () => {
4
9
  try {
5
10
  const { config } = await getConfig(process.cwd());
6
11
  if (config.include) {
7
- console.error('\n❌ Migration Required\n');
12
+ console.error('\n❌ Migration required\n');
8
13
  console.error('React Native Harness has migrated to the Jest CLI.');
9
14
  console.error('The "include" property in your rn-harness.config file is no longer supported.\n');
10
15
  console.error('Please follow the migration guide to update your configuration:');
@@ -19,7 +24,7 @@ const checkForOldConfig = async () => {
19
24
  const patchYargsOptions = () => {
20
25
  yargsOptions.harnessRunner = {
21
26
  type: 'string',
22
- description: 'Specify which Harness runner to use',
27
+ description: 'Specify which harness runner to use',
23
28
  requiresArg: true,
24
29
  };
25
30
  // Remove all options that are not supported by Harness
@@ -57,5 +62,17 @@ const patchYargsOptions = () => {
57
62
  delete yargsOptions.coverageProvider;
58
63
  delete yargsOptions.logHeapUsage;
59
64
  };
60
- patchYargsOptions();
61
- checkForOldConfig().then(() => run());
65
+ if (process.argv.includes('init')) {
66
+ runInitWizard();
67
+ }
68
+ else {
69
+ patchYargsOptions();
70
+ const hasConfigArg = process.argv.includes('--config') || process.argv.includes('-c');
71
+ if (!hasConfigArg) {
72
+ const existingConfigExt = JEST_CONFIG_EXTENSIONS.find((ext) => fs.existsSync(path.join(process.cwd(), `${JEST_HARNESS_CONFIG_BASE}${ext}`)));
73
+ if (existingConfigExt) {
74
+ process.argv.push('--config', `${JEST_HARNESS_CONFIG_BASE}${existingConfigExt}`);
75
+ }
76
+ }
77
+ checkForOldConfig().then(() => run());
78
+ }
package/dist/jest.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.d.ts","sourceRoot":"","sources":["../src/jest.ts"],"names":[],"mappings":""}
package/dist/jest.js ADDED
@@ -0,0 +1,7 @@
1
+ import { run, yargsOptions } from 'jest-cli';
2
+ yargsOptions.harnessRunner = {
3
+ type: 'string',
4
+ description: 'Specify which Harness runner to use',
5
+ requiresArg: true,
6
+ };
7
+ run();
@@ -1,4 +1,5 @@
1
- export declare const buildAndroidApp: (deviceId: string, maxRetries?: number) => Promise<void>;
1
+ export declare const buildAndroidApp: () => Promise<void>;
2
+ export declare const installApp: (deviceId: string) => Promise<void>;
2
3
  export declare const killApp: (deviceId: string, bundleId: string) => Promise<void>;
3
- export declare const runApp: (deviceId: string, bundleId: string) => Promise<void>;
4
+ export declare const runApp: (deviceId: string, bundleId: string, activityName: string) => Promise<void>;
4
5
  //# 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,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
+ {"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,54 +1,29 @@
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
- });
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
+ ]);
31
14
  };
32
15
  export const killApp = async (deviceId, 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
- });
16
+ await spawn('adb', ['-s', deviceId, 'shell', 'am', 'force-stop', bundleId]);
42
17
  };
43
- export const runApp = async (deviceId, bundleId) => {
18
+ export const runApp = async (deviceId, bundleId, activityName) => {
44
19
  await killApp(deviceId, bundleId);
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
- });
20
+ await spawn('adb', [
21
+ '-s',
22
+ deviceId,
23
+ 'shell',
24
+ 'am',
25
+ 'start',
26
+ '-n',
27
+ `${bundleId}/${activityName}`,
28
+ ]);
54
29
  };
@@ -0,0 +1,5 @@
1
+ export declare const killApp: (deviceId: string, bundleId: string) => Promise<void>;
2
+ export declare const runApp: (deviceId: string, bundleId: string) => Promise<void>;
3
+ export declare const isAppInstalled: (deviceId: string, bundleId: string) => Promise<boolean>;
4
+ export declare const reversePort: (port: number) => Promise<void>;
5
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/device.ts"],"names":[],"mappings":"AAEA,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,KACf,OAAO,CAAC,IAAI,CAWd,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAejB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAE5D,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { spawn } from '@react-native-harness/tools';
2
+ export const killApp = async (deviceId, bundleId) => {
3
+ await spawn('adb', ['-s', deviceId, 'shell', 'am', 'force-stop', bundleId]);
4
+ };
5
+ export const runApp = async (deviceId, bundleId) => {
6
+ await killApp(deviceId, bundleId);
7
+ await spawn('adb', [
8
+ '-s',
9
+ deviceId,
10
+ 'shell',
11
+ 'am',
12
+ 'start',
13
+ '-n',
14
+ `${bundleId}/.MainActivity`,
15
+ ]);
16
+ };
17
+ export const isAppInstalled = async (deviceId, bundleId) => {
18
+ try {
19
+ const { stdout } = await spawn('adb', [
20
+ '-s',
21
+ deviceId,
22
+ 'shell',
23
+ 'pm',
24
+ 'list',
25
+ 'packages',
26
+ bundleId,
27
+ ]);
28
+ return stdout.trim() !== '';
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ };
34
+ export const reversePort = async (port) => {
35
+ await spawn('adb', ['reverse', `tcp:${port}`, `tcp:${port}`]);
36
+ };
@@ -7,5 +7,4 @@ 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>;
11
10
  //# sourceMappingURL=emulator.d.ts.map
@@ -1 +1 @@
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
+ {"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,147 +1,116 @@
1
- import { exec } from 'node:child_process';
1
+ import { spawn } from '@react-native-harness/tools';
2
2
  export const getEmulatorNameFromId = async (emulatorId) => {
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
- });
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
+ }
13
17
  };
14
18
  export const getEmulatorDeviceId = async (avdName) => {
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
- }
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;
31
29
  }
32
30
  }
33
- resolve(null);
34
- });
35
- });
31
+ }
32
+ return null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
36
37
  };
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
- });
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
+ }
55
58
  };
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
- });
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;
88
84
  };
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
- });
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);
100
92
  };
101
- const stopEmulatorById = (emulatorId, resolve, reject) => {
93
+ const stopEmulatorById = async (emulatorId) => {
102
94
  // Stop the emulator using the found ID
103
- exec(`adb -s ${emulatorId} emu kill`, (killError) => {
104
- if (killError) {
105
- reject(killError);
106
- return;
107
- }
108
- resolve();
109
- });
95
+ await spawn('adb', ['-s', emulatorId, 'emu', 'kill']);
110
96
  };
111
97
  export const isAppInstalled = async (emulatorId, bundleId) => {
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
- });
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
+ }
122
113
  };
123
114
  export const reversePort = async (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
- });
115
+ await spawn('adb', ['reverse', `tcp:${port}`, `tcp:${port}`]);
147
116
  };
@@ -1 +1 @@
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
+ {"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,21 +1,24 @@
1
- import { getAppiumInteractionEngine } from '@react-native-harness/interaction-engine';
2
- import { assertNativeRunner } from '@react-native-harness/config';
1
+ import { assertAndroidRunnerConfig, } from '@react-native-harness/config';
2
+ import { logger } from '@react-native-harness/tools';
3
3
  import { runEmulator, getEmulatorDeviceId, reversePort, isAppInstalled, getEmulatorStatus, } from './emulator.js';
4
- import { buildAndroidApp, runApp, killApp } from './build.js';
4
+ import { 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';
7
8
  const androidPlatformAdapter = {
8
9
  name: 'android',
9
- getEnvironment: async (config) => {
10
- assertNativeRunner(config);
10
+ getEnvironment: async (runner) => {
11
+ assertAndroidRunnerConfig(runner);
11
12
  let emulator = null;
12
- const emulatorStatus = await getEmulatorStatus(config.runner.deviceId);
13
+ const emulatorStatus = await getEmulatorStatus(runner.deviceId);
14
+ logger.debug(`Emulator status: ${emulatorStatus}`);
13
15
  const metroPromise = runMetro();
14
16
  if (emulatorStatus === 'stopped') {
15
- emulator = await runEmulator(config.runner.deviceId);
17
+ logger.debug(`Emulator ${runner.deviceId} is stopped, starting it`);
18
+ emulator = await runEmulator(runner.deviceId);
16
19
  }
17
- const interactionEnginePromise = getAppiumInteractionEngine(config);
18
- const deviceId = await getEmulatorDeviceId(config.runner.deviceId);
20
+ const deviceId = await getEmulatorDeviceId(runner.deviceId);
21
+ logger.debug(`Device ID: ${deviceId}`);
19
22
  if (!deviceId) {
20
23
  throw new Error('Emulator not found');
21
24
  }
@@ -24,28 +27,29 @@ const androidPlatformAdapter = {
24
27
  reversePort(8080),
25
28
  reversePort(3001),
26
29
  ]);
27
- const isInstalled = await isAppInstalled(deviceId, config.runner.bundleId);
30
+ logger.debug('Ports reversed');
31
+ const isInstalled = await isAppInstalled(deviceId, runner.bundleId);
32
+ logger.debug(`App is installed: ${isInstalled}`);
28
33
  if (!isInstalled) {
29
- await buildAndroidApp(deviceId);
34
+ throw new AppNotInstalledError(runner.deviceId, runner.bundleId, 'android');
30
35
  }
31
- else {
32
- await runApp(deviceId, config.runner.bundleId);
33
- }
34
- const interactionEngine = await interactionEnginePromise;
36
+ logger.debug('Waiting for Metro to start');
35
37
  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');
36
42
  return {
37
43
  restart: async () => {
38
- await runApp(config.runner.deviceId, config.runner.bundleId);
44
+ await runApp(deviceId, runner.bundleId, runner.activityName);
39
45
  },
40
46
  dispose: async () => {
47
+ await killApp(deviceId, runner.bundleId);
41
48
  if (emulator) {
42
49
  await killWithAwait(emulator);
43
50
  }
44
- await interactionEngine.close();
45
51
  await killWithAwait(metro);
46
- await killApp(deviceId, config.runner.bundleId);
47
52
  },
48
- interactionEngine,
49
53
  };
50
54
  },
51
55
  };