@react-native-harness/platform-android 1.1.0-rc.4 → 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.
@@ -17,6 +17,7 @@ import {
17
17
  } from '../adb.js';
18
18
  import * as tools from '@react-native-harness/tools';
19
19
  import * as environment from '../environment.js';
20
+ import * as avdConfig from '../avd-config.js';
20
21
 
21
22
  const createAbortError = () =>
22
23
  new DOMException('The operation was aborted', 'AbortError');
@@ -48,7 +49,7 @@ describe('getStartAppArgs', () => {
48
49
  user_id: 42,
49
50
  mode: 'debug',
50
51
  },
51
- })
52
+ }),
52
53
  ).toEqual([
53
54
  'shell',
54
55
  'am',
@@ -77,7 +78,7 @@ describe('getStartAppArgs', () => {
77
78
  extras: {
78
79
  count: Number.MAX_SAFE_INTEGER + 1,
79
80
  },
80
- })
81
+ }),
81
82
  ).toThrow('must be a safe integer');
82
83
  });
83
84
 
@@ -88,7 +89,7 @@ describe('getStartAppArgs', () => {
88
89
  } as Awaited<ReturnType<typeof tools.spawn>>);
89
90
 
90
91
  await expect(getAppUid('emulator-5554', 'com.example.app')).resolves.toBe(
91
- 10234
92
+ 10234,
92
93
  );
93
94
 
94
95
  expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
@@ -108,7 +109,7 @@ describe('getStartAppArgs', () => {
108
109
  } as Awaited<ReturnType<typeof tools.spawn>>);
109
110
 
110
111
  await expect(getLogcatTimestamp('emulator-5554')).resolves.toBe(
111
- '03-12 11:35:08.000'
112
+ '03-12 11:35:08.000',
112
113
  );
113
114
 
114
115
  expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
@@ -146,14 +147,19 @@ describe('getStartAppArgs', () => {
146
147
  });
147
148
 
148
149
  it('creates an AVD and appends config overrides', async () => {
150
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
149
151
  const spawnSpy = vi
150
152
  .spyOn(tools, 'spawn')
151
- .mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>);
153
+ .mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
154
+ .mockResolvedValueOnce({
155
+ stdout:
156
+ 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
157
+ } as Awaited<ReturnType<typeof tools.spawn>>);
152
158
  const verifyAndroidEmulatorSdk = vi
153
159
  .spyOn(environment, 'ensureAndroidSdkPackages')
154
160
  .mockResolvedValue('/tmp/android-sdk');
155
161
  vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
156
- 'x86_64'
162
+ 'x86_64',
157
163
  );
158
164
 
159
165
  await createAvd({
@@ -170,29 +176,128 @@ describe('getStartAppArgs', () => {
170
176
  'platforms;android-35',
171
177
  'system-images;android-35;default;x86_64',
172
178
  ]);
173
- expect(spawnSpy).toHaveBeenNthCalledWith(1, 'bash', [
179
+ expect(spawnSpy).toHaveBeenNthCalledWith(
180
+ 1,
181
+ expect.stringMatching(/avdmanager$/),
182
+ ['list', 'device'],
183
+ );
184
+ expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
174
185
  '-lc',
175
186
  expect.stringContaining(
176
- 'create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8"'
187
+ 'create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8" -p ',
177
188
  ),
178
189
  ]);
179
- expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
190
+ expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
191
+ '-lc',
192
+ expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
193
+ ]);
194
+ expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
180
195
  '-lc',
181
196
  expect.stringContaining(
182
- `'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `
197
+ `'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `,
183
198
  ),
184
199
  ]);
185
200
  });
186
201
 
187
- it('creates an AVD with arm64 system image packages on arm64 hosts', async () => {
188
- vi.spyOn(tools, 'spawn').mockResolvedValue(
189
- {} as Awaited<ReturnType<typeof tools.spawn>>
202
+ it('recreates the companion ini file when avdmanager leaves it missing', async () => {
203
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
204
+ const spawnSpy = vi
205
+ .spyOn(tools, 'spawn')
206
+ .mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
207
+ .mockResolvedValueOnce({
208
+ stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
209
+ } as Awaited<ReturnType<typeof tools.spawn>>);
210
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
211
+ '/tmp/android-sdk',
212
+ );
213
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
214
+ 'x86_64',
190
215
  );
216
+
217
+ await createAvd({
218
+ name: 'Pixel_6_API_35',
219
+ apiLevel: 35,
220
+ profile: 'pixel_6',
221
+ diskSize: '1G',
222
+ heapSize: '1G',
223
+ });
224
+
225
+ expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
226
+ '-lc',
227
+ expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
228
+ ]);
229
+ expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
230
+ '-lc',
231
+ expect.stringContaining(
232
+ `'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `,
233
+ ),
234
+ ]);
235
+ });
236
+
237
+ it('throws an actionable error when the requested AVD profile is unavailable', async () => {
238
+ vi.spyOn(tools, 'spawn').mockResolvedValueOnce({
239
+ stdout:
240
+ 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
241
+ } as Awaited<ReturnType<typeof tools.spawn>>);
242
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
243
+ '/tmp/android-sdk',
244
+ );
245
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
246
+ 'x86_64',
247
+ );
248
+
249
+ await expect(
250
+ createAvd({
251
+ name: 'Pixel_8_Pro_API_35',
252
+ apiLevel: 35,
253
+ profile: 'pixel_8_pro',
254
+ diskSize: '1G',
255
+ heapSize: '1G',
256
+ }),
257
+ ).rejects.toThrow(
258
+ 'Android AVD profile "pixel_8_pro" is not available on this machine. Available profiles: pixel_8, pixel_6',
259
+ );
260
+ });
261
+
262
+ it('throws a clear error when avdmanager does not create config.ini', async () => {
263
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue(null);
264
+ vi.spyOn(tools, 'spawn')
265
+ .mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
266
+ .mockResolvedValueOnce({
267
+ stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
268
+ } as Awaited<ReturnType<typeof tools.spawn>>);
269
+ vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
270
+ '/tmp/android-sdk',
271
+ );
272
+ vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
273
+ 'x86_64',
274
+ );
275
+
276
+ await expect(
277
+ createAvd({
278
+ name: 'Pixel_6_API_35',
279
+ apiLevel: 35,
280
+ profile: 'pixel_6',
281
+ diskSize: '1G',
282
+ heapSize: '1G',
283
+ }),
284
+ ).rejects.toThrow(
285
+ 'Android AVD "Pixel_6_API_35" was created, but config.ini was not found at ',
286
+ );
287
+ });
288
+
289
+ it('creates an AVD with arm64 system image packages on arm64 hosts', async () => {
290
+ vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
291
+ vi.spyOn(tools, 'spawn')
292
+ .mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
293
+ .mockResolvedValueOnce({
294
+ stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\n',
295
+ } as Awaited<ReturnType<typeof tools.spawn>>);
191
296
  const ensureAndroidSdkPackages = vi
192
297
  .spyOn(environment, 'ensureAndroidSdkPackages')
193
298
  .mockResolvedValue('/tmp/android-sdk');
194
299
  vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
195
- 'arm64-v8a'
300
+ 'arm64-v8a',
196
301
  );
197
302
 
198
303
  await createAvd({
@@ -241,7 +346,7 @@ describe('getStartAppArgs', () => {
241
346
  child.emit('close', 1, null);
242
347
 
243
348
  await expect(startPromise).rejects.toThrow(
244
- 'Unknown AVD name [Pixel_8_API_35]'
349
+ 'Unknown AVD name [Pixel_8_API_35]',
245
350
  );
246
351
  });
247
352
 
@@ -271,7 +376,7 @@ describe('getStartAppArgs', () => {
271
376
  child.emit('close', 1, null);
272
377
 
273
378
  await expect(startPromise).rejects.toThrow(
274
- 'emulator: panic: broken config'
379
+ 'emulator: panic: broken config',
275
380
  );
276
381
  });
277
382
 
@@ -291,7 +396,7 @@ describe('getStartAppArgs', () => {
291
396
  vi.spyOn(emulatorProcess, 'startDetachedProcess').mockReturnValue(
292
397
  child as unknown as ReturnType<
293
398
  typeof emulatorProcess.startDetachedProcess
294
- >
399
+ >,
295
400
  );
296
401
 
297
402
  const startPromise = startEmulator('Pixel_8_API_35');
@@ -317,7 +422,7 @@ describe('getStartAppArgs', () => {
317
422
  .mockReturnValue(
318
423
  child as unknown as ReturnType<
319
424
  typeof emulatorProcess.startDetachedProcess
320
- >
425
+ >,
321
426
  );
322
427
 
323
428
  const startPromise = startEmulator('Pixel_8_API_35');
@@ -326,7 +431,7 @@ describe('getStartAppArgs', () => {
326
431
 
327
432
  expect(startDetachedProcess).toHaveBeenCalledWith(
328
433
  expect.stringMatching(/emulator$/),
329
- expect.arrayContaining(['-no-snapshot-load', '-no-snapshot-save'])
434
+ expect.arrayContaining(['-no-snapshot-load', '-no-snapshot-save']),
330
435
  );
331
436
  });
332
437
 
@@ -345,22 +450,22 @@ describe('getStartAppArgs', () => {
345
450
  .mockReturnValue(
346
451
  child as unknown as ReturnType<
347
452
  typeof emulatorProcess.startDetachedProcess
348
- >
453
+ >,
349
454
  );
350
455
 
351
456
  const startPromise = startEmulator(
352
457
  'Pixel_8_API_35',
353
- 'clean-snapshot-generation'
458
+ 'clean-snapshot-generation',
354
459
  );
355
460
  await vi.runAllTimersAsync();
356
461
  await startPromise;
357
462
 
358
463
  expect(startDetachedProcess).toHaveBeenCalledWith(
359
464
  expect.stringMatching(/emulator$/),
360
- expect.arrayContaining(['-no-snapshot-load'])
465
+ expect.arrayContaining(['-no-snapshot-load']),
361
466
  );
362
467
  expect(startDetachedProcess.mock.calls[0]?.[1]).not.toContain(
363
- '-no-snapshot-save'
468
+ '-no-snapshot-save',
364
469
  );
365
470
  });
366
471
 
@@ -379,7 +484,7 @@ describe('getStartAppArgs', () => {
379
484
  .mockReturnValue(
380
485
  child as unknown as ReturnType<
381
486
  typeof emulatorProcess.startDetachedProcess
382
- >
487
+ >,
383
488
  );
384
489
 
385
490
  const startPromise = startEmulator('Pixel_8_API_35', 'snapshot-reuse');
@@ -388,10 +493,10 @@ describe('getStartAppArgs', () => {
388
493
 
389
494
  expect(startDetachedProcess).toHaveBeenCalledWith(
390
495
  expect.stringMatching(/emulator$/),
391
- expect.arrayContaining(['-no-snapshot-save'])
496
+ expect.arrayContaining(['-no-snapshot-save']),
392
497
  );
393
498
  expect(startDetachedProcess.mock.calls[0]?.[1]).not.toContain(
394
- '-no-snapshot-load'
499
+ '-no-snapshot-load',
395
500
  );
396
501
  });
397
502
 
@@ -459,7 +564,7 @@ describe('getStartAppArgs', () => {
459
564
 
460
565
  const waitPromise = waitForBoot(
461
566
  'Pixel_8_API_35',
462
- new AbortController().signal
567
+ new AbortController().signal,
463
568
  );
464
569
 
465
570
  await vi.advanceTimersByTimeAsync(1000);
@@ -482,7 +587,7 @@ describe('getStartAppArgs', () => {
482
587
 
483
588
  const waitPromise = waitForEmulatorDisconnect(
484
589
  'emulator-5554',
485
- new AbortController().signal
590
+ new AbortController().signal,
486
591
  );
487
592
 
488
593
  await vi.advanceTimersByTimeAsync(1000);
package/src/adb.ts CHANGED
@@ -4,6 +4,11 @@ import { spawn as nodeSpawn } from 'node:child_process';
4
4
  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
+ import {
8
+ getAvdConfigPath,
9
+ getAvdDirectory,
10
+ readAvdConfig,
11
+ } from './avd-config.js';
7
12
  import {
8
13
  ensureAndroidEmulatorAvailable,
9
14
  ensureAndroidSdkPackages,
@@ -52,11 +57,6 @@ 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
 
@@ -160,6 +160,68 @@ export const verifyAndroidEmulatorSdk = async (
160
160
  await ensureAndroidSdkPackages(getRequiredEmulatorPackages(apiLevel));
161
161
  };
162
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
+
163
225
  export const getStartAppArgs = (
164
226
  bundleId: string,
165
227
  activityName: string,
@@ -354,15 +416,16 @@ export const createAvd = async ({
354
416
  );
355
417
 
356
418
  await verifyAndroidEmulatorSdk(apiLevel);
419
+ await ensureAvdProfileAvailable(profile);
357
420
  await spawn('bash', [
358
421
  '-lc',
359
- `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)}"`,
360
423
  ]);
424
+ await ensureAvdIniExists({ name, apiLevel });
425
+ const configPath = await ensureAvdConfigExists(name);
361
426
  await spawn('bash', [
362
427
  '-lc',
363
- `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${getAvdConfigPath(
364
- name,
365
- )}"`,
428
+ `printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${configPath}"`,
366
429
  ]);
367
430
  };
368
431
 
@@ -1,5 +0,0 @@
1
- import { Config, NativeTestRunnerConfig } from './types.js';
2
- export declare function assertNativeRunner(config: Config): asserts config is Config & {
3
- runner: NativeTestRunnerConfig;
4
- };
5
- //# sourceMappingURL=assertions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE5D,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,IAAI,MAAM,GAAG;IAAE,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAO/D"}
@@ -1,6 +0,0 @@
1
- export function assertNativeRunner(config) {
2
- if (config.runner.platform !== 'ios' &&
3
- config.runner.platform !== 'android') {
4
- throw new Error('Runner is not a native runner');
5
- }
6
- }
@@ -1,6 +0,0 @@
1
- export type AndroidEmulator = {
2
- adbId: string;
3
- stop: () => Promise<void>;
4
- };
5
- export declare const runEmulator: (avdName: string) => Promise<AndroidEmulator>;
6
- //# sourceMappingURL=emulator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../src/emulator.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,SAAS,MAAM,KACd,OAAO,CAAC,eAAe,CA8BzB,CAAC"}
package/dist/emulator.js DELETED
@@ -1,27 +0,0 @@
1
- import { spawn } from '@react-native-harness/tools';
2
- import * as adb from './adb.js';
3
- export const runEmulator = async (avdName) => {
4
- const process = spawn('emulator', ['-avd', avdName]);
5
- await process.nodeChildProcess;
6
- const adbId = await adb.getEmulatorName(avdName);
7
- if (!adbId) {
8
- throw new Error('Emulator not found');
9
- }
10
- // Poll for emulator status until it's fully running
11
- const checkStatus = async () => {
12
- const status = await adb.isBootCompleted(adbId);
13
- if (!status) {
14
- await new Promise((resolve) => setTimeout(resolve, 2000));
15
- await checkStatus();
16
- }
17
- };
18
- // Start checking status after a brief delay to allow emulator to start
19
- await new Promise((resolve) => setTimeout(resolve, 3000));
20
- await checkStatus();
21
- return {
22
- adbId,
23
- stop: async () => {
24
- await adb.stopEmulator(adbId);
25
- },
26
- };
27
- };
package/dist/reader.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Config } from './types.js';
2
- export declare const getConfig: (dir: string) => Promise<{
3
- config: Config;
4
- projectRoot: string;
5
- }>;
6
- //# sourceMappingURL=reader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAsElD,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,KACV,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAUjD,CAAC"}
package/dist/reader.js DELETED
@@ -1,57 +0,0 @@
1
- import { ConfigSchema } from './types.js';
2
- import { ConfigValidationError, ConfigNotFoundError, ConfigLoadError, } from './errors.js';
3
- import path from 'node:path';
4
- import fs from 'node:fs';
5
- import { createRequire } from 'node:module';
6
- import { ZodError } from 'zod';
7
- const extensions = ['.js', '.mjs', '.cjs', '.json'];
8
- const importUp = async (dir, name) => {
9
- const filePath = path.join(dir, name);
10
- for (const ext of extensions) {
11
- const filePathWithExt = `${filePath}${ext}`;
12
- if (fs.existsSync(filePathWithExt)) {
13
- let rawConfig;
14
- try {
15
- if (ext === '.mjs') {
16
- rawConfig = await import(filePathWithExt).then((module) => module.default);
17
- }
18
- else {
19
- const require = createRequire(import.meta.url);
20
- rawConfig = require(filePathWithExt);
21
- }
22
- }
23
- catch (error) {
24
- throw new ConfigLoadError(filePathWithExt, error instanceof Error ? error : undefined);
25
- }
26
- try {
27
- const config = ConfigSchema.parse(rawConfig);
28
- return { config, filePathWithExt, configDir: dir };
29
- }
30
- catch (error) {
31
- if (error instanceof ZodError) {
32
- const validationErrors = error.errors.map((err) => {
33
- const path = err.path.length > 0 ? ` at "${err.path.join('.')}"` : '';
34
- return `${err.message}${path}`;
35
- });
36
- throw new ConfigValidationError(filePathWithExt, validationErrors);
37
- }
38
- throw error;
39
- }
40
- }
41
- }
42
- const parentDir = path.dirname(dir);
43
- if (parentDir === dir) {
44
- throw new ConfigNotFoundError(dir);
45
- }
46
- return importUp(parentDir, name);
47
- };
48
- export const getConfig = async (dir) => {
49
- const { config, configDir } = await importUp(dir, 'rn-harness.config');
50
- return {
51
- config: {
52
- ...config,
53
- reporter: config.reporter,
54
- },
55
- projectRoot: configDir,
56
- };
57
- };