@react-native-harness/platform-android 1.1.0-rc.2 → 1.1.0-rc.4

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 (87) hide show
  1. package/README.md +9 -2
  2. package/dist/__tests__/adb.test.js +283 -10
  3. package/dist/__tests__/avd-config.test.d.ts +2 -0
  4. package/dist/__tests__/avd-config.test.d.ts.map +1 -0
  5. package/dist/__tests__/avd-config.test.js +174 -0
  6. package/dist/__tests__/ci-action.test.d.ts +2 -0
  7. package/dist/__tests__/ci-action.test.d.ts.map +1 -0
  8. package/dist/__tests__/ci-action.test.js +46 -0
  9. package/dist/__tests__/emulator-startup.test.d.ts +2 -0
  10. package/dist/__tests__/emulator-startup.test.d.ts.map +1 -0
  11. package/dist/__tests__/emulator-startup.test.js +19 -0
  12. package/dist/__tests__/environment.test.d.ts +2 -0
  13. package/dist/__tests__/environment.test.d.ts.map +1 -0
  14. package/dist/__tests__/environment.test.js +117 -0
  15. package/dist/__tests__/instance.test.d.ts +2 -0
  16. package/dist/__tests__/instance.test.d.ts.map +1 -0
  17. package/dist/__tests__/instance.test.js +423 -0
  18. package/dist/__tests__/targets.test.d.ts +2 -0
  19. package/dist/__tests__/targets.test.d.ts.map +1 -0
  20. package/dist/__tests__/targets.test.js +49 -0
  21. package/dist/adb.d.ts +23 -0
  22. package/dist/adb.d.ts.map +1 -1
  23. package/dist/adb.js +259 -16
  24. package/dist/app-monitor.d.ts.map +1 -1
  25. package/dist/app-monitor.js +27 -7
  26. package/dist/assertions.d.ts +5 -0
  27. package/dist/assertions.d.ts.map +1 -0
  28. package/dist/assertions.js +6 -0
  29. package/dist/avd-config.d.ts +41 -0
  30. package/dist/avd-config.d.ts.map +1 -0
  31. package/dist/avd-config.js +173 -0
  32. package/dist/config.d.ts +77 -0
  33. package/dist/config.d.ts.map +1 -1
  34. package/dist/config.js +5 -0
  35. package/dist/emulator-startup.d.ts +3 -0
  36. package/dist/emulator-startup.d.ts.map +1 -0
  37. package/dist/emulator-startup.js +17 -0
  38. package/dist/emulator.d.ts +6 -0
  39. package/dist/emulator.d.ts.map +1 -0
  40. package/dist/emulator.js +27 -0
  41. package/dist/environment.d.ts +31 -0
  42. package/dist/environment.d.ts.map +1 -0
  43. package/dist/environment.js +317 -0
  44. package/dist/errors.d.ts +7 -0
  45. package/dist/errors.d.ts.map +1 -0
  46. package/dist/errors.js +14 -0
  47. package/dist/factory.d.ts.map +1 -1
  48. package/dist/factory.js +3 -0
  49. package/dist/index.d.ts +3 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +3 -0
  52. package/dist/instance.d.ts +6 -0
  53. package/dist/instance.d.ts.map +1 -0
  54. package/dist/instance.js +232 -0
  55. package/dist/reader.d.ts +6 -0
  56. package/dist/reader.d.ts.map +1 -0
  57. package/dist/reader.js +57 -0
  58. package/dist/runner.d.ts +2 -2
  59. package/dist/runner.d.ts.map +1 -1
  60. package/dist/runner.js +9 -52
  61. package/dist/targets.d.ts +1 -1
  62. package/dist/targets.d.ts.map +1 -1
  63. package/dist/targets.js +4 -0
  64. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  65. package/dist/types.d.ts +381 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +107 -0
  68. package/package.json +4 -4
  69. package/src/__tests__/adb.test.ts +419 -15
  70. package/src/__tests__/avd-config.test.ts +206 -0
  71. package/src/__tests__/ci-action.test.ts +81 -0
  72. package/src/__tests__/emulator-startup.test.ts +32 -0
  73. package/src/__tests__/environment.test.ts +212 -0
  74. package/src/__tests__/instance.test.ts +610 -0
  75. package/src/__tests__/targets.test.ts +53 -0
  76. package/src/adb.ts +430 -28
  77. package/src/app-monitor.ts +56 -18
  78. package/src/avd-config.ts +290 -0
  79. package/src/config.ts +8 -0
  80. package/src/emulator-startup.ts +28 -0
  81. package/src/environment.ts +554 -0
  82. package/src/errors.ts +19 -0
  83. package/src/factory.ts +4 -0
  84. package/src/index.ts +7 -1
  85. package/src/instance.ts +380 -0
  86. package/src/runner.ts +19 -70
  87. package/src/targets.ts +18 -8
@@ -0,0 +1,117 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { ensureAndroidAdbAvailable, ensureAndroidEmulatorAvailable, getAndroidSdkRoot, getAndroidSystemImagePackage, getDefaultUnixAndroidSdkRoot, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, } from '../environment.js';
6
+ import * as tools from '@react-native-harness/tools';
7
+ describe('Android environment', () => {
8
+ beforeEach(() => {
9
+ vi.restoreAllMocks();
10
+ vi.unstubAllEnvs();
11
+ });
12
+ it('skips bootstrapping command-line tools when adb is already installed', async () => {
13
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
14
+ const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
15
+ fs.mkdirSync(path.dirname(adbPath), { recursive: true });
16
+ fs.writeFileSync(adbPath, '');
17
+ const spawnSpy = vi.spyOn(tools, 'spawn');
18
+ await expect(ensureAndroidAdbAvailable({
19
+ env: { ANDROID_HOME: sdkRoot },
20
+ })).resolves.toBe(sdkRoot);
21
+ expect(spawnSpy).not.toHaveBeenCalled();
22
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
23
+ });
24
+ it('installs only platform-tools when adb is missing', async () => {
25
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
26
+ const sdkManagerDirectory = path.join(sdkRoot, 'cmdline-tools', 'latest', 'bin');
27
+ fs.mkdirSync(sdkManagerDirectory, { recursive: true });
28
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
29
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
30
+ const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (command, args) => {
31
+ if (command === 'bash' && typeof args?.[1] === 'string') {
32
+ const commandString = args[1];
33
+ if (commandString.includes('platform-tools')) {
34
+ const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
35
+ fs.mkdirSync(path.dirname(adbPath), { recursive: true });
36
+ fs.writeFileSync(adbPath, '');
37
+ }
38
+ }
39
+ return {};
40
+ }));
41
+ await expect(ensureAndroidAdbAvailable({
42
+ env: { ANDROID_HOME: sdkRoot },
43
+ })).resolves.toBe(sdkRoot);
44
+ expect(spawnSpy).toHaveBeenCalledWith('bash', ['-lc', expect.stringContaining('platform-tools')], expect.any(Object));
45
+ expect(spawnSpy.mock.calls.some(([command, args]) => command === 'bash' &&
46
+ typeof args?.[1] === 'string' &&
47
+ args[1].includes('platform-tools') &&
48
+ args[1].includes('emulator'))).toBe(false);
49
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
50
+ });
51
+ it('installs emulator only when emulator is missing', async () => {
52
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
53
+ const sdkManagerDirectory = path.join(sdkRoot, 'cmdline-tools', 'latest', 'bin');
54
+ fs.mkdirSync(sdkManagerDirectory, { recursive: true });
55
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
56
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
57
+ const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (command, args) => {
58
+ if (command === 'bash' && typeof args?.[1] === 'string') {
59
+ const commandString = args[1];
60
+ if (commandString.includes('emulator')) {
61
+ const emulatorPath = path.join(sdkRoot, 'emulator', 'emulator');
62
+ fs.mkdirSync(path.dirname(emulatorPath), { recursive: true });
63
+ fs.writeFileSync(emulatorPath, '');
64
+ }
65
+ }
66
+ return {};
67
+ }));
68
+ await expect(ensureAndroidEmulatorAvailable({
69
+ env: { ANDROID_HOME: sdkRoot },
70
+ })).resolves.toBe(sdkRoot);
71
+ expect(spawnSpy).toHaveBeenCalledWith('bash', ['-lc', expect.stringContaining('emulator')], expect.any(Object));
72
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
73
+ });
74
+ it('uses the default Unix SDK root when env vars are missing', () => {
75
+ expect(getDefaultUnixAndroidSdkRoot({
76
+ platform: 'darwin',
77
+ homeDirectory: '/Users/tester',
78
+ })).toBe('/Users/tester/Library/Android/sdk');
79
+ expect(getAndroidSdkRoot({}, {
80
+ platform: 'linux',
81
+ homeDirectory: '/home/tester',
82
+ })).toBe('/home/tester/Android/Sdk');
83
+ });
84
+ it('prefers ANDROID_HOME and ANDROID_SDK_ROOT over default paths', () => {
85
+ expect(getAndroidSdkRoot({
86
+ ANDROID_HOME: '/env/android-home',
87
+ ANDROID_SDK_ROOT: '/env/android-sdk-root',
88
+ }, {
89
+ platform: 'darwin',
90
+ homeDirectory: '/Users/tester',
91
+ })).toBe('/env/android-home');
92
+ expect(getAndroidSdkRoot({
93
+ ANDROID_SDK_ROOT: '/env/android-sdk-root',
94
+ }, {
95
+ platform: 'linux',
96
+ homeDirectory: '/home/tester',
97
+ })).toBe('/env/android-sdk-root');
98
+ });
99
+ it('selects Android packages using the host architecture', () => {
100
+ expect(getHostAndroidSystemImageArch('x64')).toBe('x86_64');
101
+ expect(getHostAndroidSystemImageArch('arm64')).toBe('arm64-v8a');
102
+ expect(getAndroidSystemImagePackage(35, 'x86_64')).toBe('system-images;android-35;default;x86_64');
103
+ expect(getAndroidSystemImagePackage(35, 'arm64-v8a')).toBe('system-images;android-35;default;arm64-v8a');
104
+ });
105
+ it('derives emulator package requirements from runner config fields', () => {
106
+ expect(getRequiredAndroidSdkPackages({
107
+ apiLevel: 34,
108
+ includeEmulator: true,
109
+ architecture: 'x86_64',
110
+ })).toEqual([
111
+ 'platform-tools',
112
+ 'emulator',
113
+ 'platforms;android-34',
114
+ 'system-images;android-34;default;x86_64',
115
+ ]);
116
+ });
117
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=instance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/instance.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,423 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { DEFAULT_METRO_PORT, } from '@react-native-harness/config';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { getAndroidEmulatorPlatformInstance, getAndroidPhysicalDevicePlatformInstance, } from '../instance.js';
7
+ import * as adb from '../adb.js';
8
+ import * as avdConfig from '../avd-config.js';
9
+ import * as sharedPrefs from '../shared-prefs.js';
10
+ import { HarnessAppPathError, HarnessEmulatorConfigError } from '../errors.js';
11
+ const harnessConfig = {
12
+ metroPort: DEFAULT_METRO_PORT,
13
+ };
14
+ const harnessConfigWithoutNativeCrashDetection = {
15
+ metroPort: DEFAULT_METRO_PORT,
16
+ detectNativeCrashes: false,
17
+ };
18
+ const init = {
19
+ signal: new AbortController().signal,
20
+ };
21
+ describe('Android platform instance', () => {
22
+ beforeEach(() => {
23
+ vi.restoreAllMocks();
24
+ vi.unstubAllEnvs();
25
+ });
26
+ it('reuses a running emulator and does not shut it down on dispose', async () => {
27
+ const ensureAndroidEmulatorEnvironment = vi
28
+ .spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment')
29
+ .mockResolvedValue('/tmp/android-sdk');
30
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
31
+ vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
32
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
33
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
34
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
35
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
36
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
37
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
38
+ vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(undefined);
39
+ vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
40
+ const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
41
+ const instance = await getAndroidEmulatorPlatformInstance({
42
+ name: 'android',
43
+ device: {
44
+ type: 'emulator',
45
+ name: 'Pixel_8_API_35',
46
+ avd: {
47
+ apiLevel: 35,
48
+ profile: 'pixel_8',
49
+ diskSize: '1G',
50
+ heapSize: '1G',
51
+ },
52
+ },
53
+ bundleId: 'com.harnessplayground',
54
+ activityName: '.MainActivity',
55
+ }, harnessConfig, init);
56
+ await instance.dispose();
57
+ expect(ensureAndroidEmulatorEnvironment).not.toHaveBeenCalled();
58
+ expect(stopEmulator).not.toHaveBeenCalled();
59
+ });
60
+ it('creates and boots an emulator when missing and shuts it down on dispose', async () => {
61
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
62
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
63
+ vi.spyOn(adb, 'hasAvd').mockResolvedValue(false);
64
+ const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
65
+ const startEmulator = vi
66
+ .spyOn(adb, 'startEmulator')
67
+ .mockResolvedValue(undefined);
68
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
69
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
70
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
71
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
72
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
73
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
74
+ vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(undefined);
75
+ vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
76
+ const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
77
+ const instance = await getAndroidEmulatorPlatformInstance({
78
+ name: 'android',
79
+ device: {
80
+ type: 'emulator',
81
+ name: 'Pixel_8_API_35',
82
+ avd: {
83
+ apiLevel: 35,
84
+ profile: 'pixel_8',
85
+ diskSize: '1G',
86
+ heapSize: '1G',
87
+ },
88
+ },
89
+ bundleId: 'com.harnessplayground',
90
+ activityName: '.MainActivity',
91
+ }, harnessConfig, init);
92
+ expect(createAvd).toHaveBeenCalledWith({
93
+ name: 'Pixel_8_API_35',
94
+ apiLevel: 35,
95
+ profile: 'pixel_8',
96
+ diskSize: '1G',
97
+ heapSize: '1G',
98
+ });
99
+ expect(startEmulator).toHaveBeenCalledWith('Pixel_8_API_35', undefined);
100
+ await instance.dispose();
101
+ expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
102
+ });
103
+ it('verifies SDK assets before booting an existing AVD', async () => {
104
+ const ensureAndroidEmulatorEnvironment = vi
105
+ .spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment')
106
+ .mockResolvedValue('/tmp/android-sdk');
107
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
108
+ vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
109
+ const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
110
+ const startEmulator = vi
111
+ .spyOn(adb, 'startEmulator')
112
+ .mockResolvedValue(undefined);
113
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
114
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
115
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
116
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
117
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
118
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
119
+ await expect(getAndroidEmulatorPlatformInstance({
120
+ name: 'android',
121
+ device: {
122
+ type: 'emulator',
123
+ name: 'Pixel_8_API_35',
124
+ avd: {
125
+ apiLevel: 35,
126
+ profile: 'pixel_8',
127
+ diskSize: '1G',
128
+ heapSize: '1G',
129
+ },
130
+ },
131
+ bundleId: 'com.harnessplayground',
132
+ activityName: '.MainActivity',
133
+ }, harnessConfig, init)).resolves.toBeDefined();
134
+ expect(ensureAndroidEmulatorEnvironment).toHaveBeenCalledWith(35);
135
+ expect(createAvd).not.toHaveBeenCalled();
136
+ expect(startEmulator).toHaveBeenCalledWith('Pixel_8_API_35', undefined);
137
+ });
138
+ it('reuses a compatible cached AVD snapshot when caching is enabled', async () => {
139
+ vi.stubEnv('HARNESS_AVD_CACHING', 'true');
140
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
141
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
142
+ vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
143
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({
144
+ imageSysdir1: 'system-images/android-35/default/arm64-v8a/',
145
+ abiType: 'arm64-v8a',
146
+ hwDeviceName: 'pixel_8',
147
+ diskDataPartitionSize: '1G',
148
+ vmHeapSize: '1G',
149
+ });
150
+ vi.spyOn(adb, 'startEmulator').mockResolvedValue(undefined);
151
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
152
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
153
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
154
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
155
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
156
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
157
+ await expect(getAndroidEmulatorPlatformInstance({
158
+ name: 'android',
159
+ device: {
160
+ type: 'emulator',
161
+ name: 'Pixel_8_API_35',
162
+ avd: {
163
+ apiLevel: 35,
164
+ profile: 'pixel_8',
165
+ diskSize: '1G',
166
+ heapSize: '1G',
167
+ snapshot: { enabled: false },
168
+ },
169
+ },
170
+ bundleId: 'com.harnessplayground',
171
+ activityName: '.MainActivity',
172
+ }, harnessConfig, init)).resolves.toBeDefined();
173
+ expect(adb.startEmulator).toHaveBeenCalledTimes(1);
174
+ expect(adb.startEmulator).toHaveBeenCalledWith('Pixel_8_API_35', 'snapshot-reuse');
175
+ });
176
+ it('recreates an incompatible cached AVD before the real boot', async () => {
177
+ vi.stubEnv('HARNESS_AVD_CACHING', 'true');
178
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
179
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
180
+ vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
181
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({
182
+ imageSysdir1: 'system-images/android-34/default/x86_64/',
183
+ abiType: 'x86_64',
184
+ hwDeviceName: 'pixel_7',
185
+ diskDataPartitionSize: '2G',
186
+ vmHeapSize: '2G',
187
+ });
188
+ const deleteAvd = vi.spyOn(adb, 'deleteAvd').mockResolvedValue(undefined);
189
+ const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
190
+ vi.spyOn(adb, 'startEmulator').mockResolvedValue(undefined);
191
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
192
+ const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
193
+ const waitForEmulatorDisconnect = vi
194
+ .spyOn(adb, 'waitForEmulatorDisconnect')
195
+ .mockResolvedValue(undefined);
196
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
197
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
198
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
199
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
200
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
201
+ await expect(getAndroidEmulatorPlatformInstance({
202
+ name: 'android',
203
+ device: {
204
+ type: 'emulator',
205
+ name: 'Pixel_8_API_35',
206
+ avd: {
207
+ apiLevel: 35,
208
+ profile: 'pixel_8',
209
+ diskSize: '1G',
210
+ heapSize: '1G',
211
+ snapshot: { enabled: true },
212
+ },
213
+ },
214
+ bundleId: 'com.harnessplayground',
215
+ activityName: '.MainActivity',
216
+ }, harnessConfig, init)).resolves.toBeDefined();
217
+ expect(deleteAvd).toHaveBeenCalledWith('Pixel_8_API_35');
218
+ expect(createAvd).toHaveBeenCalled();
219
+ expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
220
+ expect(waitForEmulatorDisconnect).toHaveBeenCalledWith('emulator-5554', init.signal);
221
+ expect(adb.startEmulator).toHaveBeenNthCalledWith(1, 'Pixel_8_API_35', 'clean-snapshot-generation');
222
+ expect(adb.startEmulator).toHaveBeenNthCalledWith(2, 'Pixel_8_API_35', 'snapshot-reuse');
223
+ });
224
+ it('generates a snapshot on first run before the test boot', async () => {
225
+ vi.stubEnv('HARNESS_AVD_CACHING', 'true');
226
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
227
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
228
+ vi.spyOn(adb, 'hasAvd').mockResolvedValue(false);
229
+ vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
230
+ const startEmulator = vi
231
+ .spyOn(adb, 'startEmulator')
232
+ .mockResolvedValue(undefined);
233
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
234
+ const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
235
+ const waitForEmulatorDisconnect = vi
236
+ .spyOn(adb, 'waitForEmulatorDisconnect')
237
+ .mockResolvedValue(undefined);
238
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
239
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
240
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
241
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
242
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
243
+ await expect(getAndroidEmulatorPlatformInstance({
244
+ name: 'android',
245
+ device: {
246
+ type: 'emulator',
247
+ name: 'Pixel_8_API_35',
248
+ avd: {
249
+ apiLevel: 35,
250
+ profile: 'pixel_8',
251
+ diskSize: '1G',
252
+ heapSize: '1G',
253
+ snapshot: { enabled: true },
254
+ },
255
+ },
256
+ bundleId: 'com.harnessplayground',
257
+ activityName: '.MainActivity',
258
+ }, harnessConfig, init)).resolves.toBeDefined();
259
+ expect(startEmulator).toHaveBeenNthCalledWith(1, 'Pixel_8_API_35', 'clean-snapshot-generation');
260
+ expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
261
+ expect(waitForEmulatorDisconnect).toHaveBeenCalledWith('emulator-5554', init.signal);
262
+ expect(startEmulator).toHaveBeenNthCalledWith(2, 'Pixel_8_API_35', 'snapshot-reuse');
263
+ });
264
+ it('installs the app from HARNESS_APP_PATH when missing', async () => {
265
+ const appPath = path.join(os.tmpdir(), 'HarnessPlayground.apk');
266
+ fs.writeFileSync(appPath, 'apk');
267
+ vi.stubEnv('HARNESS_APP_PATH', appPath);
268
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
269
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
270
+ vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
271
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
272
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
273
+ const installApp = vi.spyOn(adb, 'installApp').mockResolvedValue(undefined);
274
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
275
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
276
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
277
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
278
+ await expect(getAndroidEmulatorPlatformInstance({
279
+ name: 'android',
280
+ device: {
281
+ type: 'emulator',
282
+ name: 'Pixel_8_API_35',
283
+ avd: {
284
+ apiLevel: 35,
285
+ profile: 'pixel_8',
286
+ diskSize: '1G',
287
+ heapSize: '1G',
288
+ },
289
+ },
290
+ bundleId: 'com.harnessplayground',
291
+ activityName: '.MainActivity',
292
+ }, harnessConfig, init)).resolves.toBeDefined();
293
+ expect(installApp).toHaveBeenCalledWith('emulator-5554', appPath);
294
+ fs.rmSync(appPath, { force: true });
295
+ });
296
+ it('throws a HarnessAppPathError when HARNESS_APP_PATH is missing', async () => {
297
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
298
+ vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
299
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
300
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
301
+ await expect(getAndroidEmulatorPlatformInstance({
302
+ name: 'android',
303
+ device: {
304
+ type: 'emulator',
305
+ name: 'Pixel_8_API_35',
306
+ avd: {
307
+ apiLevel: 35,
308
+ profile: 'pixel_8',
309
+ diskSize: '1G',
310
+ heapSize: '1G',
311
+ },
312
+ },
313
+ bundleId: 'com.harnessplayground',
314
+ activityName: '.MainActivity',
315
+ }, harnessConfig, init)).rejects.toBeInstanceOf(HarnessAppPathError);
316
+ });
317
+ it('throws a HarnessAppPathError when HARNESS_APP_PATH points to a missing app', async () => {
318
+ vi.stubEnv('HARNESS_APP_PATH', '/tmp/missing.apk');
319
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
320
+ vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
321
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
322
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
323
+ await expect(getAndroidEmulatorPlatformInstance({
324
+ name: 'android',
325
+ device: {
326
+ type: 'emulator',
327
+ name: 'Pixel_8_API_35',
328
+ avd: {
329
+ apiLevel: 35,
330
+ profile: 'pixel_8',
331
+ diskSize: '1G',
332
+ heapSize: '1G',
333
+ },
334
+ },
335
+ bundleId: 'com.harnessplayground',
336
+ activityName: '.MainActivity',
337
+ }, harnessConfig, init)).rejects.toBeInstanceOf(HarnessAppPathError);
338
+ });
339
+ it('throws a HarnessEmulatorConfigError when the emulator is missing and avd config is absent', async () => {
340
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
341
+ await expect(getAndroidEmulatorPlatformInstance({
342
+ name: 'android',
343
+ device: {
344
+ type: 'emulator',
345
+ name: 'Pixel_8_API_35',
346
+ },
347
+ bundleId: 'com.harnessplayground',
348
+ activityName: '.MainActivity',
349
+ }, harnessConfig, init)).rejects.toBeInstanceOf(HarnessEmulatorConfigError);
350
+ });
351
+ it('returns a noop emulator app monitor when native crash detection is disabled', async () => {
352
+ vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
353
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
354
+ vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
355
+ vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
356
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
357
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
358
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
359
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
360
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
361
+ const instance = await getAndroidEmulatorPlatformInstance({
362
+ name: 'android',
363
+ device: {
364
+ type: 'emulator',
365
+ name: 'Pixel_8_API_35',
366
+ avd: {
367
+ apiLevel: 35,
368
+ profile: 'pixel_8',
369
+ diskSize: '1G',
370
+ heapSize: '1G',
371
+ },
372
+ },
373
+ bundleId: 'com.harnessplayground',
374
+ activityName: '.MainActivity',
375
+ }, harnessConfigWithoutNativeCrashDetection, init);
376
+ const listener = vi.fn();
377
+ const appMonitor = instance.createAppMonitor();
378
+ await expect(appMonitor.start()).resolves.toBeUndefined();
379
+ await expect(appMonitor.stop()).resolves.toBeUndefined();
380
+ await expect(appMonitor.dispose()).resolves.toBeUndefined();
381
+ expect(appMonitor.addListener(listener)).toBeUndefined();
382
+ expect(appMonitor.removeListener(listener)).toBeUndefined();
383
+ });
384
+ it('returns a noop physical device app monitor when native crash detection is disabled', async () => {
385
+ vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['012345']);
386
+ vi.spyOn(adb, 'getDeviceInfo').mockResolvedValue({
387
+ manufacturer: 'motorola',
388
+ model: 'moto g72',
389
+ });
390
+ vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
391
+ vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
392
+ vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
393
+ vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
394
+ vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
395
+ await expect(getAndroidPhysicalDevicePlatformInstance({
396
+ name: 'android-device',
397
+ device: {
398
+ type: 'physical',
399
+ manufacturer: 'motorola',
400
+ model: 'moto g72',
401
+ },
402
+ bundleId: 'com.harnessplayground',
403
+ activityName: '.MainActivity',
404
+ }, harnessConfigWithoutNativeCrashDetection)).resolves.toBeDefined();
405
+ const instance = await getAndroidPhysicalDevicePlatformInstance({
406
+ name: 'android-device',
407
+ device: {
408
+ type: 'physical',
409
+ manufacturer: 'motorola',
410
+ model: 'moto g72',
411
+ },
412
+ bundleId: 'com.harnessplayground',
413
+ activityName: '.MainActivity',
414
+ }, harnessConfigWithoutNativeCrashDetection);
415
+ const listener = vi.fn();
416
+ const appMonitor = instance.createAppMonitor();
417
+ await expect(appMonitor.start()).resolves.toBeUndefined();
418
+ await expect(appMonitor.stop()).resolves.toBeUndefined();
419
+ await expect(appMonitor.dispose()).resolves.toBeUndefined();
420
+ expect(appMonitor.addListener(listener)).toBeUndefined();
421
+ expect(appMonitor.removeListener(listener)).toBeUndefined();
422
+ });
423
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=targets.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/targets.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getRunTargets } from '../targets.js';
3
+ import * as adb from '../adb.js';
4
+ import * as environment from '../environment.js';
5
+ describe('Android target discovery', () => {
6
+ beforeEach(() => {
7
+ vi.restoreAllMocks();
8
+ vi.unstubAllEnvs();
9
+ });
10
+ it('installs adb and emulator only for discovery', async () => {
11
+ const ensureAndroidAdbAvailable = vi
12
+ .spyOn(environment, 'ensureAndroidAdbAvailable')
13
+ .mockResolvedValue('/tmp/android-sdk');
14
+ const ensureAndroidEmulatorAvailable = vi
15
+ .spyOn(environment, 'ensureAndroidEmulatorAvailable')
16
+ .mockResolvedValue('/tmp/android-sdk');
17
+ vi.spyOn(adb, 'getAvds').mockResolvedValue(['Pixel_8_API_35']);
18
+ vi.spyOn(adb, 'getConnectedDevices').mockResolvedValue([
19
+ {
20
+ id: 'device-1',
21
+ manufacturer: 'Google',
22
+ model: 'Pixel 8',
23
+ },
24
+ ]);
25
+ await expect(getRunTargets()).resolves.toEqual([
26
+ {
27
+ type: 'emulator',
28
+ name: 'Pixel_8_API_35',
29
+ platform: 'android',
30
+ description: 'Android emulator',
31
+ device: {
32
+ name: 'Pixel_8_API_35',
33
+ },
34
+ },
35
+ {
36
+ type: 'physical',
37
+ name: 'Google Pixel 8',
38
+ platform: 'android',
39
+ description: 'Physical device (device-1)',
40
+ device: {
41
+ manufacturer: 'Google',
42
+ model: 'Pixel 8',
43
+ },
44
+ },
45
+ ]);
46
+ expect(ensureAndroidAdbAvailable).toHaveBeenCalledTimes(1);
47
+ expect(ensureAndroidEmulatorAvailable).toHaveBeenCalledTimes(1);
48
+ });
49
+ });
package/dist/adb.d.ts CHANGED
@@ -1,4 +1,19 @@
1
1
  import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
2
+ import type { ChildProcessByStdio } from 'node:child_process';
3
+ import type { Readable } from 'node:stream';
4
+ import { type EmulatorBootMode } from './emulator-startup.js';
5
+ export declare const emulatorProcess: {
6
+ startDetachedProcess: (file: string, args: readonly string[]) => ChildProcessByStdio<null, Readable, Readable>;
7
+ };
8
+ export type CreateAvdOptions = {
9
+ name: string;
10
+ apiLevel: number;
11
+ profile: string;
12
+ diskSize: string;
13
+ heapSize: string;
14
+ };
15
+ export declare const getRequiredEmulatorPackages: (apiLevel: number) => string[];
16
+ export declare const verifyAndroidEmulatorSdk: (apiLevel: number) => Promise<void>;
2
17
  export declare const getStartAppArgs: (bundleId: string, activityName: string, options?: AndroidAppLaunchOptions) => string[];
3
18
  export declare const isAppInstalled: (adbId: string, bundleId: string) => Promise<boolean>;
4
19
  export declare const reversePort: (adbId: string, port: number, hostPort?: number) => Promise<void>;
@@ -14,6 +29,14 @@ export type DeviceInfo = {
14
29
  export declare const getDeviceInfo: (adbId: string) => Promise<DeviceInfo | null>;
15
30
  export declare const isBootCompleted: (adbId: string) => Promise<boolean>;
16
31
  export declare const stopEmulator: (adbId: string) => Promise<void>;
32
+ export declare const installApp: (adbId: string, appPath: string) => Promise<void>;
33
+ export declare const hasAvd: (name: string) => Promise<boolean>;
34
+ export declare const createAvd: ({ name, apiLevel, profile, diskSize, heapSize, }: CreateAvdOptions) => Promise<void>;
35
+ export declare const deleteAvd: (name: string) => Promise<void>;
36
+ export declare const startEmulator: (name: string, mode?: EmulatorBootMode) => Promise<void>;
37
+ export declare const waitForEmulator: (name: string, signal: AbortSignal) => Promise<string>;
38
+ export declare const waitForEmulatorDisconnect: (adbId: string, signal: AbortSignal) => Promise<void>;
39
+ export declare const waitForBoot: (name: string, signal: AbortSignal) => Promise<string>;
17
40
  export declare const isAppRunning: (adbId: string, bundleId: string) => Promise<boolean>;
18
41
  export declare const getAppUid: (adbId: string, bundleId: string) => Promise<number>;
19
42
  export declare const setHideErrorDialogs: (adbId: string, hide: boolean) => Promise<void>;
package/dist/adb.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG/E,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,cAAc,MAAM,EACpB,UAAU,uBAAuB,KAChC,MAAM,EAoCR,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,EACb,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAWjB,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,EACb,MAAM,MAAM,EACZ,WAAU,MAAa,KACtB,OAAO,CAAC,IAAI,CAQd,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,OAAO,MAAM,EACb,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,OAAO,MAAM,EACb,UAAU,MAAM,EAChB,cAAc,MAAM,EACpB,UAAU,uBAAuB,KAChC,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,eAAO,MAAM,YAAY,QAAa,OAAO,CAAC,MAAM,EAAE,CAOrD,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAGvB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,EACb,UAAU,MAAM,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CASvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,KACZ,OAAO,CAAC,UAAU,GAAG,IAAI,CAI3B,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,OAAO,CAGpE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAE9D,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,OAAO,MAAM,EACb,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAiBjB,CAAC;AAEF,eAAO,MAAM,SAAS,GACpB,OAAO,MAAM,EACb,UAAU,MAAM,KACf,OAAO,CAAC,MAAM,CAoBhB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,OAAO,MAAM,EACb,MAAM,OAAO,KACZ,OAAO,CAAC,IAAI,CAWd,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAUtE,CAAC;AAEF,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,MAAM,EAAE,CAUhD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAa,OAAO,CAAC,SAAS,EAAE,CA8B/D,CAAC"}
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}