@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
@@ -5,6 +5,7 @@ import { SubprocessError } from '@react-native-harness/tools';
5
5
  import { createAvd, deleteAvd, emulatorProcess, getAppUid, getLogcatTimestamp, getStartAppArgs, hasAvd, installApp, startEmulator, waitForBoot, waitForEmulatorDisconnect, } from '../adb.js';
6
6
  import * as tools from '@react-native-harness/tools';
7
7
  import * as environment from '../environment.js';
8
+ import * as avdConfig from '../avd-config.js';
8
9
  const createAbortError = () => new DOMException('The operation was aborted', 'AbortError');
9
10
  const createMockChildProcess = () => {
10
11
  const process = new EventEmitter();
@@ -101,9 +102,13 @@ describe('getStartAppArgs', () => {
101
102
  ]);
102
103
  });
103
104
  it('creates an AVD and appends config overrides', async () => {
105
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
104
106
  const spawnSpy = vi
105
107
  .spyOn(tools, 'spawn')
106
- .mockResolvedValue({});
108
+ .mockResolvedValue({})
109
+ .mockResolvedValueOnce({
110
+ stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
111
+ });
107
112
  const verifyAndroidEmulatorSdk = vi
108
113
  .spyOn(environment, 'ensureAndroidSdkPackages')
109
114
  .mockResolvedValue('/tmp/android-sdk');
@@ -121,17 +126,84 @@ describe('getStartAppArgs', () => {
121
126
  'platforms;android-35',
122
127
  'system-images;android-35;default;x86_64',
123
128
  ]);
124
- expect(spawnSpy).toHaveBeenNthCalledWith(1, 'bash', [
129
+ expect(spawnSpy).toHaveBeenNthCalledWith(1, expect.stringMatching(/avdmanager$/), ['list', 'device']);
130
+ expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
125
131
  '-lc',
126
- expect.stringContaining('create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8"'),
132
+ expect.stringContaining('create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8" -p '),
127
133
  ]);
128
- expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
134
+ expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
135
+ '-lc',
136
+ expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
137
+ ]);
138
+ expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
139
+ '-lc',
140
+ expect.stringContaining(`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `),
141
+ ]);
142
+ });
143
+ it('recreates the companion ini file when avdmanager leaves it missing', async () => {
144
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
145
+ const spawnSpy = vi
146
+ .spyOn(tools, 'spawn')
147
+ .mockResolvedValue({})
148
+ .mockResolvedValueOnce({
149
+ stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
150
+ });
151
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
152
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
153
+ await createAvd({
154
+ name: 'Pixel_6_API_35',
155
+ apiLevel: 35,
156
+ profile: 'pixel_6',
157
+ diskSize: '1G',
158
+ heapSize: '1G',
159
+ });
160
+ expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
161
+ '-lc',
162
+ expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
163
+ ]);
164
+ expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
129
165
  '-lc',
130
166
  expect.stringContaining(`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `),
131
167
  ]);
132
168
  });
169
+ it('throws an actionable error when the requested AVD profile is unavailable', async () => {
170
+ vi.spyOn(tools, 'spawn').mockResolvedValueOnce({
171
+ stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
172
+ });
173
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
174
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
175
+ await expect(createAvd({
176
+ name: 'Pixel_8_Pro_API_35',
177
+ apiLevel: 35,
178
+ profile: 'pixel_8_pro',
179
+ diskSize: '1G',
180
+ heapSize: '1G',
181
+ })).rejects.toThrow('Android AVD profile "pixel_8_pro" is not available on this machine. Available profiles: pixel_8, pixel_6');
182
+ });
183
+ it('throws a clear error when avdmanager does not create config.ini', async () => {
184
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue(null);
185
+ vi.spyOn(tools, 'spawn')
186
+ .mockResolvedValue({})
187
+ .mockResolvedValueOnce({
188
+ stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
189
+ });
190
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
191
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
192
+ await expect(createAvd({
193
+ name: 'Pixel_6_API_35',
194
+ apiLevel: 35,
195
+ profile: 'pixel_6',
196
+ diskSize: '1G',
197
+ heapSize: '1G',
198
+ })).rejects.toThrow('Android AVD "Pixel_6_API_35" was created, but config.ini was not found at ');
199
+ });
133
200
  it('creates an AVD with arm64 system image packages on arm64 hosts', async () => {
134
- vi.spyOn(tools, 'spawn').mockResolvedValue({});
201
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
202
+ vi.spyOn(tools, 'spawn')
203
+ .mockResolvedValue({})
204
+ .mockResolvedValueOnce({
205
+ stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\n',
206
+ });
135
207
  const ensureAndroidSdkPackages = vi
136
208
  .spyOn(environment, 'ensureAndroidSdkPackages')
137
209
  .mockResolvedValue('/tmp/android-sdk');
@@ -1,10 +1,76 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { getAndroidSdkRoot, getAndroidSystemImagePackage, getDefaultUnixAndroidSdkRoot, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, } from '../environment.js';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { ensureAndroidAdbAvailable, ensureAndroidEmulatorAvailable, getAndroidSdkRoot, getAndroidSystemImagePackage, getDefaultUnixAndroidSdkRoot, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, } from '../environment.js';
6
+ import * as tools from '@react-native-harness/tools';
3
7
  describe('Android environment', () => {
4
8
  beforeEach(() => {
5
9
  vi.restoreAllMocks();
6
10
  vi.unstubAllEnvs();
7
11
  });
12
+ it('skips bootstrapping command-line tools when adb is already installed', async () => {
13
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
14
+ const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
15
+ fs.mkdirSync(path.dirname(adbPath), { recursive: true });
16
+ fs.writeFileSync(adbPath, '');
17
+ const spawnSpy = vi.spyOn(tools, 'spawn');
18
+ await expect(ensureAndroidAdbAvailable({
19
+ env: { ANDROID_HOME: sdkRoot },
20
+ })).resolves.toBe(sdkRoot);
21
+ expect(spawnSpy).not.toHaveBeenCalled();
22
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
23
+ });
24
+ it('installs only platform-tools when adb is missing', async () => {
25
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
26
+ const sdkManagerDirectory = path.join(sdkRoot, 'cmdline-tools', 'latest', 'bin');
27
+ fs.mkdirSync(sdkManagerDirectory, { recursive: true });
28
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
29
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
30
+ const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (command, args) => {
31
+ if (command === 'bash' && typeof args?.[1] === 'string') {
32
+ const commandString = args[1];
33
+ if (commandString.includes('platform-tools')) {
34
+ const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
35
+ fs.mkdirSync(path.dirname(adbPath), { recursive: true });
36
+ fs.writeFileSync(adbPath, '');
37
+ }
38
+ }
39
+ return {};
40
+ }));
41
+ await expect(ensureAndroidAdbAvailable({
42
+ env: { ANDROID_HOME: sdkRoot },
43
+ })).resolves.toBe(sdkRoot);
44
+ expect(spawnSpy).toHaveBeenCalledWith('bash', ['-lc', expect.stringContaining('platform-tools')], expect.any(Object));
45
+ expect(spawnSpy.mock.calls.some(([command, args]) => command === 'bash' &&
46
+ typeof args?.[1] === 'string' &&
47
+ args[1].includes('platform-tools') &&
48
+ args[1].includes('emulator'))).toBe(false);
49
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
50
+ });
51
+ it('installs emulator only when emulator is missing', async () => {
52
+ const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
53
+ const sdkManagerDirectory = path.join(sdkRoot, 'cmdline-tools', 'latest', 'bin');
54
+ fs.mkdirSync(sdkManagerDirectory, { recursive: true });
55
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
56
+ fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
57
+ const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (command, args) => {
58
+ if (command === 'bash' && typeof args?.[1] === 'string') {
59
+ const commandString = args[1];
60
+ if (commandString.includes('emulator')) {
61
+ const emulatorPath = path.join(sdkRoot, 'emulator', 'emulator');
62
+ fs.mkdirSync(path.dirname(emulatorPath), { recursive: true });
63
+ fs.writeFileSync(emulatorPath, '');
64
+ }
65
+ }
66
+ return {};
67
+ }));
68
+ await expect(ensureAndroidEmulatorAvailable({
69
+ env: { ANDROID_HOME: sdkRoot },
70
+ })).resolves.toBe(sdkRoot);
71
+ expect(spawnSpy).toHaveBeenCalledWith('bash', ['-lc', expect.stringContaining('emulator')], expect.any(Object));
72
+ fs.rmSync(sdkRoot, { force: true, recursive: true });
73
+ });
8
74
  it('uses the default Unix SDK root when env vars are missing', () => {
9
75
  expect(getDefaultUnixAndroidSdkRoot({
10
76
  platform: 'darwin',
@@ -54,7 +54,7 @@ describe('Android platform instance', () => {
54
54
  activityName: '.MainActivity',
55
55
  }, harnessConfig, init);
56
56
  await instance.dispose();
57
- expect(ensureAndroidEmulatorEnvironment).toHaveBeenCalledWith(35);
57
+ expect(ensureAndroidEmulatorEnvironment).not.toHaveBeenCalled();
58
58
  expect(stopEmulator).not.toHaveBeenCalled();
59
59
  });
60
60
  it('creates and boots an emulator when missing and shuts it down on dispose', async () => {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=targets.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/targets.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getRunTargets } from '../targets.js';
3
+ import * as adb from '../adb.js';
4
+ import * as environment from '../environment.js';
5
+ describe('Android target discovery', () => {
6
+ beforeEach(() => {
7
+ vi.restoreAllMocks();
8
+ vi.unstubAllEnvs();
9
+ });
10
+ it('installs adb and emulator only for discovery', async () => {
11
+ const ensureAndroidAdbAvailable = vi
12
+ .spyOn(environment, 'ensureAndroidAdbAvailable')
13
+ .mockResolvedValue('/tmp/android-sdk');
14
+ const ensureAndroidEmulatorAvailable = vi
15
+ .spyOn(environment, 'ensureAndroidEmulatorAvailable')
16
+ .mockResolvedValue('/tmp/android-sdk');
17
+ vi.spyOn(adb, 'getAvds').mockResolvedValue(['Pixel_8_API_35']);
18
+ vi.spyOn(adb, 'getConnectedDevices').mockResolvedValue([
19
+ {
20
+ id: 'device-1',
21
+ manufacturer: 'Google',
22
+ model: 'Pixel 8',
23
+ },
24
+ ]);
25
+ await expect(getRunTargets()).resolves.toEqual([
26
+ {
27
+ type: 'emulator',
28
+ name: 'Pixel_8_API_35',
29
+ platform: 'android',
30
+ description: 'Android emulator',
31
+ device: {
32
+ name: 'Pixel_8_API_35',
33
+ },
34
+ },
35
+ {
36
+ type: 'physical',
37
+ name: 'Google Pixel 8',
38
+ platform: 'android',
39
+ description: 'Physical device (device-1)',
40
+ device: {
41
+ manufacturer: 'Google',
42
+ model: 'Pixel 8',
43
+ },
44
+ },
45
+ ]);
46
+ expect(ensureAndroidAdbAvailable).toHaveBeenCalledTimes(1);
47
+ expect(ensureAndroidEmulatorAvailable).toHaveBeenCalledTimes(1);
48
+ });
49
+ });
package/dist/adb.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
package/dist/adb.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { spawn, SubprocessError } from '@react-native-harness/tools';
2
2
  import { spawn as nodeSpawn } from 'node:child_process';
3
3
  import { access, rm } from 'node:fs/promises';
4
- import { ensureAndroidSdkPackages, getAdbBinaryPath, getAndroidSystemImagePackage, getAvdManagerBinaryPath, getEmulatorBinaryPath, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, getSdkManagerBinaryPath, } from './environment.js';
4
+ import { getAvdConfigPath, getAvdDirectory, readAvdConfig, } from './avd-config.js';
5
+ import { ensureAndroidEmulatorAvailable, ensureAndroidSdkPackages, getAdbBinaryPath, getAndroidSystemImagePackage, getAvdManagerBinaryPath, getEmulatorBinaryPath, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, } from './environment.js';
5
6
  import { getEmulatorStartupArgs, } from './emulator-startup.js';
6
7
  const wait = async (ms) => {
7
8
  await new Promise((resolve) => {
@@ -24,7 +25,6 @@ const waitWithSignal = async (ms, signal) => {
24
25
  }
25
26
  await Promise.race([wait(ms), waitForAbort(signal)]);
26
27
  };
27
- const getAvdConfigPath = (name) => `${process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`}/${name}.avd/config.ini`;
28
28
  const EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS = 5000;
29
29
  const EMULATOR_OUTPUT_BUFFER_LIMIT = 16 * 1024;
30
30
  export const emulatorProcess = {
@@ -64,16 +64,10 @@ const formatEmulatorStartupError = ({ name, stdout, stderr, exitCode, signal, er
64
64
  });
65
65
  };
66
66
  const ensureEmulatorInstalled = async () => {
67
+ await ensureAndroidEmulatorAvailable();
67
68
  const emulatorBinaryPath = getEmulatorBinaryPath();
68
- try {
69
- await access(emulatorBinaryPath);
70
- return emulatorBinaryPath;
71
- }
72
- catch {
73
- await spawn(getSdkManagerBinaryPath(), ['emulator']);
74
- await access(emulatorBinaryPath);
75
- return emulatorBinaryPath;
76
- }
69
+ await access(emulatorBinaryPath);
70
+ return emulatorBinaryPath;
77
71
  };
78
72
  export const getRequiredEmulatorPackages = (apiLevel) => {
79
73
  return getRequiredAndroidSdkPackages({
@@ -85,6 +79,43 @@ export const getRequiredEmulatorPackages = (apiLevel) => {
85
79
  export const verifyAndroidEmulatorSdk = async (apiLevel) => {
86
80
  await ensureAndroidSdkPackages(getRequiredEmulatorPackages(apiLevel));
87
81
  };
82
+ const listAvdProfiles = async () => {
83
+ const { stdout } = await spawn(getAvdManagerBinaryPath(), ['list', 'device']);
84
+ return stdout
85
+ .split('\n')
86
+ .map((line) => line.match(/^id:\s+\d+\s+or\s+"([^"]+)"/i)?.[1]?.trim())
87
+ .filter((profile) => profile != null && profile !== '');
88
+ };
89
+ const ensureAvdProfileAvailable = async (profile) => {
90
+ const availableProfiles = await listAvdProfiles();
91
+ if (availableProfiles.includes(profile)) {
92
+ return;
93
+ }
94
+ const availableProfilesList = availableProfiles.length > 0
95
+ ? availableProfiles.join(', ')
96
+ : 'None reported by avdmanager.';
97
+ throw new Error(`Android AVD profile "${profile}" is not available on this machine. Available profiles: ${availableProfilesList}`);
98
+ };
99
+ const ensureAvdConfigExists = async (name) => {
100
+ const configPath = getAvdConfigPath(name);
101
+ if ((await readAvdConfig(name)) == null) {
102
+ throw new Error(`Android AVD "${name}" was created, but config.ini was not found at ${configPath}.`);
103
+ }
104
+ return configPath;
105
+ };
106
+ const getAvdIniPath = (name) => {
107
+ const avdHome = process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`;
108
+ return `${avdHome}/${name}.ini`;
109
+ };
110
+ const ensureAvdIniExists = async ({ name, apiLevel, }) => {
111
+ const iniPath = getAvdIniPath(name);
112
+ const avdDirectory = getAvdDirectory(name);
113
+ await spawn('bash', [
114
+ '-lc',
115
+ `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}"`,
116
+ ]);
117
+ return iniPath;
118
+ };
88
119
  export const getStartAppArgs = (bundleId, activityName, options) => {
89
120
  const args = [
90
121
  'shell',
@@ -213,13 +244,16 @@ export const hasAvd = async (name) => {
213
244
  export const createAvd = async ({ name, apiLevel, profile, diskSize, heapSize, }) => {
214
245
  const systemImagePackage = getAndroidSystemImagePackage(apiLevel, getHostAndroidSystemImageArch());
215
246
  await verifyAndroidEmulatorSdk(apiLevel);
247
+ await ensureAvdProfileAvailable(profile);
216
248
  await spawn('bash', [
217
249
  '-lc',
218
- `printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}"`,
250
+ `printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}" -p "${getAvdDirectory(name)}"`,
219
251
  ]);
252
+ await ensureAvdIniExists({ name, apiLevel });
253
+ const configPath = await ensureAvdConfigExists(name);
220
254
  await spawn('bash', [
221
255
  '-lc',
222
- `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${getAvdConfigPath(name)}"`,
256
+ `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${configPath}"`,
223
257
  ]);
224
258
  };
225
259
  export const deleteAvd = async (name) => {
package/dist/config.d.ts CHANGED
@@ -68,8 +68,8 @@ export declare const AndroidEmulatorSchema: z.ZodObject<{
68
68
  } | undefined;
69
69
  }>>;
70
70
  }, "strip", z.ZodTypeAny, {
71
- name: string;
72
71
  type: "emulator";
72
+ name: string;
73
73
  avd?: {
74
74
  apiLevel: number;
75
75
  profile: string;
@@ -80,8 +80,8 @@ export declare const AndroidEmulatorSchema: z.ZodObject<{
80
80
  } | undefined;
81
81
  } | undefined;
82
82
  }, {
83
- name: string;
84
83
  type: "emulator";
84
+ name: string;
85
85
  avd?: {
86
86
  apiLevel: number;
87
87
  profile: string;
@@ -97,13 +97,13 @@ export declare const PhysicalAndroidDeviceSchema: z.ZodObject<{
97
97
  manufacturer: z.ZodString;
98
98
  model: z.ZodString;
99
99
  }, "strip", z.ZodTypeAny, {
100
+ type: "physical";
100
101
  manufacturer: string;
101
102
  model: string;
102
- type: "physical";
103
103
  }, {
104
+ type: "physical";
104
105
  manufacturer: string;
105
106
  model: string;
106
- type: "physical";
107
107
  }>;
108
108
  export declare const AndroidDeviceSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
109
109
  type: z.ZodLiteral<"emulator">;
@@ -138,8 +138,8 @@ export declare const AndroidDeviceSchema: z.ZodDiscriminatedUnion<"type", [z.Zod
138
138
  } | undefined;
139
139
  }>>;
140
140
  }, "strip", z.ZodTypeAny, {
141
- name: string;
142
141
  type: "emulator";
142
+ name: string;
143
143
  avd?: {
144
144
  apiLevel: number;
145
145
  profile: string;
@@ -150,8 +150,8 @@ export declare const AndroidDeviceSchema: z.ZodDiscriminatedUnion<"type", [z.Zod
150
150
  } | undefined;
151
151
  } | undefined;
152
152
  }, {
153
- name: string;
154
153
  type: "emulator";
154
+ name: string;
155
155
  avd?: {
156
156
  apiLevel: number;
157
157
  profile: string;
@@ -166,13 +166,13 @@ export declare const AndroidDeviceSchema: z.ZodDiscriminatedUnion<"type", [z.Zod
166
166
  manufacturer: z.ZodString;
167
167
  model: z.ZodString;
168
168
  }, "strip", z.ZodTypeAny, {
169
+ type: "physical";
169
170
  manufacturer: string;
170
171
  model: string;
171
- type: "physical";
172
172
  }, {
173
+ type: "physical";
173
174
  manufacturer: string;
174
175
  model: string;
175
- type: "physical";
176
176
  }>]>;
177
177
  export declare const AndroidPlatformConfigSchema: z.ZodObject<{
178
178
  name: z.ZodString;
@@ -209,8 +209,8 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
209
209
  } | undefined;
210
210
  }>>;
211
211
  }, "strip", z.ZodTypeAny, {
212
- name: string;
213
212
  type: "emulator";
213
+ name: string;
214
214
  avd?: {
215
215
  apiLevel: number;
216
216
  profile: string;
@@ -221,8 +221,8 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
221
221
  } | undefined;
222
222
  } | undefined;
223
223
  }, {
224
- name: string;
225
224
  type: "emulator";
225
+ name: string;
226
226
  avd?: {
227
227
  apiLevel: number;
228
228
  profile: string;
@@ -237,13 +237,13 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
237
237
  manufacturer: z.ZodString;
238
238
  model: z.ZodString;
239
239
  }, "strip", z.ZodTypeAny, {
240
+ type: "physical";
240
241
  manufacturer: string;
241
242
  model: string;
242
- type: "physical";
243
243
  }, {
244
+ type: "physical";
244
245
  manufacturer: string;
245
246
  model: string;
246
- type: "physical";
247
247
  }>]>;
248
248
  bundleId: z.ZodString;
249
249
  activityName: z.ZodDefault<z.ZodString>;
@@ -257,8 +257,8 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
257
257
  }, "strip", z.ZodTypeAny, {
258
258
  name: string;
259
259
  device: {
260
- name: string;
261
260
  type: "emulator";
261
+ name: string;
262
262
  avd?: {
263
263
  apiLevel: number;
264
264
  profile: string;
@@ -269,9 +269,9 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
269
269
  } | undefined;
270
270
  } | undefined;
271
271
  } | {
272
+ type: "physical";
272
273
  manufacturer: string;
273
274
  model: string;
274
- type: "physical";
275
275
  };
276
276
  bundleId: string;
277
277
  activityName: string;
@@ -281,8 +281,8 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
281
281
  }, {
282
282
  name: string;
283
283
  device: {
284
- name: string;
285
284
  type: "emulator";
285
+ name: string;
286
286
  avd?: {
287
287
  apiLevel: number;
288
288
  profile: string;
@@ -293,9 +293,9 @@ export declare const AndroidPlatformConfigSchema: z.ZodObject<{
293
293
  } | undefined;
294
294
  } | undefined;
295
295
  } | {
296
+ type: "physical";
296
297
  manufacturer: string;
297
298
  model: string;
298
- type: "physical";
299
299
  };
300
300
  bundleId: string;
301
301
  activityName?: string | undefined;
@@ -15,6 +15,9 @@ export declare const getRequiredAndroidSdkPackages: ({ apiLevel, includeEmulator
15
15
  architecture?: AndroidSystemImageArch;
16
16
  }) => string[];
17
17
  export declare const ensureAndroidSdkPackages: (packages: readonly string[], { env, platform, homeDirectory, }?: AndroidSdkRootOptions) => Promise<string>;
18
+ export declare const ensureAndroidAdbAvailable: (options?: AndroidSdkRootOptions) => Promise<string>;
19
+ export declare const ensureAndroidEmulatorAvailable: (options?: AndroidSdkRootOptions) => Promise<string>;
20
+ export declare const ensureAndroidAvdProvisioningAvailable: (apiLevel: number, architecture?: AndroidSystemImageArch, options?: AndroidSdkRootOptions) => Promise<string>;
18
21
  export declare const ensureAndroidDiscoveryEnvironment: () => Promise<string>;
19
22
  export declare const ensureAndroidPhysicalDeviceEnvironment: () => Promise<string>;
20
23
  export declare const ensureAndroidEmulatorEnvironment: (apiLevel: number) => Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAE5E,KAAK,qBAAqB,GAAG;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAQF,eAAO,MAAM,4BAA4B,GAAI,+BAG1C,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAM,KAAG,MAAM,GAAG,IAUrD,CAAC;AAwRF,eAAO,MAAM,iBAAiB,GAC5B,MAAK,MAAM,CAAC,UAAwB,EACpC,UAAS,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAM,KAC/C,MAAM,GAAG,IAIX,CAAC;AAiBF,eAAO,MAAM,6BAA6B,GACxC,eAAc,MAAqB,KAClC,sBAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,UAAU,MAAM,KAAG,MAE5D,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,UAAU,MAAM,EAChB,eAAc,sBAAwD,KACrE,MAEF,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,+CAI3C;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAClC,KAAG,MAAM,EAed,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,UAAU,SAAS,MAAM,EAAE,EAC3B,oCAIG,qBAA0B,KAC5B,OAAO,CAAC,MAAM,CA0BhB,CAAC;AAEF,eAAO,MAAM,iCAAiC,QAAa,OAAO,CAAC,MAAM,CAMxE,CAAC;AAEF,eAAO,MAAM,sCAAsC,QACvC,OAAO,CAAC,MAAM,CAIvB,CAAC;AAEJ,eAAO,MAAM,gCAAgC,GAC3C,UAAU,MAAM,KACf,OAAO,CAAC,MAAM,CAShB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,CAAC,UA2BT,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAO,IAE9C,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,UAAS,MAAoC,KAC5C,MAAqD,CAAC;AAEzD,eAAO,MAAM,qBAAqB,GAChC,UAAS,MAAoC,KAC5C,MAAoD,CAAC;AAExD,eAAO,MAAM,uBAAuB,GAClC,UAAS,MAAoC,KAC5C,MACsE,CAAC;AAE1E,eAAO,MAAM,uBAAuB,GAClC,UAAS,MAAoC,KAC5C,MACsE,CAAC"}
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAE5E,KAAK,qBAAqB,GAAG;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAQF,eAAO,MAAM,4BAA4B,GAAI,+BAG1C,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAM,KAAG,MAAM,GAAG,IAUrD,CAAC;AAwRF,eAAO,MAAM,iBAAiB,GAC5B,MAAK,MAAM,CAAC,UAAwB,EACpC,UAAS,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAM,KAC/C,MAAM,GAAG,IAIX,CAAC;AAiBF,eAAO,MAAM,6BAA6B,GACxC,eAAc,MAAqB,KAClC,sBAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,UAAU,MAAM,KAAG,MAE5D,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,UAAU,MAAM,EAChB,eAAc,sBAAwD,KACrE,MAEF,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,+CAI3C;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAClC,KAAG,MAAM,EAed,CAAC;AAoBF,eAAO,MAAM,wBAAwB,GACnC,UAAU,SAAS,MAAM,EAAE,EAC3B,oCAIG,qBAA0B,KAC5B,OAAO,CAAC,MAAM,CA8BhB,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,UAAS,qBAA0B,KAClC,OAAO,CAAC,MAAM,CAEhB,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,UAAS,qBAA0B,KAClC,OAAO,CAAC,MAAM,CAEhB,CAAC;AAEF,eAAO,MAAM,qCAAqC,GAChD,UAAU,MAAM,EAChB,eAAc,sBAAwD,EACtE,UAAS,qBAA0B,KAClC,OAAO,CAAC,MAAM,CAQhB,CAAC;AAEF,eAAO,MAAM,iCAAiC,QAAa,OAAO,CAAC,MAAM,CAIxE,CAAC;AAEF,eAAO,MAAM,sCAAsC,QACvC,OAAO,CAAC,MAAM,CAIvB,CAAC;AAEJ,eAAO,MAAM,gCAAgC,GAC3C,UAAU,MAAM,KACf,OAAO,CAAC,MAAM,CAOhB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,CAAC,UA2BT,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAO,IAE9C,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,UAAS,MAAoC,KAC5C,MAAqD,CAAC;AAEzD,eAAO,MAAM,qBAAqB,GAChC,UAAS,MAAoC,KAC5C,MAAoD,CAAC;AAExD,eAAO,MAAM,uBAAuB,GAClC,UAAS,MAAoC,KAC5C,MACsE,CAAC;AAE1E,eAAO,MAAM,uBAAuB,GAClC,UAAS,MAAoC,KAC5C,MACsE,CAAC"}
@@ -232,34 +232,56 @@ export const getRequiredAndroidSdkPackages = ({ apiLevel, includeEmulator = fals
232
232
  }
233
233
  return packages;
234
234
  };
235
- export const ensureAndroidSdkPackages = async (packages, { env = process.env, platform = process.platform, homeDirectory = os.homedir(), } = {}) => {
235
+ const getMissingAndroidSdkPackagesForEnvironment = async (packages, { env = process.env, platform = process.platform, homeDirectory = os.homedir(), } = {}) => {
236
236
  const sdkRoot = getRequiredAndroidSdkRoot(env, { platform, homeDirectory });
237
237
  await mkdir(sdkRoot, { recursive: true });
238
- await ensureAndroidCommandLineTools(sdkRoot, platform);
239
- const missingPackages = await getMissingAndroidSdkPackages(sdkRoot, packages);
240
- if (missingPackages.length > 0) {
241
- await installAndroidSdkPackages(sdkRoot, missingPackages);
238
+ return {
239
+ sdkRoot,
240
+ missingPackages: await getMissingAndroidSdkPackages(sdkRoot, packages),
241
+ };
242
+ };
243
+ export const ensureAndroidSdkPackages = async (packages, { env = process.env, platform = process.platform, homeDirectory = os.homedir(), } = {}) => {
244
+ const { sdkRoot, missingPackages } = await getMissingAndroidSdkPackagesForEnvironment(packages, {
245
+ env,
246
+ platform,
247
+ homeDirectory,
248
+ });
249
+ if (missingPackages.length === 0) {
250
+ return sdkRoot;
242
251
  }
252
+ await ensureAndroidCommandLineTools(sdkRoot, platform);
253
+ await installAndroidSdkPackages(sdkRoot, missingPackages);
243
254
  const unresolvedPackages = await getMissingAndroidSdkPackages(sdkRoot, packages);
244
255
  if (unresolvedPackages.length > 0) {
245
256
  throw new Error(`Android SDK packages are still missing after installation: ${unresolvedPackages.join(', ')}`);
246
257
  }
247
258
  return sdkRoot;
248
259
  };
260
+ export const ensureAndroidAdbAvailable = async (options = {}) => {
261
+ return ensureAndroidSdkPackages(['platform-tools'], options);
262
+ };
263
+ export const ensureAndroidEmulatorAvailable = async (options = {}) => {
264
+ return ensureAndroidSdkPackages(['emulator'], options);
265
+ };
266
+ export const ensureAndroidAvdProvisioningAvailable = async (apiLevel, architecture = getHostAndroidSystemImageArch(), options = {}) => {
267
+ return ensureAndroidSdkPackages([
268
+ getAndroidPlatformPackage(apiLevel),
269
+ getAndroidSystemImagePackage(apiLevel, architecture),
270
+ ], options);
271
+ };
249
272
  export const ensureAndroidDiscoveryEnvironment = async () => {
250
273
  initializeAndroidProcessEnv();
251
- return ensureAndroidSdkPackages(getRequiredAndroidSdkPackages({ includeEmulator: true }));
274
+ return ensureAndroidAdbAvailable();
252
275
  };
253
276
  export const ensureAndroidPhysicalDeviceEnvironment = async () => {
254
277
  initializeAndroidProcessEnv();
255
- return ensureAndroidSdkPackages(getRequiredAndroidSdkPackages());
278
+ return ensureAndroidAdbAvailable();
256
279
  };
257
280
  export const ensureAndroidEmulatorEnvironment = async (apiLevel) => {
258
281
  initializeAndroidProcessEnv();
259
- return ensureAndroidSdkPackages(getRequiredAndroidSdkPackages({
260
- apiLevel,
261
- includeEmulator: true,
262
- }));
282
+ await ensureAndroidAdbAvailable();
283
+ await ensureAndroidEmulatorAvailable();
284
+ return ensureAndroidAvdProvisioningAvailable(apiLevel);
263
285
  };
264
286
  export const getAndroidProcessEnv = (env = process.env) => {
265
287
  const sdkRoot = getAndroidSdkRoot(env);
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,0BAA0B,EAC/B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,EACL,qBAAqB,EAGtB,MAAM,aAAa,CAAC;AAgKrB,eAAO,MAAM,kCAAkC,GAC7C,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,EAC5B,MAAM,0BAA0B,KAC/B,OAAO,CAAC,qBAAqB,CAoI/B,CAAC;AAEF,eAAO,MAAM,wCAAwC,GACnD,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,KAC3B,OAAO,CAAC,qBAAqB,CAiE/B,CAAC"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,0BAA0B,EAC/B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,EACL,qBAAqB,EAGtB,MAAM,aAAa,CAAC;AAkKrB,eAAO,MAAM,kCAAkC,GAC7C,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,EAC5B,MAAM,0BAA0B,KAC/B,OAAO,CAAC,qBAAqB,CAkI/B,CAAC;AAEF,eAAO,MAAM,wCAAwC,GACnD,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,KAC3B,OAAO,CAAC,qBAAqB,CAiE/B,CAAC"}
package/dist/instance.js CHANGED
@@ -8,7 +8,7 @@ import { applyHarnessDebugHttpHost, clearHarnessDebugHttpHost, } from './shared-
8
8
  import { getDeviceName } from './utils.js';
9
9
  import { createAndroidAppMonitor } from './app-monitor.js';
10
10
  import { HarnessAppPathError, HarnessEmulatorConfigError } from './errors.js';
11
- import { ensureAndroidEmulatorEnvironment, getHostAndroidSystemImageArch, } from './environment.js';
11
+ import { ensureAndroidEmulatorAvailable, ensureAndroidEmulatorEnvironment, getHostAndroidSystemImageArch, } from './environment.js';
12
12
  import { isInteractive } from '@react-native-harness/tools';
13
13
  import fs from 'node:fs';
14
14
  const androidInstanceLogger = logger.child('android-instance');
@@ -40,6 +40,7 @@ const configureAndroidRuntime = async (adbId, config, harnessConfig) => {
40
40
  return adb.getAppUid(adbId, config.bundleId);
41
41
  };
42
42
  const startAndWaitForBoot = async ({ emulatorName, signal, mode, }) => {
43
+ await ensureAndroidEmulatorAvailable();
43
44
  await adb.startEmulator(emulatorName, mode);
44
45
  return adb.waitForBoot(emulatorName, signal);
45
46
  };
@@ -135,9 +136,6 @@ export const getAndroidEmulatorPlatformInstance = async (config, harnessConfig,
135
136
  startedByHarness = true;
136
137
  androidInstanceLogger.debug('Android emulator %s connected as %s', emulatorConfig.name, adbId);
137
138
  }
138
- else if (emulatorConfig.avd) {
139
- await ensureAndroidEmulatorEnvironment(emulatorConfig.avd.apiLevel);
140
- }
141
139
  if (!adbId) {
142
140
  throw new DeviceNotFoundError(getDeviceName(emulatorConfig));
143
141
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,EAChC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,aAAa,CAAC;AAWrB,QAAA,MAAM,gBAAgB,GACpB,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,EAC5B,MAAM,0BAA0B,KAC/B,OAAO,CAAC,qBAAqB,CAoB/B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,EAChC,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,aAAa,CAAC;AAUrB,QAAA,MAAM,gBAAgB,GACpB,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,EAC5B,MAAM,0BAA0B,KAC/B,OAAO,CAAC,qBAAqB,CAgB/B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}