appium-android-driver 12.4.5 → 12.4.7
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 +12 -0
- package/build/lib/commands/app-management.d.ts +167 -113
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +147 -103
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/find.d.ts +20 -3
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +10 -3
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/performance.d.ts +80 -51
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +113 -89
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/shell.d.ts +21 -0
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +21 -0
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +34 -64
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +41 -80
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/lib/commands/app-management.ts +639 -0
- package/lib/commands/find.ts +20 -3
- package/lib/commands/{performance.js → performance.ts} +167 -108
- package/lib/commands/shell.ts +21 -0
- package/lib/commands/{streamscreen.js → streamscreen.ts} +86 -109
- package/package.json +2 -2
- package/lib/commands/app-management.js +0 -533
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import {util} from '@appium/support';
|
|
2
|
-
import {waitForCondition, longSleep} from 'asyncbox';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
import {EOL} from 'node:os';
|
|
5
|
-
import B from 'bluebird';
|
|
6
|
-
|
|
7
|
-
const APP_EXTENSIONS = ['.apk', '.apks'];
|
|
8
|
-
const PACKAGE_INSTALL_TIMEOUT_MS = 90000;
|
|
9
|
-
// These constants are in sync with
|
|
10
|
-
// https://developer.apple.com/documentation/xctest/xcuiapplicationstate/xcuiapplicationstaterunningbackground?language=objc
|
|
11
|
-
export const APP_STATE = /** @type {const} */ ({
|
|
12
|
-
NOT_INSTALLED: 0,
|
|
13
|
-
NOT_RUNNING: 1,
|
|
14
|
-
RUNNING_IN_BACKGROUND: 3,
|
|
15
|
-
RUNNING_IN_FOREGROUND: 4,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} IsAppInstalledOptions
|
|
20
|
-
* @property {string} [user] - The user id
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @this {AndroidDriver}
|
|
25
|
-
* @param {string} appId
|
|
26
|
-
* @param {IsAppInstalledOptions} [opts={}]
|
|
27
|
-
* @returns {Promise<boolean>}
|
|
28
|
-
*/
|
|
29
|
-
export async function isAppInstalled(appId, opts = {}) {
|
|
30
|
-
return await this.adb.isAppInstalled(appId, opts);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @this {AndroidDriver}
|
|
35
|
-
* @param {string} appId Application package identifier
|
|
36
|
-
* @param {string | number} [user] The user ID for which the package is installed.
|
|
37
|
-
* The `current` user id is used by default.
|
|
38
|
-
* @returns {Promise<boolean>}
|
|
39
|
-
*/
|
|
40
|
-
export async function mobileIsAppInstalled(appId, user) {
|
|
41
|
-
const _opts = {};
|
|
42
|
-
if (util.hasValue(user)) {
|
|
43
|
-
_opts.user = `${user}`;
|
|
44
|
-
}
|
|
45
|
-
return await this.isAppInstalled(appId, _opts);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @this {AndroidDriver}
|
|
50
|
-
* @param {string} appId Application package identifier
|
|
51
|
-
* @returns {Promise<import('./types').AppState>}
|
|
52
|
-
*/
|
|
53
|
-
export async function queryAppState(appId) {
|
|
54
|
-
this.log.info(`Querying the state of '${appId}'`);
|
|
55
|
-
if (!(await this.adb.isAppInstalled(appId))) {
|
|
56
|
-
return APP_STATE.NOT_INSTALLED;
|
|
57
|
-
}
|
|
58
|
-
if (!(await this.adb.isAppRunning(appId))) {
|
|
59
|
-
return APP_STATE.NOT_RUNNING;
|
|
60
|
-
}
|
|
61
|
-
const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
|
|
62
|
-
for (const line of (await this.adb.dumpWindows()).split('\n')) {
|
|
63
|
-
if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
|
|
64
|
-
return APP_STATE.RUNNING_IN_FOREGROUND;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return APP_STATE.RUNNING_IN_BACKGROUND;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @this {AndroidDriver}
|
|
72
|
-
* @param {string} appId Application package identifier
|
|
73
|
-
* @returns {Promise<void>}
|
|
74
|
-
*/
|
|
75
|
-
export async function activateApp(appId) {
|
|
76
|
-
return await this.adb.activateApp(appId);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @this {AndroidDriver}
|
|
81
|
-
* @param {string} appId
|
|
82
|
-
* @param {Omit<import('appium-adb').UninstallOptions, 'appId'>} opts
|
|
83
|
-
* @returns {Promise<boolean>}
|
|
84
|
-
*/
|
|
85
|
-
export async function removeApp(appId, opts = {}) {
|
|
86
|
-
return await this.adb.uninstallApk(appId, opts);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @this {import('../driver').AndroidDriver}
|
|
91
|
-
* @param {string} appId Application package identifier
|
|
92
|
-
* @param {number} [timeout] The count of milliseconds to wait until the
|
|
93
|
-
* app is uninstalled.
|
|
94
|
-
* @param {boolean} [keepData] Set to true in order to keep the
|
|
95
|
-
* application data and cache folders after uninstall.
|
|
96
|
-
* @param {boolean} [skipInstallCheck] Whether to check if the app is installed prior to
|
|
97
|
-
* uninstalling it. By default this is checked.
|
|
98
|
-
* @returns {Promise<boolean>}
|
|
99
|
-
*/
|
|
100
|
-
export async function mobileRemoveApp(appId, timeout, keepData, skipInstallCheck) {
|
|
101
|
-
return await this.removeApp(appId, {
|
|
102
|
-
timeout,
|
|
103
|
-
keepData,
|
|
104
|
-
skipInstallCheck,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* @this {AndroidDriver}
|
|
110
|
-
* @param {string} appId
|
|
111
|
-
* @param {import('./types').TerminateAppOpts} [options={}]
|
|
112
|
-
* @returns {Promise<boolean>}
|
|
113
|
-
*/
|
|
114
|
-
export async function terminateApp(appId, options = {}) {
|
|
115
|
-
this.log.info(`Terminating '${appId}'`);
|
|
116
|
-
const pids = await this.adb.listAppProcessIds(appId);
|
|
117
|
-
if (_.isEmpty(pids)) {
|
|
118
|
-
this.log.info(`The app '${appId}' is not running`);
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
await this.adb.forceStop(appId);
|
|
122
|
-
const timeout =
|
|
123
|
-
util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
|
|
124
|
-
? parseInt(String(options.timeout), 10)
|
|
125
|
-
: 500;
|
|
126
|
-
|
|
127
|
-
if (timeout <= 0) {
|
|
128
|
-
this.log.info(
|
|
129
|
-
`'${appId}' has been terminated. Skipping checking of the application process state ` +
|
|
130
|
-
`since the timeout was set to ${timeout}ms`,
|
|
131
|
-
);
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** @type {number[]} */
|
|
136
|
-
let currentPids = [];
|
|
137
|
-
try {
|
|
138
|
-
await waitForCondition(async () => {
|
|
139
|
-
if (await this.queryAppState(appId) <= APP_STATE.NOT_RUNNING) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
currentPids = await this.adb.listAppProcessIds(appId);
|
|
143
|
-
if (_.isEmpty(currentPids) || _.isEmpty(_.intersection(pids, currentPids))) {
|
|
144
|
-
this.log.info(
|
|
145
|
-
`The application '${appId}' was reported running, ` +
|
|
146
|
-
`although all process ids belonging to it have been changed: ` +
|
|
147
|
-
`(${JSON.stringify(pids)} -> ${JSON.stringify(currentPids)}). ` +
|
|
148
|
-
`Assuming the termination was successful.`
|
|
149
|
-
);
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
return false;
|
|
153
|
-
}, {
|
|
154
|
-
waitMs: timeout,
|
|
155
|
-
intervalMs: 100,
|
|
156
|
-
});
|
|
157
|
-
} catch {
|
|
158
|
-
if (!_.isEmpty(currentPids) && !_.isEmpty(_.difference(pids, currentPids))) {
|
|
159
|
-
this.log.warn(
|
|
160
|
-
`Some of processes belonging to the '${appId}' applcation are still running ` +
|
|
161
|
-
`after ${timeout}ms (${JSON.stringify(pids)} -> ${JSON.stringify(currentPids)})`
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
throw this.log.errorWithException(`'${appId}' is still running after ${timeout}ms timeout`);
|
|
165
|
-
}
|
|
166
|
-
this.log.info(`'${appId}' has been successfully terminated`);
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* @this {AndroidDriver}
|
|
172
|
-
* @param {string} appId Application package identifier
|
|
173
|
-
* @param {number|string} [timeout] The count of milliseconds to wait until the app is terminated.
|
|
174
|
-
* 500ms by default.
|
|
175
|
-
* @returns {Promise<boolean>}
|
|
176
|
-
*/
|
|
177
|
-
export async function mobileTerminateApp(appId, timeout) {
|
|
178
|
-
return await this.terminateApp(appId, {
|
|
179
|
-
timeout,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* @this {AndroidDriver}
|
|
185
|
-
* @param {string} appPath
|
|
186
|
-
* @param {Omit<import('appium-adb').InstallOptions, 'appId'>} opts
|
|
187
|
-
* @returns {Promise<void>}
|
|
188
|
-
*/
|
|
189
|
-
export async function installApp(appPath, opts) {
|
|
190
|
-
const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
|
|
191
|
-
await this.adb.install(localPath, opts);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* @this {AndroidDriver}
|
|
196
|
-
* @param {string} appPath
|
|
197
|
-
* @param {boolean} [checkVersion]
|
|
198
|
-
* @param {number} [timeout] The count of milliseconds to wait until the app is installed.
|
|
199
|
-
* 20000ms by default.
|
|
200
|
-
* @param {boolean} [allowTestPackages] Set to true in order to allow test packages installation.
|
|
201
|
-
* `false` by default.
|
|
202
|
-
* @param {boolean} [useSdcard] Set to true to install the app on sdcard instead of the device memory.
|
|
203
|
-
* `false` by default.
|
|
204
|
-
* @param {boolean} [grantPermissions] Set to true in order to grant all the
|
|
205
|
-
* permissions requested in the application's manifest automatically after the installation is completed
|
|
206
|
-
* under Android 6+. `false` by default.
|
|
207
|
-
* @param {boolean} [replace] Set it to false if you don't want the application to be upgraded/reinstalled
|
|
208
|
-
* if it is already present on the device. `true` by default.
|
|
209
|
-
* @param {boolean} [noIncremental] Forcefully disables incremental installs if set to `true`.
|
|
210
|
-
* Read https://developer.android.com/preview/features#incremental for more details.
|
|
211
|
-
* `false` by default.
|
|
212
|
-
* @returns {Promise<void>}
|
|
213
|
-
*/
|
|
214
|
-
export async function mobileInstallApp(
|
|
215
|
-
appPath,
|
|
216
|
-
checkVersion,
|
|
217
|
-
timeout,
|
|
218
|
-
allowTestPackages,
|
|
219
|
-
useSdcard,
|
|
220
|
-
grantPermissions,
|
|
221
|
-
replace,
|
|
222
|
-
noIncremental,
|
|
223
|
-
) {
|
|
224
|
-
const opts = {
|
|
225
|
-
timeout,
|
|
226
|
-
allowTestPackages,
|
|
227
|
-
useSdcard,
|
|
228
|
-
grantPermissions,
|
|
229
|
-
replace,
|
|
230
|
-
noIncremental,
|
|
231
|
-
};
|
|
232
|
-
if (checkVersion) {
|
|
233
|
-
const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
|
|
234
|
-
await this.adb.installOrUpgrade(localPath, null, {
|
|
235
|
-
...opts,
|
|
236
|
-
enforceCurrentBuild: false,
|
|
237
|
-
});
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return await this.installApp(appPath, opts);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* @this {AndroidDriver}
|
|
246
|
-
* @param {string} appId Application package identifier
|
|
247
|
-
* @returns {Promise<void>}
|
|
248
|
-
*/
|
|
249
|
-
export async function mobileClearApp(appId) {
|
|
250
|
-
await this.adb.clear(appId);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @this {AndroidDriver}
|
|
255
|
-
* @returns {Promise<string>}
|
|
256
|
-
*/
|
|
257
|
-
export async function getCurrentActivity() {
|
|
258
|
-
return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appActivity);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* @this {AndroidDriver}
|
|
263
|
-
* @returns {Promise<string>}
|
|
264
|
-
*/
|
|
265
|
-
export async function getCurrentPackage() {
|
|
266
|
-
return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appPackage);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* @this {AndroidDriver}
|
|
271
|
-
* @param {number} seconds
|
|
272
|
-
* @returns {Promise<string|true>}
|
|
273
|
-
*/
|
|
274
|
-
export async function background(seconds) {
|
|
275
|
-
if (seconds < 0) {
|
|
276
|
-
// if user passes in a negative seconds value, interpret that as the instruction
|
|
277
|
-
// to not bring the app back at all
|
|
278
|
-
await this.adb.goToHome();
|
|
279
|
-
return true;
|
|
280
|
-
}
|
|
281
|
-
let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
|
|
282
|
-
await this.adb.goToHome();
|
|
283
|
-
|
|
284
|
-
// people can wait for a long time, so to be safe let's use the longSleep function and log
|
|
285
|
-
// progress periodically.
|
|
286
|
-
const sleepMs = seconds * 1000;
|
|
287
|
-
const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
|
|
288
|
-
// for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
|
|
289
|
-
const intervalMs = _.min([30 * 1000, parseInt(String(sleepMs / 100), 10)]);
|
|
290
|
-
/**
|
|
291
|
-
*
|
|
292
|
-
* @param {{elapsedMs: number, progress: number}} param0
|
|
293
|
-
*/
|
|
294
|
-
const progressCb = ({elapsedMs, progress}) => {
|
|
295
|
-
const waitSecs = (elapsedMs / 1000).toFixed(0);
|
|
296
|
-
const progressPct = (progress * 100).toFixed(2);
|
|
297
|
-
this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
|
|
298
|
-
};
|
|
299
|
-
await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
|
|
300
|
-
|
|
301
|
-
/** @type {import('appium-adb').StartAppOptions} */
|
|
302
|
-
let args;
|
|
303
|
-
if (this._cachedActivityArgs?.[`${appPackage}/${appActivity}`]) {
|
|
304
|
-
// the activity was started with `startActivity`, so use those args to restart
|
|
305
|
-
args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
|
|
306
|
-
} else {
|
|
307
|
-
try {
|
|
308
|
-
this.log.debug(`Activating app '${appPackage}' in order to restore it`);
|
|
309
|
-
await this.adb.activateApp(/** @type {string} */ (appPackage));
|
|
310
|
-
return true;
|
|
311
|
-
} catch {}
|
|
312
|
-
args =
|
|
313
|
-
(appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
|
|
314
|
-
(appPackage === this.opts.appWaitPackage &&
|
|
315
|
-
(this.opts.appWaitActivity || '').split(',').includes(String(appActivity)))
|
|
316
|
-
? {
|
|
317
|
-
// the activity is the original session activity, so use the original args
|
|
318
|
-
pkg: /** @type {string} */ (this.opts.appPackage),
|
|
319
|
-
activity: this.opts.appActivity ?? undefined,
|
|
320
|
-
action: this.opts.intentAction,
|
|
321
|
-
category: this.opts.intentCategory,
|
|
322
|
-
flags: this.opts.intentFlags,
|
|
323
|
-
waitPkg: this.opts.appWaitPackage ?? undefined,
|
|
324
|
-
waitActivity: this.opts.appWaitActivity ?? undefined,
|
|
325
|
-
waitForLaunch: this.opts.appWaitForLaunch,
|
|
326
|
-
waitDuration: this.opts.appWaitDuration,
|
|
327
|
-
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
328
|
-
stopApp: false,
|
|
329
|
-
user: this.opts.userProfile,
|
|
330
|
-
}
|
|
331
|
-
: {
|
|
332
|
-
// the activity was started some other way, so use defaults
|
|
333
|
-
pkg: /** @type {string} */ (appPackage),
|
|
334
|
-
activity: appActivity ?? undefined,
|
|
335
|
-
waitPkg: appPackage ?? undefined,
|
|
336
|
-
waitActivity: appActivity ?? undefined,
|
|
337
|
-
stopApp: false,
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
args = /** @type {import('appium-adb').StartAppOptions} */ (
|
|
341
|
-
_.pickBy(args, (value) => !_.isUndefined(value))
|
|
342
|
-
);
|
|
343
|
-
this.log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
|
|
344
|
-
return await this.adb.startApp(args);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Puts the app to background and waits the given number of seconds then restores the app
|
|
349
|
-
* if necessary. The call is blocking.
|
|
350
|
-
*
|
|
351
|
-
* @this {AndroidDriver}
|
|
352
|
-
* @param {number} [seconds=-1] The amount of seconds to wait between putting the app to background and restoring it.
|
|
353
|
-
* Any negative value means to not restore the app after putting it to background.
|
|
354
|
-
* @returns {Promise<void>}
|
|
355
|
-
*/
|
|
356
|
-
export async function mobileBackgroundApp (seconds = -1) {
|
|
357
|
-
await this.background(seconds);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* @this {AndroidDriver}
|
|
362
|
-
* @param {import('../driver').AndroidDriverOpts?} [opts=null]
|
|
363
|
-
* @returns {Promise<void>}
|
|
364
|
-
*/
|
|
365
|
-
export async function resetAUT(opts = null) {
|
|
366
|
-
const {
|
|
367
|
-
app,
|
|
368
|
-
appPackage,
|
|
369
|
-
fastReset,
|
|
370
|
-
fullReset,
|
|
371
|
-
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
372
|
-
autoGrantPermissions,
|
|
373
|
-
allowTestPackages,
|
|
374
|
-
} = opts ?? this.opts;
|
|
375
|
-
|
|
376
|
-
if (!appPackage) {
|
|
377
|
-
throw new Error("'appPackage' option is required");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const isInstalled = await this.adb.isAppInstalled(appPackage);
|
|
381
|
-
|
|
382
|
-
if (isInstalled) {
|
|
383
|
-
try {
|
|
384
|
-
await this.adb.forceStop(appPackage);
|
|
385
|
-
} catch {}
|
|
386
|
-
// fullReset has priority over fastReset
|
|
387
|
-
if (!fullReset && fastReset) {
|
|
388
|
-
const output = await this.adb.clear(appPackage);
|
|
389
|
-
if (_.isString(output) && output.toLowerCase().includes('failed')) {
|
|
390
|
-
throw new Error(
|
|
391
|
-
`Cannot clear the application data of '${appPackage}'. Original error: ${output}`,
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
// executing `shell pm clear` resets previously assigned application permissions as well
|
|
395
|
-
if (autoGrantPermissions) {
|
|
396
|
-
try {
|
|
397
|
-
await this.adb.grantAllPermissions(appPackage);
|
|
398
|
-
} catch (error) {
|
|
399
|
-
this.log.error(`Unable to grant permissions requested. Original error: ${error.message}`);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
this.log.debug(
|
|
403
|
-
`Performed fast reset on the installed '${appPackage}' application (stop and clear)`,
|
|
404
|
-
);
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (!app) {
|
|
410
|
-
throw new Error(
|
|
411
|
-
`Either provide 'app' option to install '${appPackage}' or ` +
|
|
412
|
-
`consider setting 'noReset' to 'true' if '${appPackage}' is supposed to be preinstalled.`,
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
this.log.debug(`Running full reset on '${appPackage}' (reinstall)`);
|
|
417
|
-
if (isInstalled) {
|
|
418
|
-
await this.adb.uninstallApk(appPackage);
|
|
419
|
-
}
|
|
420
|
-
await this.adb.install(app, {
|
|
421
|
-
grantPermissions: autoGrantPermissions,
|
|
422
|
-
timeout: androidInstallTimeout,
|
|
423
|
-
allowTestPackages,
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* @this {AndroidDriver}
|
|
429
|
-
* @param {import('../driver').AndroidDriverOpts?} [opts=null]
|
|
430
|
-
* @returns {Promise<void>}
|
|
431
|
-
*/
|
|
432
|
-
export async function installAUT(opts = null) {
|
|
433
|
-
const {
|
|
434
|
-
app,
|
|
435
|
-
appPackage,
|
|
436
|
-
fastReset,
|
|
437
|
-
fullReset,
|
|
438
|
-
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
439
|
-
autoGrantPermissions,
|
|
440
|
-
allowTestPackages,
|
|
441
|
-
enforceAppInstall,
|
|
442
|
-
} = opts ?? this.opts;
|
|
443
|
-
|
|
444
|
-
if (!app || !appPackage) {
|
|
445
|
-
throw new Error("'app' and 'appPackage' options are required");
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if (fullReset) {
|
|
449
|
-
await this.resetAUT(opts);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const {appState, wasUninstalled} = await this.adb.installOrUpgrade(app, appPackage, {
|
|
454
|
-
grantPermissions: autoGrantPermissions,
|
|
455
|
-
timeout: androidInstallTimeout,
|
|
456
|
-
allowTestPackages,
|
|
457
|
-
enforceCurrentBuild: enforceAppInstall,
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// There is no need to reset the newly installed app
|
|
461
|
-
const isInstalledOverExistingApp =
|
|
462
|
-
!wasUninstalled && appState !== this.adb.APP_INSTALL_STATE.NOT_INSTALLED;
|
|
463
|
-
if (fastReset && isInstalledOverExistingApp) {
|
|
464
|
-
this.log.info(`Performing fast reset on '${appPackage}'`);
|
|
465
|
-
await this.resetAUT(opts);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* @this {AndroidDriver}
|
|
471
|
-
* @param {string[]} otherApps
|
|
472
|
-
* @param {import('../driver').AndroidDriverOpts?} [opts=null]
|
|
473
|
-
* @returns {Promise<void>}
|
|
474
|
-
*/
|
|
475
|
-
export async function installOtherApks(otherApps, opts = null) {
|
|
476
|
-
const {
|
|
477
|
-
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
478
|
-
autoGrantPermissions,
|
|
479
|
-
allowTestPackages,
|
|
480
|
-
} = opts ?? this.opts;
|
|
481
|
-
|
|
482
|
-
// Install all of the APK's asynchronously
|
|
483
|
-
await B.all(
|
|
484
|
-
otherApps.map((otherApp) => {
|
|
485
|
-
this.log.debug(`Installing app: ${otherApp}`);
|
|
486
|
-
return this.adb.installOrUpgrade(otherApp, undefined, {
|
|
487
|
-
grantPermissions: autoGrantPermissions,
|
|
488
|
-
timeout: androidInstallTimeout,
|
|
489
|
-
allowTestPackages,
|
|
490
|
-
});
|
|
491
|
-
}),
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* @this {AndroidDriver}
|
|
497
|
-
* @param {string[]} appPackages
|
|
498
|
-
* @param {string[]} [filterPackages=[]]
|
|
499
|
-
* @returns {Promise<void>}
|
|
500
|
-
*/
|
|
501
|
-
export async function uninstallOtherPackages(appPackages, filterPackages = []) {
|
|
502
|
-
if (appPackages.includes('*')) {
|
|
503
|
-
this.log.debug('Uninstall third party packages');
|
|
504
|
-
appPackages = await getThirdPartyPackages.bind(this)(filterPackages);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
this.log.debug(`Uninstalling packages: ${appPackages}`);
|
|
508
|
-
await B.all(appPackages.map((appPackage) => this.adb.uninstallApk(appPackage)));
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* @this {AndroidDriver}
|
|
513
|
-
* @param {string[]} [filterPackages=[]]
|
|
514
|
-
* @returns {Promise<string[]>}
|
|
515
|
-
*/
|
|
516
|
-
export async function getThirdPartyPackages(filterPackages = []) {
|
|
517
|
-
try {
|
|
518
|
-
const packagesString = await this.adb.shell(['pm', 'list', 'packages', '-3']);
|
|
519
|
-
const appPackagesArray = packagesString
|
|
520
|
-
.trim()
|
|
521
|
-
.replace(/package:/g, '')
|
|
522
|
-
.split(EOL);
|
|
523
|
-
this.log.debug(`'${appPackagesArray}' filtered with '${filterPackages}'`);
|
|
524
|
-
return _.difference(appPackagesArray, filterPackages);
|
|
525
|
-
} catch (err) {
|
|
526
|
-
this.log.warn(`Unable to get packages with 'adb shell pm list packages -3': ${err.message}`);
|
|
527
|
-
return [];
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* @typedef {import('../driver').AndroidDriver} AndroidDriver
|
|
533
|
-
*/
|