@react-native-harness/platform-android 1.1.0 → 1.2.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.
- package/dist/__tests__/adb-id.test.d.ts +2 -0
- package/dist/__tests__/adb-id.test.d.ts.map +1 -0
- package/dist/__tests__/adb-id.test.js +41 -0
- package/dist/__tests__/adb.test.js +148 -13
- package/dist/__tests__/app-monitor.test.js +52 -75
- package/dist/__tests__/emulator-startup.test.js +1 -0
- package/dist/__tests__/instance.test.js +97 -36
- 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 +4 -0
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +124 -1
- package/dist/app-monitor.d.ts.map +1 -1
- package/dist/app-monitor.js +2 -5
- 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/eslint.config.mjs +1 -0
- package/package.json +5 -4
- package/src/__tests__/adb-id.test.ts +51 -0
- package/src/__tests__/emulator-startup.test.ts +3 -0
- package/src/__tests__/instance.test.ts +143 -51
- package/src/adb-errors.ts +47 -0
- package/src/adb-id.ts +4 -0
- package/src/adb.ts +174 -1
- package/src/app-monitor.ts +3 -8
- package/src/emulator-startup.ts +0 -2
- package/src/instance.ts +30 -2
- package/test/setup.ts +68 -0
- package/vite.config.ts +19 -0
- package/src/__tests__/adb.test.ts +0 -598
- package/src/__tests__/app-monitor.test.ts +0 -273
- package/src/__tests__/environment.test.ts +0 -212
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class AdbError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'AdbError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class AdbDeviceNotFoundError extends AdbError {
|
|
8
|
+
constructor(adbId) {
|
|
9
|
+
super(`Android device "${adbId}" not found or not connected. ` +
|
|
10
|
+
`Run "adb devices" to see available devices.`);
|
|
11
|
+
this.name = 'AdbDeviceNotFoundError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class AdbAppNotInstalledError extends AdbError {
|
|
15
|
+
constructor(bundleId, adbId) {
|
|
16
|
+
super(`App "${bundleId}" is not installed on device "${adbId}". ` +
|
|
17
|
+
`Install the app before running tests.`);
|
|
18
|
+
this.name = 'AdbAppNotInstalledError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class AdbPermissionGrantError extends AdbError {
|
|
22
|
+
constructor(bundleId, permissions, adbId) {
|
|
23
|
+
const permissionList = permissions.join(', ');
|
|
24
|
+
super(`Failed to grant permissions [${permissionList}] to "${bundleId}" on device "${adbId}". ` +
|
|
25
|
+
`Verify the app is installed and the device supports these permissions.`);
|
|
26
|
+
this.name = 'AdbPermissionGrantError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class AdbBinaryNotFoundError extends AdbError {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(`adb binary not found or not accessible. ` +
|
|
32
|
+
`Ensure Android SDK is properly installed and ANDROID_HOME is set.`);
|
|
33
|
+
this.name = 'AdbBinaryNotFoundError';
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/adb-id.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb-id.d.ts","sourceRoot":"","sources":["../src/adb-id.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,OAE/C,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,QAAQ,aAAa,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"adb-id.d.ts","sourceRoot":"","sources":["../src/adb-id.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,OAE/C,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,QAAQ,aAAa,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAmCvB,CAAC"}
|
package/dist/adb-id.js
CHANGED
|
@@ -7,6 +7,9 @@ export const getAdbId = async (device) => {
|
|
|
7
7
|
const adbIds = await adb.getDeviceIds();
|
|
8
8
|
for (const adbId of adbIds) {
|
|
9
9
|
if (isAndroidDeviceEmulator(device)) {
|
|
10
|
+
if (!isAdbIdEmulator(adbId)) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
10
13
|
const emulatorName = await adb.getEmulatorName(adbId);
|
|
11
14
|
if (emulatorName === device.name) {
|
|
12
15
|
return adbId;
|
package/dist/adb.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
|
|
2
|
+
import { type Subprocess } from '@react-native-harness/tools';
|
|
2
3
|
import type { ChildProcessByStdio } from 'node:child_process';
|
|
3
4
|
import type { Readable } from 'node:stream';
|
|
4
5
|
import { type EmulatorBootMode } from './emulator-startup.js';
|
|
@@ -30,6 +31,7 @@ export declare const getDeviceInfo: (adbId: string) => Promise<DeviceInfo | null
|
|
|
30
31
|
export declare const isBootCompleted: (adbId: string) => Promise<boolean>;
|
|
31
32
|
export declare const stopEmulator: (adbId: string) => Promise<void>;
|
|
32
33
|
export declare const installApp: (adbId: string, appPath: string) => Promise<void>;
|
|
34
|
+
export declare const uninstallApp: (adbId: string, bundleId: string) => Promise<void>;
|
|
33
35
|
export declare const hasAvd: (name: string) => Promise<boolean>;
|
|
34
36
|
export declare const createAvd: ({ name, apiLevel, profile, diskSize, heapSize, }: CreateAvdOptions) => Promise<void>;
|
|
35
37
|
export declare const deleteAvd: (name: string) => Promise<void>;
|
|
@@ -41,6 +43,7 @@ export declare const isAppRunning: (adbId: string, bundleId: string) => Promise<
|
|
|
41
43
|
export declare const getAppUid: (adbId: string, bundleId: string) => Promise<number>;
|
|
42
44
|
export declare const setHideErrorDialogs: (adbId: string, hide: boolean) => Promise<void>;
|
|
43
45
|
export declare const getLogcatTimestamp: (adbId: string) => Promise<string>;
|
|
46
|
+
export declare const startLogcat: (adbId: string, args: readonly string[]) => Subprocess;
|
|
44
47
|
export declare const getAvds: () => Promise<string[]>;
|
|
45
48
|
export type AdbDevice = {
|
|
46
49
|
id: string;
|
|
@@ -48,4 +51,5 @@ export type AdbDevice = {
|
|
|
48
51
|
manufacturer: string;
|
|
49
52
|
};
|
|
50
53
|
export declare const getConnectedDevices: () => Promise<AdbDevice[]>;
|
|
54
|
+
export declare const grantPermissions: (adbId: string, bundleId: string) => Promise<void>;
|
|
51
55
|
//# sourceMappingURL=adb.d.ts.map
|
package/dist/adb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
|
package/dist/adb.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { spawn, SubprocessError } from '@react-native-harness/tools';
|
|
1
|
+
import { logger, 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
4
|
import { getAvdConfigPath, getAvdDirectory, readAvdConfig, } from './avd-config.js';
|
|
5
5
|
import { ensureAndroidEmulatorAvailable, ensureAndroidSdkPackages, getAdbBinaryPath, getAndroidSystemImagePackage, getAvdManagerBinaryPath, getEmulatorBinaryPath, getHostAndroidSystemImageArch, getRequiredAndroidSdkPackages, } from './environment.js';
|
|
6
6
|
import { getEmulatorStartupArgs, } from './emulator-startup.js';
|
|
7
|
+
import { AdbAppNotInstalledError, AdbPermissionGrantError, } from './adb-errors.js';
|
|
7
8
|
const wait = async (ms) => {
|
|
8
9
|
await new Promise((resolve) => {
|
|
9
10
|
setTimeout(resolve, ms);
|
|
@@ -27,6 +28,7 @@ const waitWithSignal = async (ms, signal) => {
|
|
|
27
28
|
};
|
|
28
29
|
const EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS = 5000;
|
|
29
30
|
const EMULATOR_OUTPUT_BUFFER_LIMIT = 16 * 1024;
|
|
31
|
+
const androidAdbLogger = logger.child('android-adb');
|
|
30
32
|
export const emulatorProcess = {
|
|
31
33
|
startDetachedProcess: (file, args) => nodeSpawn(file, args, {
|
|
32
34
|
detached: true,
|
|
@@ -219,6 +221,61 @@ export const getDeviceInfo = async (adbId) => {
|
|
|
219
221
|
const model = await getShellProperty(adbId, 'ro.product.model');
|
|
220
222
|
return { manufacturer, model };
|
|
221
223
|
};
|
|
224
|
+
const getRequestedPermissions = async (adbId, bundleId) => {
|
|
225
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
226
|
+
'-s',
|
|
227
|
+
adbId,
|
|
228
|
+
'shell',
|
|
229
|
+
'dumpsys',
|
|
230
|
+
'package',
|
|
231
|
+
bundleId,
|
|
232
|
+
]);
|
|
233
|
+
const requestedPermissions = new Set();
|
|
234
|
+
const lines = stdout.split('\n');
|
|
235
|
+
let inRequestedPermissionsSection = false;
|
|
236
|
+
for (const line of lines) {
|
|
237
|
+
const trimmedLine = line.trim();
|
|
238
|
+
if (trimmedLine === 'requested permissions:') {
|
|
239
|
+
inRequestedPermissionsSection = true;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (!inRequestedPermissionsSection) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (trimmedLine === '') {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (trimmedLine.endsWith(':')) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
if (/^[a-zA-Z0-9_.]+$/.test(trimmedLine)) {
|
|
252
|
+
requestedPermissions.add(trimmedLine);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
return [...requestedPermissions];
|
|
258
|
+
};
|
|
259
|
+
const getDangerousPermissions = async (adbId) => {
|
|
260
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
261
|
+
'-s',
|
|
262
|
+
adbId,
|
|
263
|
+
'shell',
|
|
264
|
+
'pm',
|
|
265
|
+
'list',
|
|
266
|
+
'permissions',
|
|
267
|
+
'-g',
|
|
268
|
+
'-d',
|
|
269
|
+
]);
|
|
270
|
+
const dangerousPermissions = new Set();
|
|
271
|
+
for (const match of stdout.matchAll(/permission:([a-zA-Z0-9_.]+)/g)) {
|
|
272
|
+
const permission = match[1]?.trim();
|
|
273
|
+
if (permission) {
|
|
274
|
+
dangerousPermissions.add(permission);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return dangerousPermissions;
|
|
278
|
+
};
|
|
222
279
|
export const isBootCompleted = async (adbId) => {
|
|
223
280
|
try {
|
|
224
281
|
const bootCompleted = await getShellProperty(adbId, 'sys.boot_completed');
|
|
@@ -237,6 +294,9 @@ export const stopEmulator = async (adbId) => {
|
|
|
237
294
|
export const installApp = async (adbId, appPath) => {
|
|
238
295
|
await spawn(getAdbBinaryPath(), ['-s', adbId, 'install', '-r', appPath]);
|
|
239
296
|
};
|
|
297
|
+
export const uninstallApp = async (adbId, bundleId) => {
|
|
298
|
+
await spawn(getAdbBinaryPath(), ['-s', adbId, 'uninstall', bundleId]);
|
|
299
|
+
};
|
|
240
300
|
export const hasAvd = async (name) => {
|
|
241
301
|
const avds = await getAvds();
|
|
242
302
|
return avds.includes(name);
|
|
@@ -430,6 +490,10 @@ export const getLogcatTimestamp = async (adbId) => {
|
|
|
430
490
|
]);
|
|
431
491
|
return stdout.trim().replace(/^'+|'+$/g, '');
|
|
432
492
|
};
|
|
493
|
+
export const startLogcat = (adbId, args) => spawn(getAdbBinaryPath(), ['-s', adbId, ...args], {
|
|
494
|
+
stdout: 'pipe',
|
|
495
|
+
stderr: 'pipe',
|
|
496
|
+
});
|
|
433
497
|
export const getAvds = async () => {
|
|
434
498
|
try {
|
|
435
499
|
const emulatorBinaryPath = await ensureEmulatorInstalled();
|
|
@@ -468,3 +532,62 @@ export const getConnectedDevices = async () => {
|
|
|
468
532
|
}
|
|
469
533
|
return devices;
|
|
470
534
|
};
|
|
535
|
+
export const grantPermissions = async (adbId, bundleId) => {
|
|
536
|
+
androidAdbLogger.debug('grantPermissions:start %o', {
|
|
537
|
+
adbId,
|
|
538
|
+
bundleId,
|
|
539
|
+
});
|
|
540
|
+
const isInstalled = await isAppInstalled(adbId, bundleId);
|
|
541
|
+
if (!isInstalled) {
|
|
542
|
+
throw new AdbAppNotInstalledError(bundleId, adbId);
|
|
543
|
+
}
|
|
544
|
+
const [requestedPermissions, dangerousPermissions] = await Promise.all([
|
|
545
|
+
getRequestedPermissions(adbId, bundleId),
|
|
546
|
+
getDangerousPermissions(adbId),
|
|
547
|
+
]);
|
|
548
|
+
const permissions = requestedPermissions.filter((permission) => dangerousPermissions.has(permission));
|
|
549
|
+
androidAdbLogger.debug('grantPermissions:resolved %o', {
|
|
550
|
+
adbId,
|
|
551
|
+
bundleId,
|
|
552
|
+
requestedPermissions,
|
|
553
|
+
permissions,
|
|
554
|
+
});
|
|
555
|
+
if (permissions.length === 0) {
|
|
556
|
+
androidAdbLogger.debug('grantPermissions:skip %o', {
|
|
557
|
+
adbId,
|
|
558
|
+
bundleId,
|
|
559
|
+
});
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const grantCommands = permissions.map((permission) => [
|
|
563
|
+
'-s',
|
|
564
|
+
adbId,
|
|
565
|
+
'shell',
|
|
566
|
+
'pm',
|
|
567
|
+
'grant',
|
|
568
|
+
bundleId,
|
|
569
|
+
permission,
|
|
570
|
+
]);
|
|
571
|
+
try {
|
|
572
|
+
androidAdbLogger.debug('grantPermissions:commands %o', {
|
|
573
|
+
adbId,
|
|
574
|
+
bundleId,
|
|
575
|
+
grantCommands,
|
|
576
|
+
});
|
|
577
|
+
await Promise.all(grantCommands.map((args) => spawn(getAdbBinaryPath(), args)));
|
|
578
|
+
androidAdbLogger.debug('grantPermissions:success %o', {
|
|
579
|
+
adbId,
|
|
580
|
+
bundleId,
|
|
581
|
+
permissions,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
androidAdbLogger.debug('grantPermissions:error %o', {
|
|
586
|
+
adbId,
|
|
587
|
+
bundleId,
|
|
588
|
+
permissions,
|
|
589
|
+
error,
|
|
590
|
+
});
|
|
591
|
+
throw new AdbPermissionGrantError(bundleId, permissions, adbId);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-monitor.d.ts","sourceRoot":"","sources":["../src/app-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EAErB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"app-monitor.d.ts","sourceRoot":"","sources":["../src/app-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EAErB,MAAM,iCAAiC,CAAC;AA+SzC,QAAA,MAAM,qBAAqB,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,eAAe,GAAG,IAwEpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,mDAKrC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C,KAAG,iBA4KH,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG;IAC3C,eAAe,EAAE,CACf,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACtC,CAAC"}
|
package/dist/app-monitor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeRegExp, getEmitter, logger,
|
|
1
|
+
import { escapeRegExp, getEmitter, logger, SubprocessError, } from '@react-native-harness/tools';
|
|
2
2
|
import * as adb from './adb.js';
|
|
3
3
|
import { androidCrashParser } from './crash-parser.js';
|
|
4
4
|
const androidAppMonitorLogger = logger.child('android-app-monitor');
|
|
@@ -281,10 +281,7 @@ export const createAndroidAppMonitor = ({ adbId, bundleId, appUid, crashArtifact
|
|
|
281
281
|
};
|
|
282
282
|
const startLogcat = async () => {
|
|
283
283
|
const logcatTimestamp = await adb.getLogcatTimestamp(adbId);
|
|
284
|
-
logcatProcess =
|
|
285
|
-
stdout: 'pipe',
|
|
286
|
-
stderr: 'pipe',
|
|
287
|
-
});
|
|
284
|
+
logcatProcess = adb.startLogcat(adbId, getLogcatArgs(appUid, logcatTimestamp));
|
|
288
285
|
const currentProcess = logcatProcess;
|
|
289
286
|
if (!currentProcess) {
|
|
290
287
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emulator-startup.d.ts","sourceRoot":"","sources":["../src/emulator-startup.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,2BAA2B,GAC3B,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"emulator-startup.d.ts","sourceRoot":"","sources":["../src/emulator-startup.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,2BAA2B,GAC3B,gBAAgB,CAAC;AAUrB,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,MAAM,gBAAgB,KACrB,MAAM,EASR,CAAC"}
|
package/dist/emulator-startup.js
CHANGED
package/dist/instance.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAgLrB,eAAO,MAAM,kCAAkC,GAC7C,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,EAC5B,MAAM,0BAA0B,KAC/B,OAAO,CAAC,qBAAqB,CA2I/B,CAAC;AAEF,eAAO,MAAM,wCAAwC,GACnD,QAAQ,qBAAqB,EAC7B,eAAe,aAAa,KAC3B,OAAO,CAAC,qBAAqB,CAsE/B,CAAC"}
|
package/dist/instance.js
CHANGED
|
@@ -29,6 +29,16 @@ const getHarnessAppPath = () => {
|
|
|
29
29
|
}
|
|
30
30
|
return appPath;
|
|
31
31
|
};
|
|
32
|
+
const getOptionalHarnessAppPath = () => {
|
|
33
|
+
const appPath = process.env.HARNESS_APP_PATH;
|
|
34
|
+
if (!appPath) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
if (!fs.existsSync(appPath)) {
|
|
38
|
+
throw new HarnessAppPathError('invalid', appPath);
|
|
39
|
+
}
|
|
40
|
+
return appPath;
|
|
41
|
+
};
|
|
32
42
|
const configureAndroidRuntime = async (adbId, config, harnessConfig) => {
|
|
33
43
|
const metroPort = harnessConfig.metroPort;
|
|
34
44
|
await Promise.all([
|
|
@@ -98,6 +108,7 @@ const prepareCachedAvd = async ({ emulatorConfig, signal, }) => {
|
|
|
98
108
|
export const getAndroidEmulatorPlatformInstance = async (config, harnessConfig, init) => {
|
|
99
109
|
assertAndroidDeviceEmulator(config.device);
|
|
100
110
|
const detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
|
|
111
|
+
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
101
112
|
const emulatorConfig = config.device;
|
|
102
113
|
const emulatorName = emulatorConfig.name;
|
|
103
114
|
const avdConfig = emulatorConfig.avd;
|
|
@@ -141,11 +152,19 @@ export const getAndroidEmulatorPlatformInstance = async (config, harnessConfig,
|
|
|
141
152
|
}
|
|
142
153
|
androidInstanceLogger.debug('waiting for Android emulator %s to finish booting', adbId);
|
|
143
154
|
const isInstalled = await adb.isAppInstalled(adbId, config.bundleId);
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
const appPath = getOptionalHarnessAppPath();
|
|
156
|
+
if (isInstalled && appPath) {
|
|
157
|
+
await adb.uninstallApp(adbId, config.bundleId);
|
|
146
158
|
await adb.installApp(adbId, appPath);
|
|
147
159
|
}
|
|
160
|
+
else if (!isInstalled) {
|
|
161
|
+
const installPath = appPath ?? getHarnessAppPath();
|
|
162
|
+
await adb.installApp(adbId, installPath);
|
|
163
|
+
}
|
|
148
164
|
const appUid = await configureAndroidRuntime(adbId, config, harnessConfig);
|
|
165
|
+
if (permissionsEnabled) {
|
|
166
|
+
await adb.grantPermissions(adbId, config.bundleId);
|
|
167
|
+
}
|
|
149
168
|
return {
|
|
150
169
|
startApp: async (options) => {
|
|
151
170
|
await adb.startApp(adbId, config.bundleId, config.activityName, options ??
|
|
@@ -187,6 +206,7 @@ export const getAndroidEmulatorPlatformInstance = async (config, harnessConfig,
|
|
|
187
206
|
export const getAndroidPhysicalDevicePlatformInstance = async (config, harnessConfig) => {
|
|
188
207
|
assertAndroidDevicePhysical(config.device);
|
|
189
208
|
const detectNativeCrashes = harnessConfig.detectNativeCrashes ?? true;
|
|
209
|
+
const permissionsEnabled = harnessConfig.permissions ?? false;
|
|
190
210
|
const adbId = await getAdbId(config.device);
|
|
191
211
|
if (!adbId) {
|
|
192
212
|
throw new DeviceNotFoundError(getDeviceName(config.device));
|
|
@@ -196,6 +216,9 @@ export const getAndroidPhysicalDevicePlatformInstance = async (config, harnessCo
|
|
|
196
216
|
throw new AppNotInstalledError(config.bundleId, getDeviceName(config.device));
|
|
197
217
|
}
|
|
198
218
|
const appUid = await configureAndroidRuntime(adbId, config, harnessConfig);
|
|
219
|
+
if (permissionsEnabled) {
|
|
220
|
+
await adb.grantPermissions(adbId, config.bundleId);
|
|
221
|
+
}
|
|
199
222
|
return {
|
|
200
223
|
startApp: async (options) => {
|
|
201
224
|
await adb.startApp(adbId, config.bundleId, config.activityName, options ??
|