appium-adb 14.1.2 → 14.1.3
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/CHANGELOG.md +6 -0
- package/build/lib/tools/aab-utils.d.ts +5 -4
- package/build/lib/tools/aab-utils.d.ts.map +1 -1
- package/build/lib/tools/aab-utils.js +13 -11
- package/build/lib/tools/aab-utils.js.map +1 -1
- package/build/lib/tools/android-manifest.d.ts +26 -30
- package/build/lib/tools/android-manifest.d.ts.map +1 -1
- package/build/lib/tools/android-manifest.js +33 -47
- package/build/lib/tools/android-manifest.js.map +1 -1
- package/build/lib/tools/apks-utils.d.ts +25 -29
- package/build/lib/tools/apks-utils.d.ts.map +1 -1
- package/build/lib/tools/apks-utils.js +41 -52
- package/build/lib/tools/apks-utils.js.map +1 -1
- package/build/lib/tools/fs-commands.d.ts +25 -30
- package/build/lib/tools/fs-commands.d.ts.map +1 -1
- package/build/lib/tools/fs-commands.js +19 -26
- package/build/lib/tools/fs-commands.js.map +1 -1
- package/build/lib/tools/general-commands.d.ts +36 -58
- package/build/lib/tools/general-commands.d.ts.map +1 -1
- package/build/lib/tools/general-commands.js +41 -47
- package/build/lib/tools/general-commands.js.map +1 -1
- package/build/lib/tools/keyboard-commands.d.ts +27 -36
- package/build/lib/tools/keyboard-commands.d.ts.map +1 -1
- package/build/lib/tools/keyboard-commands.js +24 -34
- package/build/lib/tools/keyboard-commands.js.map +1 -1
- package/build/lib/tools/lockmgmt.d.ts +25 -36
- package/build/lib/tools/lockmgmt.d.ts.map +1 -1
- package/build/lib/tools/lockmgmt.js +34 -38
- package/build/lib/tools/lockmgmt.js.map +1 -1
- package/build/lib/tools/logcat-commands.d.ts +11 -30
- package/build/lib/tools/logcat-commands.d.ts.map +1 -1
- package/build/lib/tools/logcat-commands.js +16 -17
- package/build/lib/tools/logcat-commands.js.map +1 -1
- package/build/lib/tools/network-commands.d.ts +24 -31
- package/build/lib/tools/network-commands.d.ts.map +1 -1
- package/build/lib/tools/network-commands.js +14 -24
- package/build/lib/tools/network-commands.js.map +1 -1
- package/lib/tools/{aab-utils.js → aab-utils.ts} +24 -15
- package/lib/tools/{android-manifest.js → android-manifest.ts} +66 -59
- package/lib/tools/{apks-utils.js → apks-utils.ts} +76 -68
- package/lib/tools/{fs-commands.js → fs-commands.ts} +41 -35
- package/lib/tools/{general-commands.js → general-commands.ts} +71 -68
- package/lib/tools/{keyboard-commands.js → keyboard-commands.ts} +42 -49
- package/lib/tools/{lockmgmt.js → lockmgmt.ts} +71 -56
- package/lib/tools/{logcat-commands.js → logcat-commands.ts} +24 -22
- package/lib/tools/{network-commands.js → network-commands.ts} +42 -34
- package/package.json +1 -1
|
@@ -4,17 +4,21 @@ import {log} from '../logger.js';
|
|
|
4
4
|
import {unzipFile, APKS_EXTENSION, readPackageManifest} from '../helpers.js';
|
|
5
5
|
import {fs, zip, tempDir, util} from '@appium/support';
|
|
6
6
|
import path from 'path';
|
|
7
|
+
import type {ADB} from '../adb.js';
|
|
8
|
+
import type {APKInfo, PlatformInfo, StringRecord} from './types.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Extract package and main activity name from application manifest.
|
|
10
12
|
*
|
|
11
|
-
* @
|
|
12
|
-
* @
|
|
13
|
-
* @return {Promise<import('./types').APKInfo>} The parsed application info.
|
|
13
|
+
* @param appPath - The full path to application .apk(s) package
|
|
14
|
+
* @return The parsed application info.
|
|
14
15
|
* @throws {error} If there was an error while getting the data from the given
|
|
15
16
|
* application package.
|
|
16
17
|
*/
|
|
17
|
-
export async function packageAndLaunchActivityFromManifest(
|
|
18
|
+
export async function packageAndLaunchActivityFromManifest(
|
|
19
|
+
this: ADB,
|
|
20
|
+
appPath: string,
|
|
21
|
+
): Promise<APKInfo> {
|
|
18
22
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
19
23
|
appPath = await this.extractBaseApk(appPath);
|
|
20
24
|
}
|
|
@@ -31,13 +35,12 @@ export async function packageAndLaunchActivityFromManifest(appPath) {
|
|
|
31
35
|
/**
|
|
32
36
|
* Extract target SDK version from application manifest.
|
|
33
37
|
*
|
|
34
|
-
* @
|
|
35
|
-
* @
|
|
36
|
-
* @return {Promise<number>} The version of the target SDK.
|
|
38
|
+
* @param appPath - The full path to .apk(s) package.
|
|
39
|
+
* @return The version of the target SDK.
|
|
37
40
|
* @throws {error} If there was an error while getting the data from the given
|
|
38
41
|
* application package.
|
|
39
42
|
*/
|
|
40
|
-
export async function targetSdkVersionFromManifest(appPath) {
|
|
43
|
+
export async function targetSdkVersionFromManifest(this: ADB, appPath: string): Promise<number> {
|
|
41
44
|
log.debug(`Extracting target SDK version of '${appPath}'`);
|
|
42
45
|
const originalAppPath = appPath;
|
|
43
46
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
@@ -57,13 +60,16 @@ export async function targetSdkVersionFromManifest(appPath) {
|
|
|
57
60
|
/**
|
|
58
61
|
* Extract target SDK version from package information.
|
|
59
62
|
*
|
|
60
|
-
* @
|
|
61
|
-
* @param
|
|
62
|
-
* @param {string?} [cmdOutput=null] - Optional parameter containing the output of
|
|
63
|
+
* @param pkg - The class name of the package installed on the device under test.
|
|
64
|
+
* @param cmdOutput - Optional parameter containing the output of
|
|
63
65
|
* _dumpsys package_ command. It may speed up the method execution.
|
|
64
|
-
* @return
|
|
66
|
+
* @return The version of the target SDK.
|
|
65
67
|
*/
|
|
66
|
-
export async function targetSdkVersionUsingPKG(
|
|
68
|
+
export async function targetSdkVersionUsingPKG(
|
|
69
|
+
this: ADB,
|
|
70
|
+
pkg: string,
|
|
71
|
+
cmdOutput: string | null = null,
|
|
72
|
+
): Promise<number> {
|
|
67
73
|
const stdout = cmdOutput || (await this.shell(['dumpsys', 'package', pkg]));
|
|
68
74
|
const targetSdkVersionMatch = new RegExp(/targetSdk=([^\s\s]+)/g).exec(stdout);
|
|
69
75
|
return targetSdkVersionMatch && targetSdkVersionMatch.length >= 2
|
|
@@ -76,14 +82,18 @@ export async function targetSdkVersionUsingPKG(pkg, cmdOutput = null) {
|
|
|
76
82
|
* `${manifest}.apk` file will be created as the result of this method
|
|
77
83
|
* containing the compiled manifest.
|
|
78
84
|
*
|
|
79
|
-
* @
|
|
80
|
-
* @param
|
|
81
|
-
* @param
|
|
82
|
-
* @param {string} targetPackage - The name of the destination package
|
|
85
|
+
* @param manifest - Full path to the initial manifest template
|
|
86
|
+
* @param manifestPackage - The name of the manifest package
|
|
87
|
+
* @param targetPackage - The name of the destination package
|
|
83
88
|
*/
|
|
84
|
-
export async function compileManifest(
|
|
89
|
+
export async function compileManifest(
|
|
90
|
+
this: ADB,
|
|
91
|
+
manifest: string,
|
|
92
|
+
manifestPackage: string,
|
|
93
|
+
targetPackage: string,
|
|
94
|
+
): Promise<void> {
|
|
85
95
|
const {platform, platformPath} = await getAndroidPlatformAndPath(
|
|
86
|
-
|
|
96
|
+
this.sdkRoot as string,
|
|
87
97
|
);
|
|
88
98
|
if (!platform || !platformPath) {
|
|
89
99
|
throw new Error(
|
|
@@ -98,6 +108,7 @@ export async function compileManifest(manifest, manifestPackage, targetPackage)
|
|
|
98
108
|
try {
|
|
99
109
|
await this.initAapt2();
|
|
100
110
|
// https://developer.android.com/studio/command-line/aapt2
|
|
111
|
+
const binaries = this.binaries as StringRecord;
|
|
101
112
|
const args = [
|
|
102
113
|
'link',
|
|
103
114
|
'-o',
|
|
@@ -113,18 +124,16 @@ export async function compileManifest(manifest, manifestPackage, targetPackage)
|
|
|
113
124
|
'-v',
|
|
114
125
|
];
|
|
115
126
|
log.debug(
|
|
116
|
-
`Compiling the manifest using '${util.quote([
|
|
117
|
-
/** @type {import('./types').StringRecord} */ (this.binaries).aapt2,
|
|
118
|
-
...args,
|
|
119
|
-
])}'`,
|
|
127
|
+
`Compiling the manifest using '${util.quote([binaries.aapt2, ...args])}'`,
|
|
120
128
|
);
|
|
121
|
-
await exec(
|
|
129
|
+
await exec(binaries.aapt2, args);
|
|
122
130
|
} catch (e) {
|
|
123
131
|
log.debug(
|
|
124
132
|
'Cannot compile the manifest using aapt2. Defaulting to aapt. ' +
|
|
125
|
-
`Original error: ${e.
|
|
133
|
+
`Original error: ${(e as Error).message || (e as {stderr?: string}).stderr}`,
|
|
126
134
|
);
|
|
127
135
|
await this.initAapt();
|
|
136
|
+
const binaries = this.binaries as StringRecord;
|
|
128
137
|
const args = [
|
|
129
138
|
'package',
|
|
130
139
|
'-M',
|
|
@@ -140,15 +149,14 @@ export async function compileManifest(manifest, manifestPackage, targetPackage)
|
|
|
140
149
|
'-f',
|
|
141
150
|
];
|
|
142
151
|
log.debug(
|
|
143
|
-
`Compiling the manifest using '${util.quote([
|
|
144
|
-
/** @type {import('./types').StringRecord} */ (this.binaries).aapt,
|
|
145
|
-
...args,
|
|
146
|
-
])}'`,
|
|
152
|
+
`Compiling the manifest using '${util.quote([binaries.aapt, ...args])}'`,
|
|
147
153
|
);
|
|
148
154
|
try {
|
|
149
|
-
await exec(
|
|
155
|
+
await exec(binaries.aapt, args);
|
|
150
156
|
} catch (e1) {
|
|
151
|
-
throw new Error(
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Cannot compile the manifest. Original error: ${(e1 as Error).message || (e1 as {stderr?: string}).stderr}`,
|
|
159
|
+
);
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
log.debug(`Compiled the manifest at '${resultPath}'`);
|
|
@@ -158,41 +166,40 @@ export async function compileManifest(manifest, manifestPackage, targetPackage)
|
|
|
158
166
|
* Replace/insert the specially precompiled manifest file into the
|
|
159
167
|
* particular package.
|
|
160
168
|
*
|
|
161
|
-
* @
|
|
162
|
-
* @param {string} manifest - Full path to the precompiled manifest
|
|
169
|
+
* @param manifest - Full path to the precompiled manifest
|
|
163
170
|
* created by `compileManifest` method call
|
|
164
171
|
* without .apk extension
|
|
165
|
-
* @param
|
|
172
|
+
* @param srcApk - Full path to the existing valid application package, where
|
|
166
173
|
* this manifest has to be insetred to. This package
|
|
167
174
|
* will NOT be modified.
|
|
168
|
-
* @param
|
|
175
|
+
* @param dstApk - Full path to the resulting package.
|
|
169
176
|
* The file will be overridden if it already exists.
|
|
170
177
|
*/
|
|
171
|
-
export async function insertManifest(
|
|
178
|
+
export async function insertManifest(
|
|
179
|
+
this: ADB,
|
|
180
|
+
manifest: string,
|
|
181
|
+
srcApk: string,
|
|
182
|
+
dstApk: string,
|
|
183
|
+
): Promise<void> {
|
|
172
184
|
log.debug(`Inserting manifest '${manifest}', src: '${srcApk}', dst: '${dstApk}'`);
|
|
173
185
|
await zip.assertValidZip(srcApk);
|
|
174
186
|
await unzipFile(`${manifest}.apk`);
|
|
175
187
|
const manifestName = path.basename(manifest);
|
|
176
188
|
try {
|
|
177
189
|
await this.initAapt();
|
|
190
|
+
const binaries = this.binaries as StringRecord;
|
|
178
191
|
await fs.copyFile(srcApk, dstApk);
|
|
179
192
|
log.debug('Moving manifest');
|
|
180
193
|
try {
|
|
181
|
-
await exec(
|
|
182
|
-
'remove',
|
|
183
|
-
dstApk,
|
|
184
|
-
manifestName,
|
|
185
|
-
]);
|
|
194
|
+
await exec(binaries.aapt, ['remove', dstApk, manifestName]);
|
|
186
195
|
} catch {}
|
|
187
|
-
await exec(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
{cwd: path.dirname(manifest)},
|
|
191
|
-
);
|
|
196
|
+
await exec(binaries.aapt, ['add', dstApk, manifestName], {
|
|
197
|
+
cwd: path.dirname(manifest),
|
|
198
|
+
});
|
|
192
199
|
} catch (e) {
|
|
193
200
|
log.debug(
|
|
194
201
|
'Cannot insert manifest using aapt. Defaulting to zip. ' +
|
|
195
|
-
`Original error: ${e.
|
|
202
|
+
`Original error: ${(e as Error).message || (e as {stderr?: string}).stderr}`,
|
|
196
203
|
);
|
|
197
204
|
const tmpRoot = await tempDir.openDir();
|
|
198
205
|
try {
|
|
@@ -217,20 +224,20 @@ export async function insertManifest(manifest, srcApk, dstApk) {
|
|
|
217
224
|
/**
|
|
218
225
|
* Check whether package manifest contains Internet permissions.
|
|
219
226
|
*
|
|
220
|
-
* @
|
|
221
|
-
* @
|
|
222
|
-
* @return {Promise<boolean>} True if the manifest requires Internet access permission.
|
|
227
|
+
* @param appPath - The full path to .apk(s) package.
|
|
228
|
+
* @return True if the manifest requires Internet access permission.
|
|
223
229
|
*/
|
|
224
|
-
export async function hasInternetPermissionFromManifest(
|
|
230
|
+
export async function hasInternetPermissionFromManifest(
|
|
231
|
+
this: ADB,
|
|
232
|
+
appPath: string,
|
|
233
|
+
): Promise<boolean> {
|
|
225
234
|
log.debug(`Checking if '${appPath}' requires internet access permission in the manifest`);
|
|
226
235
|
if (appPath.endsWith(APKS_EXTENSION)) {
|
|
227
236
|
appPath = await this.extractBaseApk(appPath);
|
|
228
237
|
}
|
|
229
238
|
|
|
230
239
|
const {usesPermissions} = await readPackageManifest.bind(this)(appPath);
|
|
231
|
-
return usesPermissions.some(
|
|
232
|
-
(/** @type {string} */ name) => name === 'android.permission.INTERNET',
|
|
233
|
-
);
|
|
240
|
+
return usesPermissions.some((name: string) => name === 'android.permission.INTERNET');
|
|
234
241
|
}
|
|
235
242
|
|
|
236
243
|
// #region Private functions
|
|
@@ -238,16 +245,15 @@ export async function hasInternetPermissionFromManifest(appPath) {
|
|
|
238
245
|
/**
|
|
239
246
|
* Retrieve the path to the recent installed Android platform.
|
|
240
247
|
*
|
|
241
|
-
* @param
|
|
242
|
-
* @return
|
|
248
|
+
* @param sdkRoot
|
|
249
|
+
* @return The resulting path to the newest installed platform.
|
|
243
250
|
*/
|
|
244
|
-
export async function getAndroidPlatformAndPath(sdkRoot) {
|
|
251
|
+
export async function getAndroidPlatformAndPath(sdkRoot: string): Promise<PlatformInfo> {
|
|
245
252
|
const propsPaths = await fs.glob('*/build.prop', {
|
|
246
253
|
cwd: path.resolve(sdkRoot, 'platforms'),
|
|
247
254
|
absolute: true,
|
|
248
255
|
});
|
|
249
|
-
|
|
250
|
-
const platformsMapping = {};
|
|
256
|
+
const platformsMapping: Record<string, PlatformInfo> = {};
|
|
251
257
|
for (const propsPath of propsPaths) {
|
|
252
258
|
const propsContent = await fs.readFile(propsPath, 'utf-8');
|
|
253
259
|
const platformPath = path.dirname(propsPath);
|
|
@@ -280,3 +286,4 @@ export async function getAndroidPlatformAndPath(sdkRoot) {
|
|
|
280
286
|
}
|
|
281
287
|
|
|
282
288
|
// #endregion
|
|
289
|
+
|
|
@@ -7,13 +7,14 @@ import {LRUCache} from 'lru-cache';
|
|
|
7
7
|
import {getJavaForOs, unzipFile, buildInstallArgs, APK_INSTALL_TIMEOUT} from '../helpers.js';
|
|
8
8
|
import AsyncLock from 'async-lock';
|
|
9
9
|
import B from 'bluebird';
|
|
10
|
+
import type {ADB} from '../adb.js';
|
|
11
|
+
import type {InstallMultipleApksOptions, InstallApksOptions, StringRecord} from './types.js';
|
|
10
12
|
|
|
11
13
|
const BASE_APK = 'base-master.apk';
|
|
12
|
-
const LANGUAGE_APK = (lang) => `base-${lang}.apk`;
|
|
13
|
-
|
|
14
|
-
const APKS_CACHE = new LRUCache({
|
|
14
|
+
const LANGUAGE_APK = (lang: string) => `base-${lang}.apk`;
|
|
15
|
+
const APKS_CACHE = new LRUCache<string, string>({
|
|
15
16
|
max: 10,
|
|
16
|
-
dispose: (extractedFilesRoot) => fs.rimraf(
|
|
17
|
+
dispose: (extractedFilesRoot) => fs.rimraf(extractedFilesRoot),
|
|
17
18
|
});
|
|
18
19
|
const APKS_CACHE_GUARD = new AsyncLock();
|
|
19
20
|
const BUNDLETOOL_TIMEOUT_MS = 4 * 60 * 1000;
|
|
@@ -24,7 +25,7 @@ process.on('exit', () => {
|
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const paths =
|
|
28
|
+
const paths = [...APKS_CACHE.values()];
|
|
28
29
|
log.debug(
|
|
29
30
|
`Performing cleanup of ${paths.length} cached .apks ` + util.pluralize('package', paths.length),
|
|
30
31
|
);
|
|
@@ -33,7 +34,7 @@ process.on('exit', () => {
|
|
|
33
34
|
// Asynchronous calls are not supported in onExit handler
|
|
34
35
|
fs.rimrafSync(appPath);
|
|
35
36
|
} catch (e) {
|
|
36
|
-
log.warn(e.message);
|
|
37
|
+
log.warn((e as Error).message);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
});
|
|
@@ -44,17 +45,15 @@ process.on('exit', () => {
|
|
|
44
45
|
* The resulting temporary path, where the .apks file has been extracted,
|
|
45
46
|
* will be stored into the internal LRU cache for better performance.
|
|
46
47
|
*
|
|
47
|
-
* @param
|
|
48
|
-
* @param
|
|
48
|
+
* @param apks - The full path to the .apks file
|
|
49
|
+
* @param dstPath - The relative path to the destination file,
|
|
49
50
|
* which is going to be extracted, where each path component is an array item
|
|
50
|
-
* @returns
|
|
51
|
+
* @returns Full path to the extracted file
|
|
51
52
|
* @throws {Error} If the requested item does not exist in the extracted archive or the provides
|
|
52
53
|
* apks file is not a valid bundle
|
|
53
54
|
*/
|
|
54
|
-
async function extractFromApks(apks, dstPath) {
|
|
55
|
-
|
|
56
|
-
dstPath = [dstPath];
|
|
57
|
-
}
|
|
55
|
+
async function extractFromApks(apks: string, dstPath: string | string[]): Promise<string> {
|
|
56
|
+
const normalizedDstPath = _.isArray(dstPath) ? dstPath : [dstPath];
|
|
58
57
|
|
|
59
58
|
return await APKS_CACHE_GUARD.acquire(apks, async () => {
|
|
60
59
|
// It might be that the original file has been replaced,
|
|
@@ -64,9 +63,12 @@ async function extractFromApks(apks, dstPath) {
|
|
|
64
63
|
log.debug(`Calculated '${apks}' hash: ${apksHash}`);
|
|
65
64
|
|
|
66
65
|
if (APKS_CACHE.has(apksHash)) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
|
|
66
|
+
const cachedRoot = APKS_CACHE.get(apksHash);
|
|
67
|
+
if (cachedRoot) {
|
|
68
|
+
const resultPath = path.resolve(cachedRoot, ...normalizedDstPath);
|
|
69
|
+
if (await fs.exists(resultPath)) {
|
|
70
|
+
return resultPath;
|
|
71
|
+
}
|
|
70
72
|
}
|
|
71
73
|
APKS_CACHE.delete(apksHash);
|
|
72
74
|
}
|
|
@@ -74,10 +76,10 @@ async function extractFromApks(apks, dstPath) {
|
|
|
74
76
|
const tmpRoot = await tempDir.openDir();
|
|
75
77
|
log.debug(`Unpacking application bundle at '${apks}' to '${tmpRoot}'`);
|
|
76
78
|
await unzipFile(apks, tmpRoot);
|
|
77
|
-
const resultPath = path.resolve(tmpRoot, ...
|
|
79
|
+
const resultPath = path.resolve(tmpRoot, ...normalizedDstPath);
|
|
78
80
|
if (!(await fs.exists(resultPath))) {
|
|
79
81
|
throw new Error(
|
|
80
|
-
`${
|
|
82
|
+
`${normalizedDstPath.join(path.sep)} cannot be found in '${apks}' bundle. ` +
|
|
81
83
|
`Does the archive contain a valid application bundle?`,
|
|
82
84
|
);
|
|
83
85
|
}
|
|
@@ -89,20 +91,20 @@ async function extractFromApks(apks, dstPath) {
|
|
|
89
91
|
/**
|
|
90
92
|
* Executes bundletool utility with given arguments and returns the actual stdout
|
|
91
93
|
*
|
|
92
|
-
* @
|
|
93
|
-
* @param
|
|
94
|
-
* @
|
|
95
|
-
* @returns {Promise<string>} the actual command stdout
|
|
94
|
+
* @param args - the list of bundletool arguments
|
|
95
|
+
* @param errorMsg - The customized error message string
|
|
96
|
+
* @returns the actual command stdout
|
|
96
97
|
* @throws {Error} If bundletool jar does not exist in PATH or there was an error while
|
|
97
98
|
* executing it
|
|
98
99
|
*/
|
|
99
|
-
export async function execBundletool(
|
|
100
|
+
export async function execBundletool(
|
|
101
|
+
this: ADB,
|
|
102
|
+
args: string[],
|
|
103
|
+
errorMsg: string,
|
|
104
|
+
): Promise<string> {
|
|
100
105
|
await this.initBundletool();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/** @type {import('./types').StringRecord} */ (this.binaries).bundletool,
|
|
104
|
-
...args,
|
|
105
|
-
];
|
|
106
|
+
const binaries = this.binaries as StringRecord;
|
|
107
|
+
args = ['-jar', binaries.bundletool, ...args];
|
|
106
108
|
const env = process.env;
|
|
107
109
|
if (this.adbPort) {
|
|
108
110
|
env.ANDROID_ADB_SERVER_PORT = `${this.adbPort}`;
|
|
@@ -111,7 +113,7 @@ export async function execBundletool(args, errorMsg) {
|
|
|
111
113
|
env.ANDROID_ADB_SERVER_HOST = this.adbHost;
|
|
112
114
|
}
|
|
113
115
|
log.debug(`Executing bundletool with arguments: ${JSON.stringify(args)}`);
|
|
114
|
-
let stdout;
|
|
116
|
+
let stdout: string;
|
|
115
117
|
try {
|
|
116
118
|
({stdout} = await exec(await getJavaForOs(), args, {
|
|
117
119
|
env,
|
|
@@ -120,31 +122,30 @@ export async function execBundletool(args, errorMsg) {
|
|
|
120
122
|
log.debug(`Command stdout: ${_.truncate(stdout, {length: 300})}`);
|
|
121
123
|
return stdout;
|
|
122
124
|
} catch (e) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
const err = e as Error & {stdout?: string; stderr?: string};
|
|
126
|
+
if (err.stdout) {
|
|
127
|
+
log.debug(`Command stdout: ${err.stdout}`);
|
|
125
128
|
}
|
|
126
|
-
if (
|
|
127
|
-
log.debug(`Command stderr: ${
|
|
129
|
+
if (err.stderr) {
|
|
130
|
+
log.debug(`Command stderr: ${err.stderr}`);
|
|
128
131
|
}
|
|
129
|
-
throw new Error(`${errorMsg}. Original error: ${
|
|
132
|
+
throw new Error(`${errorMsg}. Original error: ${err.message}`);
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
/**
|
|
134
137
|
*
|
|
135
|
-
* @
|
|
136
|
-
* @
|
|
137
|
-
* @returns {Promise<string>} The same `specLocation` value
|
|
138
|
+
* @param specLocation - The full path to the generated device spec location
|
|
139
|
+
* @returns The same `specLocation` value
|
|
138
140
|
* @throws {Error} If it is not possible to retrieve the spec for the current device
|
|
139
141
|
*/
|
|
140
|
-
export async function getDeviceSpec(specLocation) {
|
|
141
|
-
|
|
142
|
-
const args = [
|
|
142
|
+
export async function getDeviceSpec(this: ADB, specLocation: string): Promise<string> {
|
|
143
|
+
const args: string[] = [
|
|
143
144
|
'get-device-spec',
|
|
144
145
|
'--adb',
|
|
145
146
|
this.executable.path,
|
|
146
147
|
'--device-id',
|
|
147
|
-
|
|
148
|
+
this.curDeviceId as string,
|
|
148
149
|
'--output',
|
|
149
150
|
specLocation,
|
|
150
151
|
];
|
|
@@ -156,15 +157,18 @@ export async function getDeviceSpec(specLocation) {
|
|
|
156
157
|
/**
|
|
157
158
|
* Installs the given apks into the device under test
|
|
158
159
|
*
|
|
159
|
-
* @
|
|
160
|
-
* @param
|
|
161
|
-
* @param {import('./types').InstallMultipleApksOptions} [options={}] - Installation options
|
|
160
|
+
* @param apkPathsToInstall - The full paths to install apks
|
|
161
|
+
* @param options - Installation options
|
|
162
162
|
*/
|
|
163
|
-
export async function installMultipleApks(
|
|
163
|
+
export async function installMultipleApks(
|
|
164
|
+
this: ADB,
|
|
165
|
+
apkPathsToInstall: string[],
|
|
166
|
+
options: InstallMultipleApksOptions = {},
|
|
167
|
+
): Promise<string> {
|
|
164
168
|
const installArgs = buildInstallArgs(await this.getApiLevel(), options);
|
|
165
169
|
return await this.adbExec(['install-multiple', ...installArgs, ...apkPathsToInstall], {
|
|
166
170
|
// @ts-ignore This validation works
|
|
167
|
-
timeout: isNaN(options.timeout) ? undefined : options.timeout,
|
|
171
|
+
timeout: isNaN(Number(options.timeout)) ? undefined : options.timeout,
|
|
168
172
|
timeoutCapName: options.timeoutCapName,
|
|
169
173
|
});
|
|
170
174
|
}
|
|
@@ -172,16 +176,18 @@ export async function installMultipleApks(apkPathsToInstall, options = {}) {
|
|
|
172
176
|
/**
|
|
173
177
|
* Installs the given .apks package into the device under test
|
|
174
178
|
*
|
|
175
|
-
* @
|
|
176
|
-
* @param
|
|
177
|
-
* @param {import('./types').InstallApksOptions} [options={}] - Installation options
|
|
179
|
+
* @param apks - The full path to the .apks file
|
|
180
|
+
* @param options - Installation options
|
|
178
181
|
* @throws {Error} If the .apks bundle cannot be installed
|
|
179
182
|
*/
|
|
180
|
-
export async function installApks(
|
|
183
|
+
export async function installApks(
|
|
184
|
+
this: ADB,
|
|
185
|
+
apks: string,
|
|
186
|
+
options: InstallApksOptions = {},
|
|
187
|
+
): Promise<void> {
|
|
181
188
|
const {grantPermissions, allowTestPackages, timeout} = options;
|
|
182
189
|
|
|
183
|
-
|
|
184
|
-
const args = [
|
|
190
|
+
const args: string[] = [
|
|
185
191
|
'install-apks',
|
|
186
192
|
'--adb',
|
|
187
193
|
this.executable.path,
|
|
@@ -190,13 +196,12 @@ export async function installApks(apks, options = {}) {
|
|
|
190
196
|
'--timeout-millis',
|
|
191
197
|
`${timeout || APKS_INSTALL_TIMEOUT}`,
|
|
192
198
|
'--device-id',
|
|
193
|
-
|
|
199
|
+
this.curDeviceId as string,
|
|
194
200
|
];
|
|
195
201
|
if (allowTestPackages) {
|
|
196
202
|
args.push('--allow-test-only');
|
|
197
203
|
}
|
|
198
|
-
|
|
199
|
-
const tasks = [
|
|
204
|
+
const tasks: Promise<any>[] = [
|
|
200
205
|
this.execBundletool(
|
|
201
206
|
args,
|
|
202
207
|
`Cannot install '${path.basename(apks)}' to the device ${this.curDeviceId}`,
|
|
@@ -215,12 +220,11 @@ export async function installApks(apks, options = {}) {
|
|
|
215
220
|
/**
|
|
216
221
|
* Extracts and returns the full path to the master .apk file inside the bundle.
|
|
217
222
|
*
|
|
218
|
-
* @
|
|
219
|
-
* @
|
|
220
|
-
* @returns {Promise<string>} The full path to the master bundle .apk
|
|
223
|
+
* @param apks - The full path to the .apks file
|
|
224
|
+
* @returns The full path to the master bundle .apk
|
|
221
225
|
* @throws {Error} If there was an error while extracting/finding the file
|
|
222
226
|
*/
|
|
223
|
-
export async function extractBaseApk(apks) {
|
|
227
|
+
export async function extractBaseApk(this: ADB, apks: string): Promise<string> {
|
|
224
228
|
return await extractFromApks(apks, ['splits', BASE_APK]);
|
|
225
229
|
}
|
|
226
230
|
|
|
@@ -228,20 +232,23 @@ export async function extractBaseApk(apks) {
|
|
|
228
232
|
* Extracts and returns the full path to the .apk, which contains the corresponding
|
|
229
233
|
* resources for the given language in the .apks bundle.
|
|
230
234
|
*
|
|
231
|
-
* @
|
|
232
|
-
* @param
|
|
233
|
-
* @param {?string} [language=null] - The language abbreviation. The default language is
|
|
235
|
+
* @param apks - The full path to the .apks file
|
|
236
|
+
* @param language - The language abbreviation. The default language is
|
|
234
237
|
* going to be selected if it is not set.
|
|
235
|
-
* @returns
|
|
238
|
+
* @returns The full path to the corresponding language .apk or the master .apk
|
|
236
239
|
* if language split is not enabled for the bundle.
|
|
237
240
|
* @throws {Error} If there was an error while extracting/finding the file
|
|
238
241
|
*/
|
|
239
|
-
export async function extractLanguageApk(
|
|
242
|
+
export async function extractLanguageApk(
|
|
243
|
+
this: ADB,
|
|
244
|
+
apks: string,
|
|
245
|
+
language: string | null = null,
|
|
246
|
+
): Promise<string> {
|
|
240
247
|
if (language) {
|
|
241
248
|
try {
|
|
242
249
|
return await extractFromApks(apks, ['splits', LANGUAGE_APK(language)]);
|
|
243
250
|
} catch (e) {
|
|
244
|
-
log.debug(e.message);
|
|
251
|
+
log.debug((e as Error).message);
|
|
245
252
|
log.info(
|
|
246
253
|
`Assuming that splitting by language is not enabled for the '${apks}' bundle ` +
|
|
247
254
|
`and returning the main apk instead`,
|
|
@@ -266,9 +273,10 @@ export async function extractLanguageApk(apks, language = null) {
|
|
|
266
273
|
|
|
267
274
|
/**
|
|
268
275
|
*
|
|
269
|
-
* @param
|
|
270
|
-
* @returns
|
|
276
|
+
* @param output
|
|
277
|
+
* @returns
|
|
271
278
|
*/
|
|
272
|
-
export function isTestPackageOnlyError(output) {
|
|
279
|
+
export function isTestPackageOnlyError(output: string): boolean {
|
|
273
280
|
return /\[INSTALL_FAILED_TEST_ONLY\]/.test(output);
|
|
274
281
|
}
|
|
282
|
+
|