@react-native-harness/platform-android 1.4.0-rc.1 → 1.4.1

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 (78) hide show
  1. package/dist/adb.d.ts +3 -1
  2. package/dist/adb.d.ts.map +1 -1
  3. package/dist/adb.js +30 -21
  4. package/dist/app-session.d.ts +11 -2
  5. package/dist/app-session.d.ts.map +1 -1
  6. package/dist/app-session.js +17 -12
  7. package/dist/avd-config.d.ts +1 -0
  8. package/dist/avd-config.d.ts.map +1 -1
  9. package/dist/avd-config.js +11 -0
  10. package/dist/emulator-startup.d.ts +23 -0
  11. package/dist/emulator-startup.d.ts.map +1 -1
  12. package/dist/emulator-startup.js +58 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/instance.d.ts.map +1 -1
  17. package/dist/instance.js +21 -15
  18. package/dist/runner.d.ts +2 -2
  19. package/dist/runner.d.ts.map +1 -1
  20. package/package.json +4 -4
  21. package/src/adb.ts +72 -65
  22. package/src/app-session.ts +28 -12
  23. package/src/avd-config.ts +17 -0
  24. package/src/emulator-startup.ts +74 -0
  25. package/src/index.ts +1 -0
  26. package/src/instance.ts +30 -16
  27. package/src/runner.ts +5 -9
  28. package/dist/__tests__/adb-id.test.d.ts +0 -2
  29. package/dist/__tests__/adb-id.test.d.ts.map +0 -1
  30. package/dist/__tests__/adb-id.test.js +0 -41
  31. package/dist/__tests__/adb.test.d.ts +0 -2
  32. package/dist/__tests__/adb.test.d.ts.map +0 -1
  33. package/dist/__tests__/adb.test.js +0 -552
  34. package/dist/__tests__/app-monitor.test.d.ts +0 -2
  35. package/dist/__tests__/app-monitor.test.d.ts.map +0 -1
  36. package/dist/__tests__/app-monitor.test.js +0 -257
  37. package/dist/__tests__/avd-config.test.d.ts +0 -2
  38. package/dist/__tests__/avd-config.test.d.ts.map +0 -1
  39. package/dist/__tests__/avd-config.test.js +0 -174
  40. package/dist/__tests__/ci-action.test.d.ts +0 -2
  41. package/dist/__tests__/ci-action.test.d.ts.map +0 -1
  42. package/dist/__tests__/ci-action.test.js +0 -46
  43. package/dist/__tests__/crash-diagnostics.test.d.ts +0 -2
  44. package/dist/__tests__/crash-diagnostics.test.d.ts.map +0 -1
  45. package/dist/__tests__/crash-diagnostics.test.js +0 -119
  46. package/dist/__tests__/crash-parser.test.d.ts +0 -2
  47. package/dist/__tests__/crash-parser.test.d.ts.map +0 -1
  48. package/dist/__tests__/crash-parser.test.js +0 -45
  49. package/dist/__tests__/crash-reporter.test.d.ts +0 -2
  50. package/dist/__tests__/crash-reporter.test.d.ts.map +0 -1
  51. package/dist/__tests__/crash-reporter.test.js +0 -150
  52. package/dist/__tests__/emulator-startup.test.d.ts +0 -2
  53. package/dist/__tests__/emulator-startup.test.d.ts.map +0 -1
  54. package/dist/__tests__/emulator-startup.test.js +0 -20
  55. package/dist/__tests__/environment.test.d.ts +0 -2
  56. package/dist/__tests__/environment.test.d.ts.map +0 -1
  57. package/dist/__tests__/environment.test.js +0 -117
  58. package/dist/__tests__/instance.test.d.ts +0 -2
  59. package/dist/__tests__/instance.test.d.ts.map +0 -1
  60. package/dist/__tests__/instance.test.js +0 -593
  61. package/dist/__tests__/shared-prefs.test.d.ts +0 -2
  62. package/dist/__tests__/shared-prefs.test.d.ts.map +0 -1
  63. package/dist/__tests__/shared-prefs.test.js +0 -87
  64. package/dist/__tests__/targets.test.d.ts +0 -2
  65. package/dist/__tests__/targets.test.d.ts.map +0 -1
  66. package/dist/__tests__/targets.test.js +0 -49
  67. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  68. package/src/__tests__/adb-id.test.ts +0 -51
  69. package/src/__tests__/avd-config.test.ts +0 -206
  70. package/src/__tests__/ci-action.test.ts +0 -81
  71. package/src/__tests__/crash-diagnostics.test.ts +0 -144
  72. package/src/__tests__/crash-parser.test.ts +0 -52
  73. package/src/__tests__/crash-reporter.test.ts +0 -167
  74. package/src/__tests__/emulator-startup.test.ts +0 -35
  75. package/src/__tests__/instance.test.ts +0 -845
  76. package/src/__tests__/shared-prefs.test.ts +0 -144
  77. package/src/__tests__/targets.test.ts +0 -53
  78. package/tsconfig.tsbuildinfo +0 -1
@@ -1,845 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import {
3
- DEFAULT_METRO_PORT,
4
- type Config as HarnessConfig,
5
- } from '@react-native-harness/config';
6
- import type { Subprocess } from '@react-native-harness/tools';
7
- import {
8
- getAndroidEmulatorPlatformInstance,
9
- getAndroidPhysicalDevicePlatformInstance,
10
- } from '../instance.js';
11
- import * as adb from '../adb.js';
12
- import * as avdConfig from '../avd-config.js';
13
- import * as sharedPrefs from '../shared-prefs.js';
14
- import { HarnessAppPathError, HarnessEmulatorConfigError } from '../errors.js';
15
-
16
- const createLogcatProcess = (lines: string[] = []): Subprocess => {
17
- const process = {
18
- nodeChildProcess: Promise.resolve({
19
- kill: vi.fn(),
20
- }),
21
- [Symbol.asyncIterator]: async function* () {
22
- for (const line of lines) {
23
- yield line;
24
- }
25
- },
26
- };
27
-
28
- return process as unknown as Subprocess;
29
- };
30
-
31
- const harnessConfig = {
32
- metroPort: DEFAULT_METRO_PORT,
33
- } as HarnessConfig;
34
- const harnessConfigWithoutNativeCrashDetection = {
35
- metroPort: DEFAULT_METRO_PORT,
36
- detectNativeCrashes: false,
37
- } as HarnessConfig;
38
- const init = {
39
- signal: new AbortController().signal,
40
- };
41
-
42
- describe('Android platform instance', () => {
43
- beforeEach(async () => {
44
- vi.restoreAllMocks();
45
- vi.unstubAllEnvs();
46
- vi.useRealTimers();
47
- vi.spyOn(
48
- await import('../environment.js'),
49
- 'ensureAndroidEmulatorAvailable',
50
- ).mockResolvedValue('/tmp/android-sdk');
51
- });
52
-
53
- it('reuses a running emulator and does not shut it down on dispose', async () => {
54
- const ensureAndroidEmulatorEnvironment = vi
55
- .spyOn(
56
- await import('../environment.js'),
57
- 'ensureAndroidEmulatorEnvironment',
58
- )
59
- .mockResolvedValue('/tmp/android-sdk');
60
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
61
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
62
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
63
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
64
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
65
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
66
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
67
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
68
- '01-01 00:00:00.000',
69
- );
70
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
71
- undefined,
72
- );
73
- vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(
74
- undefined,
75
- );
76
- vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
77
- const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
78
-
79
- const instance = await getAndroidEmulatorPlatformInstance(
80
- {
81
- name: 'android',
82
- device: {
83
- type: 'emulator',
84
- name: 'Pixel_8_API_35',
85
- avd: {
86
- apiLevel: 35,
87
- profile: 'pixel_8',
88
- diskSize: '1G',
89
- heapSize: '1G',
90
- },
91
- },
92
- bundleId: 'com.harnessplayground',
93
- activityName: '.MainActivity',
94
- },
95
- harnessConfig,
96
- init,
97
- );
98
-
99
- await instance.dispose();
100
-
101
- expect(ensureAndroidEmulatorEnvironment).not.toHaveBeenCalled();
102
- expect(stopEmulator).not.toHaveBeenCalled();
103
- });
104
-
105
- it('creates and boots an emulator when missing and shuts it down on dispose', async () => {
106
- vi.spyOn(
107
- await import('../environment.js'),
108
- 'ensureAndroidEmulatorEnvironment',
109
- ).mockResolvedValue('/tmp/android-sdk');
110
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
111
- vi.spyOn(adb, 'hasAvd').mockResolvedValue(false);
112
- const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
113
- const startEmulator = vi
114
- .spyOn(adb, 'startEmulator')
115
- .mockResolvedValue(undefined);
116
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
117
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
118
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
119
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
120
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
121
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
122
- '01-01 00:00:00.000',
123
- );
124
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
125
- undefined,
126
- );
127
- vi.spyOn(sharedPrefs, 'clearHarnessDebugHttpHost').mockResolvedValue(
128
- undefined,
129
- );
130
- vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
131
- const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
132
-
133
- const instance = await getAndroidEmulatorPlatformInstance(
134
- {
135
- name: 'android',
136
- device: {
137
- type: 'emulator',
138
- name: 'Pixel_8_API_35',
139
- avd: {
140
- apiLevel: 35,
141
- profile: 'pixel_8',
142
- diskSize: '1G',
143
- heapSize: '1G',
144
- },
145
- },
146
- bundleId: 'com.harnessplayground',
147
- activityName: '.MainActivity',
148
- },
149
- harnessConfig,
150
- init,
151
- );
152
-
153
- expect(createAvd).toHaveBeenCalledWith({
154
- name: 'Pixel_8_API_35',
155
- apiLevel: 35,
156
- profile: 'pixel_8',
157
- diskSize: '1G',
158
- heapSize: '1G',
159
- });
160
- expect(startEmulator).toHaveBeenCalledWith('Pixel_8_API_35', undefined);
161
-
162
- await instance.dispose();
163
-
164
- expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
165
- });
166
-
167
- it('verifies SDK assets before booting an existing AVD', async () => {
168
- const ensureAndroidEmulatorEnvironment = vi
169
- .spyOn(
170
- await import('../environment.js'),
171
- 'ensureAndroidEmulatorEnvironment',
172
- )
173
- .mockResolvedValue('/tmp/android-sdk');
174
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
175
- vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
176
- const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
177
- const startEmulator = vi
178
- .spyOn(adb, 'startEmulator')
179
- .mockResolvedValue(undefined);
180
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
181
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
182
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
183
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
184
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
185
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
186
- '01-01 00:00:00.000',
187
- );
188
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
189
- undefined,
190
- );
191
-
192
- await expect(
193
- getAndroidEmulatorPlatformInstance(
194
- {
195
- name: 'android',
196
- device: {
197
- type: 'emulator',
198
- name: 'Pixel_8_API_35',
199
- avd: {
200
- apiLevel: 35,
201
- profile: 'pixel_8',
202
- diskSize: '1G',
203
- heapSize: '1G',
204
- },
205
- },
206
- bundleId: 'com.harnessplayground',
207
- activityName: '.MainActivity',
208
- },
209
- harnessConfig,
210
- init,
211
- ),
212
- ).resolves.toBeDefined();
213
-
214
- expect(ensureAndroidEmulatorEnvironment).toHaveBeenCalledWith(35);
215
- expect(createAvd).not.toHaveBeenCalled();
216
- expect(startEmulator).toHaveBeenCalledWith('Pixel_8_API_35', undefined);
217
- });
218
-
219
- it('reuses a compatible cached AVD snapshot when caching is enabled', async () => {
220
- vi.stubEnv('HARNESS_AVD_CACHING', 'true');
221
- vi.spyOn(
222
- await import('../environment.js'),
223
- 'getHostAndroidSystemImageArch',
224
- ).mockReturnValue('arm64-v8a');
225
- vi.spyOn(
226
- await import('../environment.js'),
227
- 'ensureAndroidEmulatorEnvironment',
228
- ).mockResolvedValue('/tmp/android-sdk');
229
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
230
- vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
231
- vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({
232
- imageSysdir1: 'system-images/android-35/default/arm64-v8a/',
233
- abiType: 'arm64-v8a',
234
- hwDeviceName: 'pixel_8',
235
- diskDataPartitionSize: '1G',
236
- vmHeapSize: '1G',
237
- });
238
- vi.spyOn(adb, 'startEmulator').mockResolvedValue(undefined);
239
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
240
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
241
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
242
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
243
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
244
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
245
- '01-01 00:00:00.000',
246
- );
247
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
248
- undefined,
249
- );
250
-
251
- await expect(
252
- getAndroidEmulatorPlatformInstance(
253
- {
254
- name: 'android',
255
- device: {
256
- type: 'emulator',
257
- name: 'Pixel_8_API_35',
258
- avd: {
259
- apiLevel: 35,
260
- profile: 'pixel_8',
261
- diskSize: '1G',
262
- heapSize: '1G',
263
- snapshot: { enabled: false },
264
- },
265
- },
266
- bundleId: 'com.harnessplayground',
267
- activityName: '.MainActivity',
268
- },
269
- harnessConfig,
270
- init,
271
- ),
272
- ).resolves.toBeDefined();
273
-
274
- expect(adb.startEmulator).toHaveBeenCalledTimes(1);
275
- expect(adb.startEmulator).toHaveBeenCalledWith(
276
- 'Pixel_8_API_35',
277
- 'snapshot-reuse',
278
- );
279
- });
280
-
281
- it('recreates an incompatible cached AVD before the real boot', async () => {
282
- vi.stubEnv('HARNESS_AVD_CACHING', 'true');
283
- vi.spyOn(
284
- await import('../environment.js'),
285
- 'ensureAndroidEmulatorEnvironment',
286
- ).mockResolvedValue('/tmp/android-sdk');
287
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
288
- vi.spyOn(adb, 'hasAvd').mockResolvedValue(true);
289
- vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({
290
- imageSysdir1: 'system-images/android-34/default/x86_64/',
291
- abiType: 'x86_64',
292
- hwDeviceName: 'pixel_7',
293
- diskDataPartitionSize: '2G',
294
- vmHeapSize: '2G',
295
- });
296
- const deleteAvd = vi.spyOn(adb, 'deleteAvd').mockResolvedValue(undefined);
297
- const createAvd = vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
298
- vi.spyOn(adb, 'startEmulator').mockResolvedValue(undefined);
299
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
300
- const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
301
- const waitForEmulatorDisconnect = vi
302
- .spyOn(adb, 'waitForEmulatorDisconnect')
303
- .mockResolvedValue(undefined);
304
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
305
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
306
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
307
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
308
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
309
- '01-01 00:00:00.000',
310
- );
311
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
312
- undefined,
313
- );
314
-
315
- await expect(
316
- getAndroidEmulatorPlatformInstance(
317
- {
318
- name: 'android',
319
- device: {
320
- type: 'emulator',
321
- name: 'Pixel_8_API_35',
322
- avd: {
323
- apiLevel: 35,
324
- profile: 'pixel_8',
325
- diskSize: '1G',
326
- heapSize: '1G',
327
- snapshot: { enabled: true },
328
- },
329
- },
330
- bundleId: 'com.harnessplayground',
331
- activityName: '.MainActivity',
332
- },
333
- harnessConfig,
334
- init,
335
- ),
336
- ).resolves.toBeDefined();
337
-
338
- expect(deleteAvd).toHaveBeenCalledWith('Pixel_8_API_35');
339
- expect(createAvd).toHaveBeenCalled();
340
- expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
341
- expect(waitForEmulatorDisconnect).toHaveBeenCalledWith(
342
- 'emulator-5554',
343
- init.signal,
344
- );
345
- expect(adb.startEmulator).toHaveBeenNthCalledWith(
346
- 1,
347
- 'Pixel_8_API_35',
348
- 'clean-snapshot-generation',
349
- );
350
- expect(adb.startEmulator).toHaveBeenNthCalledWith(
351
- 2,
352
- 'Pixel_8_API_35',
353
- 'snapshot-reuse',
354
- );
355
- });
356
-
357
- it('generates a snapshot on first run before the test boot', async () => {
358
- vi.stubEnv('HARNESS_AVD_CACHING', 'true');
359
- vi.spyOn(
360
- await import('../environment.js'),
361
- 'ensureAndroidEmulatorEnvironment',
362
- ).mockResolvedValue('/tmp/android-sdk');
363
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
364
- vi.spyOn(adb, 'hasAvd').mockResolvedValue(false);
365
- vi.spyOn(adb, 'createAvd').mockResolvedValue(undefined);
366
- const startEmulator = vi
367
- .spyOn(adb, 'startEmulator')
368
- .mockResolvedValue(undefined);
369
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
370
- const stopEmulator = vi.spyOn(adb, 'stopEmulator').mockResolvedValue();
371
- const waitForEmulatorDisconnect = vi
372
- .spyOn(adb, 'waitForEmulatorDisconnect')
373
- .mockResolvedValue(undefined);
374
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
375
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
376
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
377
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
378
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
379
- '01-01 00:00:00.000',
380
- );
381
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
382
- undefined,
383
- );
384
-
385
- await expect(
386
- getAndroidEmulatorPlatformInstance(
387
- {
388
- name: 'android',
389
- device: {
390
- type: 'emulator',
391
- name: 'Pixel_8_API_35',
392
- avd: {
393
- apiLevel: 35,
394
- profile: 'pixel_8',
395
- diskSize: '1G',
396
- heapSize: '1G',
397
- snapshot: { enabled: true },
398
- },
399
- },
400
- bundleId: 'com.harnessplayground',
401
- activityName: '.MainActivity',
402
- },
403
- harnessConfig,
404
- init,
405
- ),
406
- ).resolves.toBeDefined();
407
-
408
- expect(startEmulator).toHaveBeenNthCalledWith(
409
- 1,
410
- 'Pixel_8_API_35',
411
- 'clean-snapshot-generation',
412
- );
413
- expect(stopEmulator).toHaveBeenCalledWith('emulator-5554');
414
- expect(waitForEmulatorDisconnect).toHaveBeenCalledWith(
415
- 'emulator-5554',
416
- init.signal,
417
- );
418
- expect(startEmulator).toHaveBeenNthCalledWith(
419
- 2,
420
- 'Pixel_8_API_35',
421
- 'snapshot-reuse',
422
- );
423
- });
424
-
425
- it('throws a HarnessAppPathError when HARNESS_APP_PATH is missing', async () => {
426
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
427
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
428
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
429
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
430
-
431
- await expect(
432
- getAndroidEmulatorPlatformInstance(
433
- {
434
- name: 'android',
435
- device: {
436
- type: 'emulator',
437
- name: 'Pixel_8_API_35',
438
- avd: {
439
- apiLevel: 35,
440
- profile: 'pixel_8',
441
- diskSize: '1G',
442
- heapSize: '1G',
443
- },
444
- },
445
- bundleId: 'com.harnessplayground',
446
- activityName: '.MainActivity',
447
- },
448
- harnessConfig,
449
- init,
450
- ),
451
- ).rejects.toBeInstanceOf(HarnessAppPathError);
452
- });
453
-
454
- it('throws a HarnessAppPathError when HARNESS_APP_PATH points to a missing app', async () => {
455
- vi.stubEnv('HARNESS_APP_PATH', '/tmp/missing.apk');
456
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
457
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
458
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
459
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(false);
460
-
461
- await expect(
462
- getAndroidEmulatorPlatformInstance(
463
- {
464
- name: 'android',
465
- device: {
466
- type: 'emulator',
467
- name: 'Pixel_8_API_35',
468
- avd: {
469
- apiLevel: 35,
470
- profile: 'pixel_8',
471
- diskSize: '1G',
472
- heapSize: '1G',
473
- },
474
- },
475
- bundleId: 'com.harnessplayground',
476
- activityName: '.MainActivity',
477
- },
478
- harnessConfig,
479
- init,
480
- ),
481
- ).rejects.toBeInstanceOf(HarnessAppPathError);
482
- });
483
-
484
- it('throws a HarnessEmulatorConfigError when the emulator is missing and avd config is absent', async () => {
485
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue([]);
486
-
487
- await expect(
488
- getAndroidEmulatorPlatformInstance(
489
- {
490
- name: 'android',
491
- device: {
492
- type: 'emulator',
493
- name: 'Pixel_8_API_35',
494
- },
495
- bundleId: 'com.harnessplayground',
496
- activityName: '.MainActivity',
497
- },
498
- harnessConfig,
499
- init,
500
- ),
501
- ).rejects.toBeInstanceOf(HarnessEmulatorConfigError);
502
- });
503
-
504
- it('creates a real emulator app session when native crash detection is disabled', async () => {
505
- vi.useFakeTimers();
506
- vi.spyOn(
507
- await import('../environment.js'),
508
- 'ensureAndroidEmulatorEnvironment',
509
- ).mockResolvedValue('/tmp/android-sdk');
510
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
511
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
512
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
513
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
514
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
515
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
516
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
517
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
518
- '01-01 00:00:00.000',
519
- );
520
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
521
- undefined,
522
- );
523
- const stopApp = vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
524
- const startApp = vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
525
- const getAppPid = vi.spyOn(adb, 'getAppPid').mockResolvedValue(4321);
526
- const startLogcat = vi
527
- .spyOn(adb, 'startLogcat')
528
- .mockReturnValue(createLogcatProcess());
529
-
530
- const instance = await getAndroidEmulatorPlatformInstance(
531
- {
532
- name: 'android',
533
- device: {
534
- type: 'emulator',
535
- name: 'Pixel_8_API_35',
536
- avd: {
537
- apiLevel: 35,
538
- profile: 'pixel_8',
539
- diskSize: '1G',
540
- heapSize: '1G',
541
- },
542
- },
543
- bundleId: 'com.harnessplayground',
544
- activityName: '.MainActivity',
545
- },
546
- harnessConfigWithoutNativeCrashDetection,
547
- init,
548
- );
549
-
550
- const listener = vi.fn();
551
- const appSession = await instance.createAppSession();
552
- appSession.addListener(listener);
553
- await vi.advanceTimersByTimeAsync(0);
554
-
555
- expect(startLogcat).toHaveBeenCalledWith('emulator-5554', [
556
- 'logcat',
557
- '-v',
558
- 'threadtime',
559
- '-b',
560
- 'crash',
561
- '--uid=10234',
562
- '-T',
563
- '01-01 00:00:00.000',
564
- ]);
565
- expect(startLogcat.mock.invocationCallOrder[0]).toBeLessThan(
566
- startApp.mock.invocationCallOrder[0],
567
- );
568
- await expect(appSession.getState()).resolves.toEqual({
569
- status: 'running',
570
- pid: 4321,
571
- });
572
- expect(getAppPid).toHaveBeenCalled();
573
- expect(listener).not.toHaveBeenCalled();
574
- expect(appSession.removeListener(listener)).toBeUndefined();
575
- await expect(appSession.dispose()).resolves.toBeUndefined();
576
- expect(stopApp).toHaveBeenCalled();
577
- });
578
-
579
- it('reports an early Android crash from logcat before a PID poll succeeds', async () => {
580
- vi.useFakeTimers();
581
- vi.spyOn(
582
- await import('../environment.js'),
583
- 'ensureAndroidEmulatorEnvironment',
584
- ).mockResolvedValue('/tmp/android-sdk');
585
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
586
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
587
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
588
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
589
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
590
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
591
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
592
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
593
- '01-01 00:00:00.000',
594
- );
595
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
596
- undefined,
597
- );
598
- vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
599
- vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
600
- vi.spyOn(adb, 'getAppPid').mockResolvedValue(null);
601
- vi.spyOn(adb, 'startLogcat').mockReturnValue(
602
- createLogcatProcess([
603
- '--------- beginning of crash',
604
- 'Process: com.harnessplayground, PID: 7777',
605
- 'FATAL EXCEPTION: main',
606
- ]),
607
- );
608
-
609
- const instance = await getAndroidEmulatorPlatformInstance(
610
- {
611
- name: 'android',
612
- device: {
613
- type: 'emulator',
614
- name: 'Pixel_8_API_35',
615
- avd: {
616
- apiLevel: 35,
617
- profile: 'pixel_8',
618
- diskSize: '1G',
619
- heapSize: '1G',
620
- },
621
- },
622
- bundleId: 'com.harnessplayground',
623
- activityName: '.MainActivity',
624
- },
625
- harnessConfig,
626
- init,
627
- );
628
-
629
- const listener = vi.fn();
630
- const appSession = await instance.createAppSession();
631
- appSession.addListener(listener);
632
-
633
- await vi.advanceTimersByTimeAsync(0);
634
-
635
- expect(appSession.getLogs().map((entry) => entry.line)).toEqual([
636
- '--------- beginning of crash',
637
- 'Process: com.harnessplayground, PID: 7777',
638
- 'FATAL EXCEPTION: main',
639
- ]);
640
- await expect(appSession.getState()).resolves.toMatchObject({
641
- status: 'exited',
642
- pid: 7777,
643
- reason: 'observed-exit',
644
- });
645
- expect(listener).toHaveBeenCalledWith({ type: 'app_exited' });
646
-
647
- await appSession.dispose();
648
- });
649
-
650
- it('creates a real physical-device app session when native crash detection is disabled', async () => {
651
- vi.useFakeTimers();
652
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['012345']);
653
- vi.spyOn(adb, 'getDeviceInfo').mockResolvedValue({
654
- manufacturer: 'motorola',
655
- model: 'moto g72',
656
- });
657
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
658
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
659
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
660
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
661
- vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue(
662
- '01-01 00:00:00.000',
663
- );
664
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
665
- undefined,
666
- );
667
- vi.spyOn(adb, 'stopApp').mockResolvedValue(undefined);
668
- vi.spyOn(adb, 'startApp').mockResolvedValue(undefined);
669
- vi.spyOn(adb, 'getAppPid').mockResolvedValue(8765);
670
- const startLogcat = vi
671
- .spyOn(adb, 'startLogcat')
672
- .mockReturnValue(createLogcatProcess());
673
-
674
- const instance = await getAndroidPhysicalDevicePlatformInstance(
675
- {
676
- name: 'android-device',
677
- device: {
678
- type: 'physical',
679
- manufacturer: 'motorola',
680
- model: 'moto g72',
681
- },
682
- bundleId: 'com.harnessplayground',
683
- activityName: '.MainActivity',
684
- },
685
- harnessConfigWithoutNativeCrashDetection,
686
- );
687
-
688
- const listener = vi.fn();
689
- const appSession = await instance.createAppSession();
690
- appSession.addListener(listener);
691
- await vi.advanceTimersByTimeAsync(0);
692
-
693
- expect(startLogcat).toHaveBeenCalledWith('012345', [
694
- 'logcat',
695
- '-v',
696
- 'threadtime',
697
- '-b',
698
- 'crash',
699
- '--uid=10234',
700
- '-T',
701
- '01-01 00:00:00.000',
702
- ]);
703
- await expect(appSession.getState()).resolves.toEqual({
704
- status: 'running',
705
- pid: 8765,
706
- });
707
- expect(listener).not.toHaveBeenCalled();
708
- expect(appSession.removeListener(listener)).toBeUndefined();
709
- await expect(appSession.dispose()).resolves.toBeUndefined();
710
- });
711
-
712
- it('grants permissions when permissions are enabled for emulator', async () => {
713
- vi.spyOn(
714
- await import('../environment.js'),
715
- 'ensureAndroidEmulatorEnvironment',
716
- ).mockResolvedValue('/tmp/android-sdk');
717
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
718
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
719
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
720
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
721
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
722
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
723
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
724
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
725
- undefined,
726
- );
727
- const grantPermissions = vi
728
- .spyOn(adb, 'grantPermissions')
729
- .mockResolvedValue(undefined);
730
-
731
- const harnessConfigWithPermissions = {
732
- ...harnessConfig,
733
- permissions: true,
734
- } as HarnessConfig;
735
-
736
- await getAndroidEmulatorPlatformInstance(
737
- {
738
- name: 'android',
739
- device: {
740
- type: 'emulator',
741
- name: 'Pixel_8_API_35',
742
- avd: {
743
- apiLevel: 35,
744
- profile: 'pixel_8',
745
- diskSize: '1G',
746
- heapSize: '1G',
747
- },
748
- },
749
- bundleId: 'com.harnessplayground',
750
- activityName: '.MainActivity',
751
- },
752
- harnessConfigWithPermissions,
753
- init,
754
- );
755
-
756
- expect(grantPermissions).toHaveBeenCalledWith(
757
- 'emulator-5554',
758
- 'com.harnessplayground',
759
- );
760
- });
761
-
762
- it('does not grant permissions when permissions are disabled for emulator', async () => {
763
- vi.spyOn(
764
- await import('../environment.js'),
765
- 'ensureAndroidEmulatorEnvironment',
766
- ).mockResolvedValue('/tmp/android-sdk');
767
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
768
- vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
769
- vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
770
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
771
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
772
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
773
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
774
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
775
- undefined,
776
- );
777
- const grantPermissions = vi
778
- .spyOn(adb, 'grantPermissions')
779
- .mockResolvedValue(undefined);
780
-
781
- await getAndroidEmulatorPlatformInstance(
782
- {
783
- name: 'android',
784
- device: {
785
- type: 'emulator',
786
- name: 'Pixel_8_API_35',
787
- avd: {
788
- apiLevel: 35,
789
- profile: 'pixel_8',
790
- diskSize: '1G',
791
- heapSize: '1G',
792
- },
793
- },
794
- bundleId: 'com.harnessplayground',
795
- activityName: '.MainActivity',
796
- },
797
- harnessConfig,
798
- init,
799
- );
800
-
801
- expect(grantPermissions).not.toHaveBeenCalled();
802
- });
803
-
804
- it('grants permissions when permissions are enabled for physical device', async () => {
805
- vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['012345']);
806
- vi.spyOn(adb, 'getDeviceInfo').mockResolvedValue({
807
- manufacturer: 'motorola',
808
- model: 'moto g72',
809
- });
810
- vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
811
- vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
812
- vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
813
- vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
814
- vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(
815
- undefined,
816
- );
817
- const grantPermissions = vi
818
- .spyOn(adb, 'grantPermissions')
819
- .mockResolvedValue(undefined);
820
-
821
- const harnessConfigWithPermissions = {
822
- ...harnessConfig,
823
- permissions: true,
824
- } as HarnessConfig;
825
-
826
- await getAndroidPhysicalDevicePlatformInstance(
827
- {
828
- name: 'android-device',
829
- device: {
830
- type: 'physical',
831
- manufacturer: 'motorola',
832
- model: 'moto g72',
833
- },
834
- bundleId: 'com.harnessplayground',
835
- activityName: '.MainActivity',
836
- },
837
- harnessConfigWithPermissions,
838
- );
839
-
840
- expect(grantPermissions).toHaveBeenCalledWith(
841
- '012345',
842
- 'com.harnessplayground',
843
- );
844
- });
845
- });