@react-native-harness/platform-android 1.1.0-rc.4 → 1.2.0-rc.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.
- package/dist/__tests__/adb.test.js +170 -6
- package/dist/__tests__/emulator-startup.test.js +1 -0
- package/dist/__tests__/instance.test.js +130 -0
- package/dist/adb-errors.d.ts +16 -0
- package/dist/adb-errors.d.ts.map +1 -0
- package/dist/adb-errors.js +35 -0
- package/dist/adb-id.d.ts.map +1 -1
- package/dist/adb-id.js +3 -0
- package/dist/adb.d.ts +2 -0
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +163 -4
- package/dist/config.d.ts +16 -16
- package/dist/emulator-startup.d.ts.map +1 -1
- package/dist/emulator-startup.js +0 -2
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +25 -2
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/__tests__/adb.test.ts +235 -28
- package/src/__tests__/emulator-startup.test.ts +3 -0
- package/src/__tests__/instance.test.ts +188 -0
- package/src/adb-errors.ts +47 -0
- package/src/adb-id.ts +4 -0
- package/src/adb.ts +232 -10
- package/src/emulator-startup.ts +0 -2
- package/src/instance.ts +30 -2
- package/dist/assertions.d.ts +0 -5
- package/dist/assertions.d.ts.map +0 -1
- package/dist/assertions.js +0 -6
- package/dist/emulator.d.ts +0 -6
- package/dist/emulator.d.ts.map +0 -1
- package/dist/emulator.js +0 -27
- package/dist/reader.d.ts +0 -6
- package/dist/reader.d.ts.map +0 -1
- package/dist/reader.js +0 -57
- package/dist/types.d.ts +0 -381
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -107
package/src/adb.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
|
|
2
|
-
import { spawn, SubprocessError } from '@react-native-harness/tools';
|
|
2
|
+
import { logger, spawn, SubprocessError } from '@react-native-harness/tools';
|
|
3
3
|
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,
|
|
@@ -18,6 +23,10 @@ import {
|
|
|
18
23
|
getEmulatorStartupArgs,
|
|
19
24
|
type EmulatorBootMode,
|
|
20
25
|
} from './emulator-startup.js';
|
|
26
|
+
import {
|
|
27
|
+
AdbAppNotInstalledError,
|
|
28
|
+
AdbPermissionGrantError,
|
|
29
|
+
} from './adb-errors.js';
|
|
21
30
|
|
|
22
31
|
const wait = async (ms: number): Promise<void> => {
|
|
23
32
|
await new Promise((resolve) => {
|
|
@@ -52,13 +61,9 @@ const waitWithSignal = async (
|
|
|
52
61
|
await Promise.race([wait(ms), waitForAbort(signal)]);
|
|
53
62
|
};
|
|
54
63
|
|
|
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
64
|
const EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS = 5000;
|
|
61
65
|
const EMULATOR_OUTPUT_BUFFER_LIMIT = 16 * 1024;
|
|
66
|
+
const androidAdbLogger = logger.child('android-adb');
|
|
62
67
|
|
|
63
68
|
export const emulatorProcess = {
|
|
64
69
|
startDetachedProcess: (
|
|
@@ -160,6 +165,68 @@ export const verifyAndroidEmulatorSdk = async (
|
|
|
160
165
|
await ensureAndroidSdkPackages(getRequiredEmulatorPackages(apiLevel));
|
|
161
166
|
};
|
|
162
167
|
|
|
168
|
+
const listAvdProfiles = async (): Promise<string[]> => {
|
|
169
|
+
const { stdout } = await spawn(getAvdManagerBinaryPath(), ['list', 'device']);
|
|
170
|
+
|
|
171
|
+
return stdout
|
|
172
|
+
.split('\n')
|
|
173
|
+
.map((line) => line.match(/^id:\s+\d+\s+or\s+"([^"]+)"/i)?.[1]?.trim())
|
|
174
|
+
.filter((profile): profile is string => profile != null && profile !== '');
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const ensureAvdProfileAvailable = async (profile: string): Promise<void> => {
|
|
178
|
+
const availableProfiles = await listAvdProfiles();
|
|
179
|
+
|
|
180
|
+
if (availableProfiles.includes(profile)) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const availableProfilesList =
|
|
185
|
+
availableProfiles.length > 0
|
|
186
|
+
? availableProfiles.join(', ')
|
|
187
|
+
: 'None reported by avdmanager.';
|
|
188
|
+
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Android AVD profile "${profile}" is not available on this machine. Available profiles: ${availableProfilesList}`,
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const ensureAvdConfigExists = async (name: string): Promise<string> => {
|
|
195
|
+
const configPath = getAvdConfigPath(name);
|
|
196
|
+
|
|
197
|
+
if ((await readAvdConfig(name)) == null) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Android AVD "${name}" was created, but config.ini was not found at ${configPath}.`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return configPath;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const getAvdIniPath = (name: string): string => {
|
|
207
|
+
const avdHome =
|
|
208
|
+
process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`;
|
|
209
|
+
return `${avdHome}/${name}.ini`;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const ensureAvdIniExists = async ({
|
|
213
|
+
name,
|
|
214
|
+
apiLevel,
|
|
215
|
+
}: {
|
|
216
|
+
name: string;
|
|
217
|
+
apiLevel: number;
|
|
218
|
+
}): Promise<string> => {
|
|
219
|
+
const iniPath = getAvdIniPath(name);
|
|
220
|
+
|
|
221
|
+
const avdDirectory = getAvdDirectory(name);
|
|
222
|
+
await spawn('bash', [
|
|
223
|
+
'-lc',
|
|
224
|
+
`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}"`,
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
return iniPath;
|
|
228
|
+
};
|
|
229
|
+
|
|
163
230
|
export const getStartAppArgs = (
|
|
164
231
|
bundleId: string,
|
|
165
232
|
activityName: string,
|
|
@@ -312,6 +379,79 @@ export const getDeviceInfo = async (
|
|
|
312
379
|
return { manufacturer, model };
|
|
313
380
|
};
|
|
314
381
|
|
|
382
|
+
const getRequestedPermissions = async (
|
|
383
|
+
adbId: string,
|
|
384
|
+
bundleId: string,
|
|
385
|
+
): Promise<string[]> => {
|
|
386
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
387
|
+
'-s',
|
|
388
|
+
adbId,
|
|
389
|
+
'shell',
|
|
390
|
+
'dumpsys',
|
|
391
|
+
'package',
|
|
392
|
+
bundleId,
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
const requestedPermissions = new Set<string>();
|
|
396
|
+
const lines = stdout.split('\n');
|
|
397
|
+
let inRequestedPermissionsSection = false;
|
|
398
|
+
|
|
399
|
+
for (const line of lines) {
|
|
400
|
+
const trimmedLine = line.trim();
|
|
401
|
+
|
|
402
|
+
if (trimmedLine === 'requested permissions:') {
|
|
403
|
+
inRequestedPermissionsSection = true;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!inRequestedPermissionsSection) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (trimmedLine === '') {
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (trimmedLine.endsWith(':')) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (/^[a-zA-Z0-9_.]+$/.test(trimmedLine)) {
|
|
420
|
+
requestedPermissions.add(trimmedLine);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return [...requestedPermissions];
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const getDangerousPermissions = async (adbId: string): Promise<Set<string>> => {
|
|
431
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
432
|
+
'-s',
|
|
433
|
+
adbId,
|
|
434
|
+
'shell',
|
|
435
|
+
'pm',
|
|
436
|
+
'list',
|
|
437
|
+
'permissions',
|
|
438
|
+
'-g',
|
|
439
|
+
'-d',
|
|
440
|
+
]);
|
|
441
|
+
|
|
442
|
+
const dangerousPermissions = new Set<string>();
|
|
443
|
+
|
|
444
|
+
for (const match of stdout.matchAll(/permission:([a-zA-Z0-9_.]+)/g)) {
|
|
445
|
+
const permission = match[1]?.trim();
|
|
446
|
+
|
|
447
|
+
if (permission) {
|
|
448
|
+
dangerousPermissions.add(permission);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return dangerousPermissions;
|
|
453
|
+
};
|
|
454
|
+
|
|
315
455
|
export const isBootCompleted = async (adbId: string): Promise<boolean> => {
|
|
316
456
|
try {
|
|
317
457
|
const bootCompleted = await getShellProperty(adbId, 'sys.boot_completed');
|
|
@@ -336,6 +476,13 @@ export const installApp = async (
|
|
|
336
476
|
await spawn(getAdbBinaryPath(), ['-s', adbId, 'install', '-r', appPath]);
|
|
337
477
|
};
|
|
338
478
|
|
|
479
|
+
export const uninstallApp = async (
|
|
480
|
+
adbId: string,
|
|
481
|
+
bundleId: string,
|
|
482
|
+
): Promise<void> => {
|
|
483
|
+
await spawn(getAdbBinaryPath(), ['-s', adbId, 'uninstall', bundleId]);
|
|
484
|
+
};
|
|
485
|
+
|
|
339
486
|
export const hasAvd = async (name: string): Promise<boolean> => {
|
|
340
487
|
const avds = await getAvds();
|
|
341
488
|
return avds.includes(name);
|
|
@@ -354,15 +501,16 @@ export const createAvd = async ({
|
|
|
354
501
|
);
|
|
355
502
|
|
|
356
503
|
await verifyAndroidEmulatorSdk(apiLevel);
|
|
504
|
+
await ensureAvdProfileAvailable(profile);
|
|
357
505
|
await spawn('bash', [
|
|
358
506
|
'-lc',
|
|
359
|
-
`printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}"`,
|
|
507
|
+
`printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}" -p "${getAvdDirectory(name)}"`,
|
|
360
508
|
]);
|
|
509
|
+
await ensureAvdIniExists({ name, apiLevel });
|
|
510
|
+
const configPath = await ensureAvdConfigExists(name);
|
|
361
511
|
await spawn('bash', [
|
|
362
512
|
'-lc',
|
|
363
|
-
`printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${
|
|
364
|
-
name,
|
|
365
|
-
)}"`,
|
|
513
|
+
`printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${configPath}"`,
|
|
366
514
|
]);
|
|
367
515
|
};
|
|
368
516
|
|
|
@@ -667,3 +815,77 @@ export const getConnectedDevices = async (): Promise<AdbDevice[]> => {
|
|
|
667
815
|
|
|
668
816
|
return devices;
|
|
669
817
|
};
|
|
818
|
+
|
|
819
|
+
export const grantPermissions = async (
|
|
820
|
+
adbId: string,
|
|
821
|
+
bundleId: string,
|
|
822
|
+
): Promise<void> => {
|
|
823
|
+
androidAdbLogger.debug('grantPermissions:start %o', {
|
|
824
|
+
adbId,
|
|
825
|
+
bundleId,
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const isInstalled = await isAppInstalled(adbId, bundleId);
|
|
829
|
+
if (!isInstalled) {
|
|
830
|
+
throw new AdbAppNotInstalledError(bundleId, adbId);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const [requestedPermissions, dangerousPermissions] = await Promise.all([
|
|
834
|
+
getRequestedPermissions(adbId, bundleId),
|
|
835
|
+
getDangerousPermissions(adbId),
|
|
836
|
+
]);
|
|
837
|
+
const permissions = requestedPermissions.filter((permission) =>
|
|
838
|
+
dangerousPermissions.has(permission),
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
androidAdbLogger.debug('grantPermissions:resolved %o', {
|
|
842
|
+
adbId,
|
|
843
|
+
bundleId,
|
|
844
|
+
requestedPermissions,
|
|
845
|
+
permissions,
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
if (permissions.length === 0) {
|
|
849
|
+
androidAdbLogger.debug('grantPermissions:skip %o', {
|
|
850
|
+
adbId,
|
|
851
|
+
bundleId,
|
|
852
|
+
});
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const grantCommands = permissions.map((permission) => [
|
|
857
|
+
'-s',
|
|
858
|
+
adbId,
|
|
859
|
+
'shell',
|
|
860
|
+
'pm',
|
|
861
|
+
'grant',
|
|
862
|
+
bundleId,
|
|
863
|
+
permission,
|
|
864
|
+
]);
|
|
865
|
+
|
|
866
|
+
try {
|
|
867
|
+
androidAdbLogger.debug('grantPermissions:commands %o', {
|
|
868
|
+
adbId,
|
|
869
|
+
bundleId,
|
|
870
|
+
grantCommands,
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
await Promise.all(
|
|
874
|
+
grantCommands.map((args) => spawn(getAdbBinaryPath(), args as string[])),
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
androidAdbLogger.debug('grantPermissions:success %o', {
|
|
878
|
+
adbId,
|
|
879
|
+
bundleId,
|
|
880
|
+
permissions,
|
|
881
|
+
});
|
|
882
|
+
} catch (error) {
|
|
883
|
+
androidAdbLogger.debug('grantPermissions:error %o', {
|
|
884
|
+
adbId,
|
|
885
|
+
bundleId,
|
|
886
|
+
permissions,
|
|
887
|
+
error,
|
|
888
|
+
});
|
|
889
|
+
throw new AdbPermissionGrantError(bundleId, permissions, adbId);
|
|
890
|
+
}
|
|
891
|
+
};
|
package/src/emulator-startup.ts
CHANGED
package/src/instance.ts
CHANGED
|
@@ -59,6 +59,20 @@ const getHarnessAppPath = (): string => {
|
|
|
59
59
|
return appPath;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
const getOptionalHarnessAppPath = (): string | undefined => {
|
|
63
|
+
const appPath = process.env.HARNESS_APP_PATH;
|
|
64
|
+
|
|
65
|
+
if (!appPath) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(appPath)) {
|
|
70
|
+
throw new HarnessAppPathError('invalid', appPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return appPath;
|
|
74
|
+
};
|
|
75
|
+
|
|
62
76
|
const configureAndroidRuntime = async (
|
|
63
77
|
adbId: string,
|
|
64
78
|
config: AndroidPlatformConfig,
|
|
@@ -180,6 +194,7 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
180
194
|
): Promise<HarnessPlatformRunner> => {
|
|
181
195
|
assertAndroidDeviceEmulator(config.device);
|
|
182
196
|
const detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
|
|
197
|
+
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
183
198
|
const emulatorConfig = config.device;
|
|
184
199
|
const emulatorName = emulatorConfig.name;
|
|
185
200
|
const avdConfig = emulatorConfig.avd;
|
|
@@ -250,14 +265,22 @@ export const getAndroidEmulatorPlatformInstance = async (
|
|
|
250
265
|
);
|
|
251
266
|
|
|
252
267
|
const isInstalled = await adb.isAppInstalled(adbId, config.bundleId);
|
|
268
|
+
const appPath = getOptionalHarnessAppPath();
|
|
253
269
|
|
|
254
|
-
if (
|
|
255
|
-
|
|
270
|
+
if (isInstalled && appPath) {
|
|
271
|
+
await adb.uninstallApp(adbId, config.bundleId);
|
|
256
272
|
await adb.installApp(adbId, appPath);
|
|
273
|
+
} else if (!isInstalled) {
|
|
274
|
+
const installPath = appPath ?? getHarnessAppPath();
|
|
275
|
+
await adb.installApp(adbId, installPath);
|
|
257
276
|
}
|
|
258
277
|
|
|
259
278
|
const appUid = await configureAndroidRuntime(adbId, config, harnessConfig);
|
|
260
279
|
|
|
280
|
+
if (permissionsEnabled) {
|
|
281
|
+
await adb.grantPermissions(adbId, config.bundleId);
|
|
282
|
+
}
|
|
283
|
+
|
|
261
284
|
return {
|
|
262
285
|
startApp: async (options) => {
|
|
263
286
|
await adb.startApp(
|
|
@@ -315,6 +338,7 @@ export const getAndroidPhysicalDevicePlatformInstance = async (
|
|
|
315
338
|
): Promise<HarnessPlatformRunner> => {
|
|
316
339
|
assertAndroidDevicePhysical(config.device);
|
|
317
340
|
const detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
|
|
341
|
+
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
318
342
|
|
|
319
343
|
const adbId = await getAdbId(config.device);
|
|
320
344
|
|
|
@@ -333,6 +357,10 @@ export const getAndroidPhysicalDevicePlatformInstance = async (
|
|
|
333
357
|
|
|
334
358
|
const appUid = await configureAndroidRuntime(adbId, config, harnessConfig);
|
|
335
359
|
|
|
360
|
+
if (permissionsEnabled) {
|
|
361
|
+
await adb.grantPermissions(adbId, config.bundleId);
|
|
362
|
+
}
|
|
363
|
+
|
|
336
364
|
return {
|
|
337
365
|
startApp: async (options) => {
|
|
338
366
|
await adb.startApp(
|
package/dist/assertions.d.ts
DELETED
package/dist/assertions.d.ts.map
DELETED
|
@@ -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"}
|
package/dist/assertions.js
DELETED
package/dist/emulator.d.ts
DELETED
package/dist/emulator.d.ts.map
DELETED
|
@@ -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
package/dist/reader.d.ts.map
DELETED
|
@@ -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
|
-
};
|