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

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 (83) 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 +51 -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/adb.d.ts +23 -0
  19. package/dist/adb.d.ts.map +1 -1
  20. package/dist/adb.js +265 -16
  21. package/dist/app-monitor.d.ts.map +1 -1
  22. package/dist/app-monitor.js +27 -7
  23. package/dist/assertions.d.ts +5 -0
  24. package/dist/assertions.d.ts.map +1 -0
  25. package/dist/assertions.js +6 -0
  26. package/dist/avd-config.d.ts +41 -0
  27. package/dist/avd-config.d.ts.map +1 -0
  28. package/dist/avd-config.js +173 -0
  29. package/dist/config.d.ts +77 -0
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/config.js +5 -0
  32. package/dist/emulator-startup.d.ts +3 -0
  33. package/dist/emulator-startup.d.ts.map +1 -0
  34. package/dist/emulator-startup.js +17 -0
  35. package/dist/emulator.d.ts +6 -0
  36. package/dist/emulator.d.ts.map +1 -0
  37. package/dist/emulator.js +27 -0
  38. package/dist/environment.d.ts +28 -0
  39. package/dist/environment.d.ts.map +1 -0
  40. package/dist/environment.js +295 -0
  41. package/dist/errors.d.ts +7 -0
  42. package/dist/errors.d.ts.map +1 -0
  43. package/dist/errors.js +14 -0
  44. package/dist/factory.d.ts.map +1 -1
  45. package/dist/factory.js +3 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -0
  49. package/dist/instance.d.ts +6 -0
  50. package/dist/instance.d.ts.map +1 -0
  51. package/dist/instance.js +234 -0
  52. package/dist/reader.d.ts +6 -0
  53. package/dist/reader.d.ts.map +1 -0
  54. package/dist/reader.js +57 -0
  55. package/dist/runner.d.ts +2 -2
  56. package/dist/runner.d.ts.map +1 -1
  57. package/dist/runner.js +12 -52
  58. package/dist/targets.d.ts +1 -1
  59. package/dist/targets.d.ts.map +1 -1
  60. package/dist/targets.js +2 -0
  61. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  62. package/dist/types.d.ts +381 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +107 -0
  65. package/package.json +4 -4
  66. package/src/__tests__/adb.test.ts +419 -15
  67. package/src/__tests__/avd-config.test.ts +206 -0
  68. package/src/__tests__/ci-action.test.ts +81 -0
  69. package/src/__tests__/emulator-startup.test.ts +32 -0
  70. package/src/__tests__/environment.test.ts +87 -0
  71. package/src/__tests__/instance.test.ts +610 -0
  72. package/src/adb.ts +423 -16
  73. package/src/app-monitor.ts +56 -18
  74. package/src/avd-config.ts +290 -0
  75. package/src/config.ts +8 -0
  76. package/src/emulator-startup.ts +28 -0
  77. package/src/environment.ts +510 -0
  78. package/src/errors.ts +19 -0
  79. package/src/factory.ts +4 -0
  80. package/src/index.ts +7 -1
  81. package/src/instance.ts +380 -0
  82. package/src/runner.ts +23 -69
  83. package/src/targets.ts +11 -8
@@ -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).toHaveBeenCalledWith(35);
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
+ });
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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"}