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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/__tests__/adb.test.js +77 -5
  2. package/dist/__tests__/environment.test.js +67 -1
  3. package/dist/__tests__/instance.test.js +1 -1
  4. package/dist/__tests__/targets.test.d.ts +2 -0
  5. package/dist/__tests__/targets.test.d.ts.map +1 -0
  6. package/dist/__tests__/targets.test.js +49 -0
  7. package/dist/adb.d.ts.map +1 -1
  8. package/dist/adb.js +47 -13
  9. package/dist/config.d.ts +16 -16
  10. package/dist/environment.d.ts +3 -0
  11. package/dist/environment.d.ts.map +1 -1
  12. package/dist/environment.js +33 -11
  13. package/dist/instance.d.ts.map +1 -1
  14. package/dist/instance.js +2 -4
  15. package/dist/runner.d.ts.map +1 -1
  16. package/dist/runner.js +2 -5
  17. package/dist/targets.d.ts.map +1 -1
  18. package/dist/targets.js +4 -2
  19. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  20. package/package.json +4 -4
  21. package/src/__tests__/adb.test.ts +133 -28
  22. package/src/__tests__/environment.test.ts +135 -10
  23. package/src/__tests__/instance.test.ts +49 -49
  24. package/src/__tests__/targets.test.ts +53 -0
  25. package/src/adb.ts +106 -48
  26. package/src/environment.ts +99 -55
  27. package/src/instance.ts +17 -17
  28. package/src/runner.ts +5 -10
  29. package/src/targets.ts +9 -2
  30. package/dist/assertions.d.ts +0 -5
  31. package/dist/assertions.d.ts.map +0 -1
  32. package/dist/assertions.js +0 -6
  33. package/dist/emulator.d.ts +0 -6
  34. package/dist/emulator.d.ts.map +0 -1
  35. package/dist/emulator.js +0 -27
  36. package/dist/reader.d.ts +0 -6
  37. package/dist/reader.d.ts.map +0 -1
  38. package/dist/reader.js +0 -57
  39. package/dist/types.d.ts +0 -381
  40. package/dist/types.d.ts.map +0 -1
  41. package/dist/types.js +0 -107
package/src/adb.ts CHANGED
@@ -5,6 +5,12 @@ import type { ChildProcessByStdio } from 'node:child_process';
5
5
  import { access, rm } from 'node:fs/promises';
6
6
  import type { Readable } from 'node:stream';
7
7
  import {
8
+ getAvdConfigPath,
9
+ getAvdDirectory,
10
+ readAvdConfig,
11
+ } from './avd-config.js';
12
+ import {
13
+ ensureAndroidEmulatorAvailable,
8
14
  ensureAndroidSdkPackages,
9
15
  getAdbBinaryPath,
10
16
  getAndroidSystemImagePackage,
@@ -12,7 +18,6 @@ import {
12
18
  getEmulatorBinaryPath,
13
19
  getHostAndroidSystemImageArch,
14
20
  getRequiredAndroidSdkPackages,
15
- getSdkManagerBinaryPath,
16
21
  } from './environment.js';
17
22
  import {
18
23
  getEmulatorStartupArgs,
@@ -36,14 +41,14 @@ const waitForAbort = (signal: AbortSignal): Promise<never> => {
36
41
  () => {
37
42
  reject(signal.reason);
38
43
  },
39
- { once: true }
44
+ { once: true },
40
45
  );
41
46
  });
42
47
  };
43
48
 
44
49
  const waitWithSignal = async (
45
50
  ms: number,
46
- signal: AbortSignal
51
+ signal: AbortSignal,
47
52
  ): Promise<void> => {
48
53
  if (signal.aborted) {
49
54
  throw signal.reason;
@@ -52,18 +57,13 @@ const waitWithSignal = async (
52
57
  await Promise.race([wait(ms), waitForAbort(signal)]);
53
58
  };
54
59
 
55
- const getAvdConfigPath = (name: string): string =>
56
- `${
57
- process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`
58
- }/${name}.avd/config.ini`;
59
-
60
60
  const EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS = 5000;
61
61
  const EMULATOR_OUTPUT_BUFFER_LIMIT = 16 * 1024;
62
62
 
63
63
  export const emulatorProcess = {
64
64
  startDetachedProcess: (
65
65
  file: string,
66
- args: readonly string[]
66
+ args: readonly string[],
67
67
  ): ChildProcessByStdio<null, Readable, Readable> =>
68
68
  nodeSpawn(file, args, {
69
69
  detached: true,
@@ -74,7 +74,7 @@ export const emulatorProcess = {
74
74
  const appendBoundedOutput = (
75
75
  output: string,
76
76
  chunk: string,
77
- limit: number = EMULATOR_OUTPUT_BUFFER_LIMIT
77
+ limit: number = EMULATOR_OUTPUT_BUFFER_LIMIT,
78
78
  ): string => {
79
79
  const nextOutput = output + chunk;
80
80
 
@@ -131,16 +131,11 @@ const formatEmulatorStartupError = ({
131
131
  };
132
132
 
133
133
  const ensureEmulatorInstalled = async (): Promise<string> => {
134
- const emulatorBinaryPath = getEmulatorBinaryPath();
134
+ await ensureAndroidEmulatorAvailable();
135
135
 
136
- try {
137
- await access(emulatorBinaryPath);
138
- return emulatorBinaryPath;
139
- } catch {
140
- await spawn(getSdkManagerBinaryPath(), ['emulator']);
141
- await access(emulatorBinaryPath);
142
- return emulatorBinaryPath;
143
- }
136
+ const emulatorBinaryPath = getEmulatorBinaryPath();
137
+ await access(emulatorBinaryPath);
138
+ return emulatorBinaryPath;
144
139
  };
145
140
 
146
141
  export type CreateAvdOptions = {
@@ -160,15 +155,77 @@ export const getRequiredEmulatorPackages = (apiLevel: number): string[] => {
160
155
  };
161
156
 
162
157
  export const verifyAndroidEmulatorSdk = async (
163
- apiLevel: number
158
+ apiLevel: number,
164
159
  ): Promise<void> => {
165
160
  await ensureAndroidSdkPackages(getRequiredEmulatorPackages(apiLevel));
166
161
  };
167
162
 
163
+ const listAvdProfiles = async (): Promise<string[]> => {
164
+ const { stdout } = await spawn(getAvdManagerBinaryPath(), ['list', 'device']);
165
+
166
+ return stdout
167
+ .split('\n')
168
+ .map((line) => line.match(/^id:\s+\d+\s+or\s+"([^"]+)"/i)?.[1]?.trim())
169
+ .filter((profile): profile is string => profile != null && profile !== '');
170
+ };
171
+
172
+ const ensureAvdProfileAvailable = async (profile: string): Promise<void> => {
173
+ const availableProfiles = await listAvdProfiles();
174
+
175
+ if (availableProfiles.includes(profile)) {
176
+ return;
177
+ }
178
+
179
+ const availableProfilesList =
180
+ availableProfiles.length > 0
181
+ ? availableProfiles.join(', ')
182
+ : 'None reported by avdmanager.';
183
+
184
+ throw new Error(
185
+ `Android AVD profile "${profile}" is not available on this machine. Available profiles: ${availableProfilesList}`,
186
+ );
187
+ };
188
+
189
+ const ensureAvdConfigExists = async (name: string): Promise<string> => {
190
+ const configPath = getAvdConfigPath(name);
191
+
192
+ if ((await readAvdConfig(name)) == null) {
193
+ throw new Error(
194
+ `Android AVD "${name}" was created, but config.ini was not found at ${configPath}.`,
195
+ );
196
+ }
197
+
198
+ return configPath;
199
+ };
200
+
201
+ const getAvdIniPath = (name: string): string => {
202
+ const avdHome =
203
+ process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`;
204
+ return `${avdHome}/${name}.ini`;
205
+ };
206
+
207
+ const ensureAvdIniExists = async ({
208
+ name,
209
+ apiLevel,
210
+ }: {
211
+ name: string;
212
+ apiLevel: number;
213
+ }): Promise<string> => {
214
+ const iniPath = getAvdIniPath(name);
215
+
216
+ const avdDirectory = getAvdDirectory(name);
217
+ await spawn('bash', [
218
+ '-lc',
219
+ `printf '%s\n%s\n%s\n%s\n' 'avd.ini.encoding=UTF-8' 'path=${avdDirectory}' 'path.rel=avd/${name}.avd' 'target=android-${apiLevel}' > "${iniPath}"`,
220
+ ]);
221
+
222
+ return iniPath;
223
+ };
224
+
168
225
  export const getStartAppArgs = (
169
226
  bundleId: string,
170
227
  activityName: string,
171
- options?: AndroidAppLaunchOptions
228
+ options?: AndroidAppLaunchOptions,
172
229
  ): string[] => {
173
230
  const args = [
174
231
  'shell',
@@ -197,7 +254,7 @@ export const getStartAppArgs = (
197
254
 
198
255
  if (!Number.isSafeInteger(value)) {
199
256
  throw new Error(
200
- `Android app launch option "${key}" must be a safe integer.`
257
+ `Android app launch option "${key}" must be a safe integer.`,
201
258
  );
202
259
  }
203
260
 
@@ -209,7 +266,7 @@ export const getStartAppArgs = (
209
266
 
210
267
  export const isAppInstalled = async (
211
268
  adbId: string,
212
- bundleId: string
269
+ bundleId: string,
213
270
  ): Promise<boolean> => {
214
271
  const { stdout } = await spawn(getAdbBinaryPath(), [
215
272
  '-s',
@@ -226,7 +283,7 @@ export const isAppInstalled = async (
226
283
  export const reversePort = async (
227
284
  adbId: string,
228
285
  port: number,
229
- hostPort: number = port
286
+ hostPort: number = port,
230
287
  ): Promise<void> => {
231
288
  await spawn(getAdbBinaryPath(), [
232
289
  '-s',
@@ -239,7 +296,7 @@ export const reversePort = async (
239
296
 
240
297
  export const stopApp = async (
241
298
  adbId: string,
242
- bundleId: string
299
+ bundleId: string,
243
300
  ): Promise<void> => {
244
301
  await spawn(getAdbBinaryPath(), [
245
302
  '-s',
@@ -255,7 +312,7 @@ export const startApp = async (
255
312
  adbId: string,
256
313
  bundleId: string,
257
314
  activityName: string,
258
- options?: AndroidAppLaunchOptions
315
+ options?: AndroidAppLaunchOptions,
259
316
  ): Promise<void> => {
260
317
  await spawn(getAdbBinaryPath(), [
261
318
  '-s',
@@ -274,7 +331,7 @@ export const getDeviceIds = async (): Promise<string[]> => {
274
331
  };
275
332
 
276
333
  export const getEmulatorName = async (
277
- adbId: string
334
+ adbId: string,
278
335
  ): Promise<string | null> => {
279
336
  const { stdout } = await spawn(getAdbBinaryPath(), [
280
337
  '-s',
@@ -288,7 +345,7 @@ export const getEmulatorName = async (
288
345
 
289
346
  export const getShellProperty = async (
290
347
  adbId: string,
291
- property: string
348
+ property: string,
292
349
  ): Promise<string | null> => {
293
350
  const { stdout } = await spawn(getAdbBinaryPath(), [
294
351
  '-s',
@@ -310,7 +367,7 @@ export type DeviceInfo = {
310
367
  };
311
368
 
312
369
  export const getDeviceInfo = async (
313
- adbId: string
370
+ adbId: string,
314
371
  ): Promise<DeviceInfo | null> => {
315
372
  const manufacturer = await getShellProperty(adbId, 'ro.product.manufacturer');
316
373
  const model = await getShellProperty(adbId, 'ro.product.model');
@@ -336,7 +393,7 @@ export const stopEmulator = async (adbId: string): Promise<void> => {
336
393
 
337
394
  export const installApp = async (
338
395
  adbId: string,
339
- appPath: string
396
+ appPath: string,
340
397
  ): Promise<void> => {
341
398
  await spawn(getAdbBinaryPath(), ['-s', adbId, 'install', '-r', appPath]);
342
399
  };
@@ -355,19 +412,20 @@ export const createAvd = async ({
355
412
  }: CreateAvdOptions): Promise<void> => {
356
413
  const systemImagePackage = getAndroidSystemImagePackage(
357
414
  apiLevel,
358
- getHostAndroidSystemImageArch()
415
+ getHostAndroidSystemImageArch(),
359
416
  );
360
417
 
361
418
  await verifyAndroidEmulatorSdk(apiLevel);
419
+ await ensureAvdProfileAvailable(profile);
362
420
  await spawn('bash', [
363
421
  '-lc',
364
- `printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}"`,
422
+ `printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}" -p "${getAvdDirectory(name)}"`,
365
423
  ]);
424
+ await ensureAvdIniExists({ name, apiLevel });
425
+ const configPath = await ensureAvdConfigExists(name);
366
426
  await spawn('bash', [
367
427
  '-lc',
368
- `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${getAvdConfigPath(
369
- name
370
- )}"`,
428
+ `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${configPath}"`,
371
429
  ]);
372
430
  };
373
431
 
@@ -379,7 +437,7 @@ export const deleteAvd = async (name: string): Promise<void> => {
379
437
  {
380
438
  force: true,
381
439
  recursive: true,
382
- }
440
+ },
383
441
  );
384
442
  await rm(
385
443
  `${
@@ -387,18 +445,18 @@ export const deleteAvd = async (name: string): Promise<void> => {
387
445
  }/${name}.ini`,
388
446
  {
389
447
  force: true,
390
- }
448
+ },
391
449
  );
392
450
  };
393
451
 
394
452
  export const startEmulator = async (
395
453
  name: string,
396
- mode: EmulatorBootMode = 'default-boot'
454
+ mode: EmulatorBootMode = 'default-boot',
397
455
  ): Promise<void> => {
398
456
  const emulatorBinaryPath = await ensureEmulatorInstalled();
399
457
  const childProcess = emulatorProcess.startDetachedProcess(
400
458
  emulatorBinaryPath,
401
- getEmulatorStartupArgs(name, mode)
459
+ getEmulatorStartupArgs(name, mode),
402
460
  );
403
461
 
404
462
  let stdout = '';
@@ -434,7 +492,7 @@ export const startEmulator = async (
434
492
  stdout,
435
493
  stderr,
436
494
  error,
437
- })
495
+ }),
438
496
  );
439
497
  });
440
498
 
@@ -446,7 +504,7 @@ export const startEmulator = async (
446
504
  stderr,
447
505
  exitCode,
448
506
  signal,
449
- })
507
+ }),
450
508
  );
451
509
  });
452
510
  });
@@ -462,7 +520,7 @@ export const startEmulator = async (
462
520
  });
463
521
 
464
522
  const observationTimeout = wait(EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS).then(
465
- () => 'timeout' as const
523
+ () => 'timeout' as const,
466
524
  );
467
525
 
468
526
  try {
@@ -478,7 +536,7 @@ export const startEmulator = async (
478
536
 
479
537
  export const waitForEmulator = async (
480
538
  name: string,
481
- signal: AbortSignal
539
+ signal: AbortSignal,
482
540
  ): Promise<string> => {
483
541
  while (!signal.aborted) {
484
542
  const adbIds = await getDeviceIds();
@@ -503,7 +561,7 @@ export const waitForEmulator = async (
503
561
 
504
562
  export const waitForEmulatorDisconnect = async (
505
563
  adbId: string,
506
- signal: AbortSignal
564
+ signal: AbortSignal,
507
565
  ): Promise<void> => {
508
566
  while (!signal.aborted) {
509
567
  const adbIds = await getDeviceIds();
@@ -520,7 +578,7 @@ export const waitForEmulatorDisconnect = async (
520
578
 
521
579
  export const waitForBoot = async (
522
580
  name: string,
523
- signal: AbortSignal
581
+ signal: AbortSignal,
524
582
  ): Promise<string> => {
525
583
  while (!signal.aborted) {
526
584
  const adbIds = await getDeviceIds();
@@ -549,7 +607,7 @@ export const waitForBoot = async (
549
607
 
550
608
  export const isAppRunning = async (
551
609
  adbId: string,
552
- bundleId: string
610
+ bundleId: string,
553
611
  ): Promise<boolean> => {
554
612
  try {
555
613
  const { stdout } = await spawn(getAdbBinaryPath(), [
@@ -571,7 +629,7 @@ export const isAppRunning = async (
571
629
 
572
630
  export const getAppUid = async (
573
631
  adbId: string,
574
- bundleId: string
632
+ bundleId: string,
575
633
  ): Promise<number> => {
576
634
  const { stdout } = await spawn(getAdbBinaryPath(), [
577
635
  '-s',
@@ -596,7 +654,7 @@ export const getAppUid = async (
596
654
 
597
655
  export const setHideErrorDialogs = async (
598
656
  adbId: string,
599
- hide: boolean
657
+ hide: boolean,
600
658
  ): Promise<void> => {
601
659
  await spawn(getAdbBinaryPath(), [
602
660
  '-s',