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