@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
|
@@ -2,9 +2,10 @@ import { EventEmitter } from 'node:events';
|
|
|
2
2
|
import { PassThrough } from 'node:stream';
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import { SubprocessError } from '@react-native-harness/tools';
|
|
5
|
-
import { createAvd, deleteAvd, emulatorProcess, getAppUid, getLogcatTimestamp, getStartAppArgs, hasAvd, installApp, startEmulator, waitForBoot, waitForEmulatorDisconnect, } from '../adb.js';
|
|
5
|
+
import { createAvd, deleteAvd, emulatorProcess, getAppUid, getLogcatTimestamp, getStartAppArgs, hasAvd, installApp, startEmulator, uninstallApp, 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();
|
|
@@ -100,10 +101,26 @@ describe('getStartAppArgs', () => {
|
|
|
100
101
|
'/tmp/app.apk',
|
|
101
102
|
]);
|
|
102
103
|
});
|
|
104
|
+
it('uninstalls the app via adb', async () => {
|
|
105
|
+
const spawnSpy = vi
|
|
106
|
+
.spyOn(tools, 'spawn')
|
|
107
|
+
.mockResolvedValueOnce({});
|
|
108
|
+
await uninstallApp('emulator-5554', 'com.example.app');
|
|
109
|
+
expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
|
|
110
|
+
'-s',
|
|
111
|
+
'emulator-5554',
|
|
112
|
+
'uninstall',
|
|
113
|
+
'com.example.app',
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
103
116
|
it('creates an AVD and appends config overrides', async () => {
|
|
117
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
104
118
|
const spawnSpy = vi
|
|
105
119
|
.spyOn(tools, 'spawn')
|
|
106
|
-
.mockResolvedValue({})
|
|
120
|
+
.mockResolvedValue({})
|
|
121
|
+
.mockResolvedValueOnce({
|
|
122
|
+
stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
|
|
123
|
+
});
|
|
107
124
|
const verifyAndroidEmulatorSdk = vi
|
|
108
125
|
.spyOn(environment, 'ensureAndroidSdkPackages')
|
|
109
126
|
.mockResolvedValue('/tmp/android-sdk');
|
|
@@ -121,17 +138,84 @@ describe('getStartAppArgs', () => {
|
|
|
121
138
|
'platforms;android-35',
|
|
122
139
|
'system-images;android-35;default;x86_64',
|
|
123
140
|
]);
|
|
124
|
-
expect(spawnSpy).toHaveBeenNthCalledWith(1, '
|
|
141
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(1, expect.stringMatching(/avdmanager$/), ['list', 'device']);
|
|
142
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
|
|
125
143
|
'-lc',
|
|
126
|
-
expect.stringContaining('create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8"'),
|
|
144
|
+
expect.stringContaining('create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8" -p '),
|
|
127
145
|
]);
|
|
128
|
-
expect(spawnSpy).toHaveBeenNthCalledWith(
|
|
146
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
|
|
147
|
+
'-lc',
|
|
148
|
+
expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
|
|
149
|
+
]);
|
|
150
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
|
|
129
151
|
'-lc',
|
|
130
152
|
expect.stringContaining(`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `),
|
|
131
153
|
]);
|
|
132
154
|
});
|
|
155
|
+
it('recreates the companion ini file when avdmanager leaves it missing', async () => {
|
|
156
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
157
|
+
const spawnSpy = vi
|
|
158
|
+
.spyOn(tools, 'spawn')
|
|
159
|
+
.mockResolvedValue({})
|
|
160
|
+
.mockResolvedValueOnce({
|
|
161
|
+
stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
|
|
162
|
+
});
|
|
163
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
|
|
164
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
|
|
165
|
+
await createAvd({
|
|
166
|
+
name: 'Pixel_6_API_35',
|
|
167
|
+
apiLevel: 35,
|
|
168
|
+
profile: 'pixel_6',
|
|
169
|
+
diskSize: '1G',
|
|
170
|
+
heapSize: '1G',
|
|
171
|
+
});
|
|
172
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
|
|
173
|
+
'-lc',
|
|
174
|
+
expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
|
|
175
|
+
]);
|
|
176
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
|
|
177
|
+
'-lc',
|
|
178
|
+
expect.stringContaining(`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `),
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
it('throws an actionable error when the requested AVD profile is unavailable', async () => {
|
|
182
|
+
vi.spyOn(tools, 'spawn').mockResolvedValueOnce({
|
|
183
|
+
stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
|
|
184
|
+
});
|
|
185
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
|
|
186
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
|
|
187
|
+
await expect(createAvd({
|
|
188
|
+
name: 'Pixel_8_Pro_API_35',
|
|
189
|
+
apiLevel: 35,
|
|
190
|
+
profile: 'pixel_8_pro',
|
|
191
|
+
diskSize: '1G',
|
|
192
|
+
heapSize: '1G',
|
|
193
|
+
})).rejects.toThrow('Android AVD profile "pixel_8_pro" is not available on this machine. Available profiles: pixel_8, pixel_6');
|
|
194
|
+
});
|
|
195
|
+
it('throws a clear error when avdmanager does not create config.ini', async () => {
|
|
196
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue(null);
|
|
197
|
+
vi.spyOn(tools, 'spawn')
|
|
198
|
+
.mockResolvedValue({})
|
|
199
|
+
.mockResolvedValueOnce({
|
|
200
|
+
stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
|
|
201
|
+
});
|
|
202
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue('/tmp/android-sdk');
|
|
203
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue('x86_64');
|
|
204
|
+
await expect(createAvd({
|
|
205
|
+
name: 'Pixel_6_API_35',
|
|
206
|
+
apiLevel: 35,
|
|
207
|
+
profile: 'pixel_6',
|
|
208
|
+
diskSize: '1G',
|
|
209
|
+
heapSize: '1G',
|
|
210
|
+
})).rejects.toThrow('Android AVD "Pixel_6_API_35" was created, but config.ini was not found at ');
|
|
211
|
+
});
|
|
133
212
|
it('creates an AVD with arm64 system image packages on arm64 hosts', async () => {
|
|
134
|
-
vi.spyOn(
|
|
213
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
214
|
+
vi.spyOn(tools, 'spawn')
|
|
215
|
+
.mockResolvedValue({})
|
|
216
|
+
.mockResolvedValueOnce({
|
|
217
|
+
stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\n',
|
|
218
|
+
});
|
|
135
219
|
const ensureAndroidSdkPackages = vi
|
|
136
220
|
.spyOn(environment, 'ensureAndroidSdkPackages')
|
|
137
221
|
.mockResolvedValue('/tmp/android-sdk');
|
|
@@ -342,4 +426,84 @@ describe('getStartAppArgs', () => {
|
|
|
342
426
|
await expect(waitPromise).resolves.toBeUndefined();
|
|
343
427
|
expect(spawnSpy).toHaveBeenCalledTimes(2);
|
|
344
428
|
});
|
|
429
|
+
it('grants requested dangerous permissions to an app', async () => {
|
|
430
|
+
const { grantPermissions } = await import('../adb.js');
|
|
431
|
+
const spawnSpy = vi
|
|
432
|
+
.spyOn(tools, 'spawn')
|
|
433
|
+
.mockResolvedValueOnce({
|
|
434
|
+
stdout: 'package:com.example.app\n',
|
|
435
|
+
})
|
|
436
|
+
.mockResolvedValueOnce({
|
|
437
|
+
stdout: `requested permissions:\n android.permission.CAMERA\n android.permission.INTERNET\n android.permission.ACCESS_FINE_LOCATION\ninstall permissions:\n`,
|
|
438
|
+
})
|
|
439
|
+
.mockResolvedValueOnce({
|
|
440
|
+
stdout: `Dangerous Permissions:\n permission:android.permission.CAMERA\n permission:android.permission.ACCESS_FINE_LOCATION\n`,
|
|
441
|
+
})
|
|
442
|
+
.mockResolvedValue({
|
|
443
|
+
stdout: '',
|
|
444
|
+
});
|
|
445
|
+
await grantPermissions('emulator-5554', 'com.example.app');
|
|
446
|
+
expect(spawnSpy).toHaveBeenCalledTimes(5);
|
|
447
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(1, expect.any(String), [
|
|
448
|
+
'-s',
|
|
449
|
+
'emulator-5554',
|
|
450
|
+
'shell',
|
|
451
|
+
'pm',
|
|
452
|
+
'list',
|
|
453
|
+
'packages',
|
|
454
|
+
'com.example.app',
|
|
455
|
+
]);
|
|
456
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(2, expect.any(String), [
|
|
457
|
+
'-s',
|
|
458
|
+
'emulator-5554',
|
|
459
|
+
'shell',
|
|
460
|
+
'dumpsys',
|
|
461
|
+
'package',
|
|
462
|
+
'com.example.app',
|
|
463
|
+
]);
|
|
464
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(3, expect.any(String), [
|
|
465
|
+
'-s',
|
|
466
|
+
'emulator-5554',
|
|
467
|
+
'shell',
|
|
468
|
+
'pm',
|
|
469
|
+
'list',
|
|
470
|
+
'permissions',
|
|
471
|
+
'-g',
|
|
472
|
+
'-d',
|
|
473
|
+
]);
|
|
474
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(4, expect.any(String), [
|
|
475
|
+
'-s',
|
|
476
|
+
'emulator-5554',
|
|
477
|
+
'shell',
|
|
478
|
+
'pm',
|
|
479
|
+
'grant',
|
|
480
|
+
'com.example.app',
|
|
481
|
+
'android.permission.CAMERA',
|
|
482
|
+
]);
|
|
483
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(5, expect.any(String), [
|
|
484
|
+
'-s',
|
|
485
|
+
'emulator-5554',
|
|
486
|
+
'shell',
|
|
487
|
+
'pm',
|
|
488
|
+
'grant',
|
|
489
|
+
'com.example.app',
|
|
490
|
+
'android.permission.ACCESS_FINE_LOCATION',
|
|
491
|
+
]);
|
|
492
|
+
});
|
|
493
|
+
it('does nothing when the app has no grantable dangerous permissions', async () => {
|
|
494
|
+
const { grantPermissions } = await import('../adb.js');
|
|
495
|
+
const spawnSpy = vi
|
|
496
|
+
.spyOn(tools, 'spawn')
|
|
497
|
+
.mockResolvedValueOnce({
|
|
498
|
+
stdout: 'package:com.example.app\n',
|
|
499
|
+
})
|
|
500
|
+
.mockResolvedValueOnce({
|
|
501
|
+
stdout: `requested permissions:\n android.permission.INTERNET\ninstall permissions:\n`,
|
|
502
|
+
})
|
|
503
|
+
.mockResolvedValueOnce({
|
|
504
|
+
stdout: `Dangerous Permissions:\n permission:android.permission.CAMERA\n`,
|
|
505
|
+
});
|
|
506
|
+
await grantPermissions('emulator-5554', 'com.example.app');
|
|
507
|
+
expect(spawnSpy).toHaveBeenCalledTimes(3);
|
|
508
|
+
});
|
|
345
509
|
});
|
|
@@ -7,6 +7,7 @@ describe('emulator startup modes', () => {
|
|
|
7
7
|
'-no-snapshot-load',
|
|
8
8
|
'-no-snapshot-save',
|
|
9
9
|
]));
|
|
10
|
+
expect(getEmulatorStartupArgs('Pixel_8_API_35', 'default-boot')).not.toEqual(expect.arrayContaining(['-camera-back', 'none']));
|
|
10
11
|
});
|
|
11
12
|
it('builds clean snapshot generation args', () => {
|
|
12
13
|
expect(getEmulatorStartupArgs('Pixel_8_API_35', 'clean-snapshot-generation')).toEqual(expect.arrayContaining(['@Pixel_8_API_35', '-no-snapshot-load']));
|
|
@@ -293,6 +293,42 @@ describe('Android platform instance', () => {
|
|
|
293
293
|
expect(installApp).toHaveBeenCalledWith('emulator-5554', appPath);
|
|
294
294
|
fs.rmSync(appPath, { force: true });
|
|
295
295
|
});
|
|
296
|
+
it('reinstalls the app from HARNESS_APP_PATH when already installed', async () => {
|
|
297
|
+
const appPath = path.join(os.tmpdir(), 'HarnessPlayground-installed.apk');
|
|
298
|
+
fs.writeFileSync(appPath, 'apk');
|
|
299
|
+
vi.stubEnv('HARNESS_APP_PATH', appPath);
|
|
300
|
+
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
301
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
302
|
+
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
303
|
+
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
304
|
+
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
305
|
+
const uninstallApp = vi
|
|
306
|
+
.spyOn(adb, 'uninstallApp')
|
|
307
|
+
.mockResolvedValue(undefined);
|
|
308
|
+
const installApp = vi.spyOn(adb, 'installApp').mockResolvedValue(undefined);
|
|
309
|
+
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
310
|
+
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
311
|
+
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
312
|
+
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
313
|
+
await expect(getAndroidEmulatorPlatformInstance({
|
|
314
|
+
name: 'android',
|
|
315
|
+
device: {
|
|
316
|
+
type: 'emulator',
|
|
317
|
+
name: 'Pixel_8_API_35',
|
|
318
|
+
avd: {
|
|
319
|
+
apiLevel: 35,
|
|
320
|
+
profile: 'pixel_8',
|
|
321
|
+
diskSize: '1G',
|
|
322
|
+
heapSize: '1G',
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
bundleId: 'com.harnessplayground',
|
|
326
|
+
activityName: '.MainActivity',
|
|
327
|
+
}, harnessConfig, init)).resolves.toBeDefined();
|
|
328
|
+
expect(uninstallApp).toHaveBeenCalledWith('emulator-5554', 'com.harnessplayground');
|
|
329
|
+
expect(installApp).toHaveBeenCalledWith('emulator-5554', appPath);
|
|
330
|
+
fs.rmSync(appPath, { force: true });
|
|
331
|
+
});
|
|
296
332
|
it('throws a HarnessAppPathError when HARNESS_APP_PATH is missing', async () => {
|
|
297
333
|
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
298
334
|
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
@@ -420,4 +456,98 @@ describe('Android platform instance', () => {
|
|
|
420
456
|
expect(appMonitor.addListener(listener)).toBeUndefined();
|
|
421
457
|
expect(appMonitor.removeListener(listener)).toBeUndefined();
|
|
422
458
|
});
|
|
459
|
+
it('grants permissions when permissions are enabled for emulator', async () => {
|
|
460
|
+
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
461
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
462
|
+
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
463
|
+
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
464
|
+
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
465
|
+
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
466
|
+
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
467
|
+
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
468
|
+
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
469
|
+
const grantPermissions = vi
|
|
470
|
+
.spyOn(adb, 'grantPermissions')
|
|
471
|
+
.mockResolvedValue(undefined);
|
|
472
|
+
const harnessConfigWithPermissions = {
|
|
473
|
+
...harnessConfig,
|
|
474
|
+
permissions: true,
|
|
475
|
+
};
|
|
476
|
+
await getAndroidEmulatorPlatformInstance({
|
|
477
|
+
name: 'android',
|
|
478
|
+
device: {
|
|
479
|
+
type: 'emulator',
|
|
480
|
+
name: 'Pixel_8_API_35',
|
|
481
|
+
avd: {
|
|
482
|
+
apiLevel: 35,
|
|
483
|
+
profile: 'pixel_8',
|
|
484
|
+
diskSize: '1G',
|
|
485
|
+
heapSize: '1G',
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
bundleId: 'com.harnessplayground',
|
|
489
|
+
activityName: '.MainActivity',
|
|
490
|
+
}, harnessConfigWithPermissions, init);
|
|
491
|
+
expect(grantPermissions).toHaveBeenCalledWith('emulator-5554', 'com.harnessplayground');
|
|
492
|
+
});
|
|
493
|
+
it('does not grant permissions when permissions are disabled for emulator', async () => {
|
|
494
|
+
vi.spyOn(await import('../environment.js'), 'ensureAndroidEmulatorEnvironment').mockResolvedValue('/tmp/android-sdk');
|
|
495
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['emulator-5554']);
|
|
496
|
+
vi.spyOn(adb, 'getEmulatorName').mockResolvedValue('Pixel_8_API_35');
|
|
497
|
+
vi.spyOn(adb, 'waitForBoot').mockResolvedValue('emulator-5554');
|
|
498
|
+
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
499
|
+
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
500
|
+
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
501
|
+
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
502
|
+
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
503
|
+
const grantPermissions = vi
|
|
504
|
+
.spyOn(adb, 'grantPermissions')
|
|
505
|
+
.mockResolvedValue(undefined);
|
|
506
|
+
await getAndroidEmulatorPlatformInstance({
|
|
507
|
+
name: 'android',
|
|
508
|
+
device: {
|
|
509
|
+
type: 'emulator',
|
|
510
|
+
name: 'Pixel_8_API_35',
|
|
511
|
+
avd: {
|
|
512
|
+
apiLevel: 35,
|
|
513
|
+
profile: 'pixel_8',
|
|
514
|
+
diskSize: '1G',
|
|
515
|
+
heapSize: '1G',
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
bundleId: 'com.harnessplayground',
|
|
519
|
+
activityName: '.MainActivity',
|
|
520
|
+
}, harnessConfig, init);
|
|
521
|
+
expect(grantPermissions).not.toHaveBeenCalled();
|
|
522
|
+
});
|
|
523
|
+
it('grants permissions when permissions are enabled for physical device', async () => {
|
|
524
|
+
vi.spyOn(adb, 'getDeviceIds').mockResolvedValue(['012345']);
|
|
525
|
+
vi.spyOn(adb, 'getDeviceInfo').mockResolvedValue({
|
|
526
|
+
manufacturer: 'motorola',
|
|
527
|
+
model: 'moto g72',
|
|
528
|
+
});
|
|
529
|
+
vi.spyOn(adb, 'isAppInstalled').mockResolvedValue(true);
|
|
530
|
+
vi.spyOn(adb, 'reversePort').mockResolvedValue(undefined);
|
|
531
|
+
vi.spyOn(adb, 'setHideErrorDialogs').mockResolvedValue(undefined);
|
|
532
|
+
vi.spyOn(adb, 'getAppUid').mockResolvedValue(10234);
|
|
533
|
+
vi.spyOn(sharedPrefs, 'applyHarnessDebugHttpHost').mockResolvedValue(undefined);
|
|
534
|
+
const grantPermissions = vi
|
|
535
|
+
.spyOn(adb, 'grantPermissions')
|
|
536
|
+
.mockResolvedValue(undefined);
|
|
537
|
+
const harnessConfigWithPermissions = {
|
|
538
|
+
...harnessConfig,
|
|
539
|
+
permissions: true,
|
|
540
|
+
};
|
|
541
|
+
await getAndroidPhysicalDevicePlatformInstance({
|
|
542
|
+
name: 'android-device',
|
|
543
|
+
device: {
|
|
544
|
+
type: 'physical',
|
|
545
|
+
manufacturer: 'motorola',
|
|
546
|
+
model: 'moto g72',
|
|
547
|
+
},
|
|
548
|
+
bundleId: 'com.harnessplayground',
|
|
549
|
+
activityName: '.MainActivity',
|
|
550
|
+
}, harnessConfigWithPermissions);
|
|
551
|
+
expect(grantPermissions).toHaveBeenCalledWith('012345', 'com.harnessplayground');
|
|
552
|
+
});
|
|
423
553
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class AdbError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class AdbDeviceNotFoundError extends AdbError {
|
|
5
|
+
constructor(adbId: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class AdbAppNotInstalledError extends AdbError {
|
|
8
|
+
constructor(bundleId: string, adbId: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class AdbPermissionGrantError extends AdbError {
|
|
11
|
+
constructor(bundleId: string, permissions: string[], adbId: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class AdbBinaryNotFoundError extends AdbError {
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=adb-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb-errors.d.ts","sourceRoot":"","sources":["../src/adb-errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,sBAAuB,SAAQ,QAAQ;gBACtC,KAAK,EAAE,MAAM;CAO1B;AAED,qBAAa,uBAAwB,SAAQ,QAAQ;gBACvC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAO5C;AAED,qBAAa,uBAAwB,SAAQ,QAAQ;gBACvC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM;CAQnE;AAED,qBAAa,sBAAuB,SAAQ,QAAQ;;CAQnD"}
|
|
@@ -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
|
@@ -30,6 +30,7 @@ export declare const getDeviceInfo: (adbId: string) => Promise<DeviceInfo | null
|
|
|
30
30
|
export declare const isBootCompleted: (adbId: string) => Promise<boolean>;
|
|
31
31
|
export declare const stopEmulator: (adbId: string) => Promise<void>;
|
|
32
32
|
export declare const installApp: (adbId: string, appPath: string) => Promise<void>;
|
|
33
|
+
export declare const uninstallApp: (adbId: string, bundleId: string) => Promise<void>;
|
|
33
34
|
export declare const hasAvd: (name: string) => Promise<boolean>;
|
|
34
35
|
export declare const createAvd: ({ name, apiLevel, profile, diskSize, heapSize, }: CreateAvdOptions) => Promise<void>;
|
|
35
36
|
export declare const deleteAvd: (name: string) => Promise<void>;
|
|
@@ -48,4 +49,5 @@ export type AdbDevice = {
|
|
|
48
49
|
manufacturer: string;
|
|
49
50
|
};
|
|
50
51
|
export declare const getConnectedDevices: () => Promise<AdbDevice[]>;
|
|
52
|
+
export declare const grantPermissions: (adbId: string, bundleId: string) => Promise<void>;
|
|
51
53
|
//# 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;AAG/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
|