appium-android-driver 7.8.3 → 8.0.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/CHANGELOG.md +25 -0
- package/build/lib/commands/app-management.d.ts +129 -5
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +433 -128
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/appearance.d.ts +17 -4
- package/build/lib/commands/appearance.d.ts.map +1 -1
- package/build/lib/commands/appearance.js +32 -33
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/context/cache.d.ts +19 -0
- package/build/lib/commands/context/cache.d.ts.map +1 -0
- package/build/lib/commands/context/cache.js +32 -0
- package/build/lib/commands/context/cache.js.map +1 -0
- package/build/lib/commands/context/exports.d.ts +141 -0
- package/build/lib/commands/context/exports.d.ts.map +1 -0
- package/build/lib/commands/context/exports.js +351 -0
- package/build/lib/commands/context/exports.js.map +1 -0
- package/build/lib/commands/context/helpers.d.ts +98 -0
- package/build/lib/commands/context/helpers.d.ts.map +1 -0
- package/build/lib/commands/context/helpers.js +715 -0
- package/build/lib/commands/context/helpers.js.map +1 -0
- package/build/lib/commands/device/common.d.ts +23 -0
- package/build/lib/commands/device/common.d.ts.map +1 -0
- package/build/lib/commands/device/common.js +230 -0
- package/build/lib/commands/device/common.js.map +1 -0
- package/build/lib/commands/device/emulator-actions.d.ts +114 -0
- package/build/lib/commands/device/emulator-actions.d.ts.map +1 -0
- package/build/lib/commands/device/emulator-actions.js +197 -0
- package/build/lib/commands/device/emulator-actions.js.map +1 -0
- package/build/lib/commands/device/emulator-console.d.ts +7 -0
- package/build/lib/commands/device/emulator-console.d.ts.map +1 -0
- package/build/lib/commands/device/emulator-console.js +24 -0
- package/build/lib/commands/device/emulator-console.js.map +1 -0
- package/build/lib/commands/device/utils.d.ts +50 -0
- package/build/lib/commands/device/utils.d.ts.map +1 -0
- package/build/lib/commands/device/utils.js +238 -0
- package/build/lib/commands/device/utils.js.map +1 -0
- package/build/lib/commands/deviceidle.d.ts +8 -5
- package/build/lib/commands/deviceidle.d.ts.map +1 -1
- package/build/lib/commands/deviceidle.js +31 -37
- package/build/lib/commands/deviceidle.js.map +1 -1
- package/build/lib/commands/element.d.ts +99 -5
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +152 -116
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/execute.d.ts +12 -4
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +83 -78
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts +42 -5
- package/build/lib/commands/file-actions.d.ts.map +1 -1
- package/build/lib/commands/file-actions.js +230 -194
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +5 -4
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +7 -10
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/geolocation.d.ts +45 -0
- package/build/lib/commands/geolocation.d.ts.map +1 -0
- package/build/lib/commands/geolocation.js +182 -0
- package/build/lib/commands/geolocation.js.map +1 -0
- package/build/lib/commands/ime.d.ts +25 -5
- package/build/lib/commands/ime.d.ts.map +1 -1
- package/build/lib/commands/ime.js +59 -42
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/intent.d.ts +56 -5
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +135 -83
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +58 -4
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +119 -17
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/lock/exports.d.ts +301 -0
- package/build/lib/commands/lock/exports.d.ts.map +1 -0
- package/build/lib/commands/lock/exports.js +121 -0
- package/build/lib/commands/lock/exports.js.map +1 -0
- package/build/lib/commands/lock/helpers.d.ts +349 -0
- package/build/lib/commands/lock/helpers.d.ts.map +1 -0
- package/build/lib/commands/lock/helpers.js +375 -0
- package/build/lib/commands/lock/helpers.js.map +1 -0
- package/build/lib/commands/log.d.ts +59 -5
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +150 -140
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +16 -5
- package/build/lib/commands/media-projection.d.ts.map +1 -1
- package/build/lib/commands/media-projection.js +69 -58
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/memory.d.ts +9 -5
- package/build/lib/commands/memory.d.ts.map +1 -1
- package/build/lib/commands/memory.js +19 -24
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/misc.d.ts +42 -0
- package/build/lib/commands/misc.d.ts.map +1 -0
- package/build/lib/commands/misc.js +100 -0
- package/build/lib/commands/misc.js.map +1 -0
- package/build/lib/commands/network.d.ts +61 -5
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +196 -189
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/performance.d.ts +67 -27
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +105 -80
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +12 -6
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +65 -62
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +44 -5
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +131 -126
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/resources.d.ts +16 -0
- package/build/lib/commands/resources.d.ts.map +1 -0
- package/build/lib/commands/resources.js +91 -0
- package/build/lib/commands/resources.js.map +1 -0
- package/build/lib/commands/shell.d.ts +8 -5
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +29 -33
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +34 -6
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +166 -162
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts +18 -13
- package/build/lib/commands/system-bars.d.ts.map +1 -1
- package/build/lib/commands/system-bars.js +68 -64
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/time.d.ts +14 -0
- package/build/lib/commands/time.d.ts.map +1 -0
- package/build/lib/commands/time.js +39 -0
- package/build/lib/commands/time.js.map +1 -0
- package/build/lib/commands/touch.d.ts +99 -6
- package/build/lib/commands/touch.d.ts.map +1 -1
- package/build/lib/commands/touch.js +399 -280
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/commands/types.d.ts +110 -2
- package/build/lib/commands/types.d.ts.map +1 -1
- package/build/lib/doctor/checks.d.ts.map +1 -1
- package/build/lib/doctor/checks.js +4 -4
- package/build/lib/doctor/checks.js.map +1 -1
- package/build/lib/driver.d.ts +224 -27
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +232 -7
- package/build/lib/driver.js.map +1 -1
- package/build/lib/index.d.ts +1 -4
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +1 -13
- package/build/lib/index.js.map +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/method-map.d.ts +0 -23
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +0 -11
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/utils.d.ts +12 -0
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +38 -2
- package/build/lib/utils.js.map +1 -1
- package/lib/commands/app-management.js +470 -145
- package/lib/commands/appearance.js +29 -36
- package/lib/commands/context/cache.js +29 -0
- package/lib/commands/context/exports.js +379 -0
- package/lib/commands/context/helpers.js +802 -0
- package/lib/commands/device/common.js +264 -0
- package/lib/commands/device/emulator-actions.js +194 -0
- package/lib/commands/device/emulator-console.js +24 -0
- package/lib/commands/device/utils.js +285 -0
- package/lib/commands/deviceidle.js +31 -44
- package/lib/commands/element.js +149 -142
- package/lib/commands/execute.js +86 -87
- package/lib/commands/file-actions.js +249 -222
- package/lib/commands/find.ts +13 -19
- package/lib/commands/geolocation.js +179 -0
- package/lib/commands/ime.js +53 -45
- package/lib/commands/intent.js +149 -91
- package/lib/commands/keyboard.js +114 -17
- package/lib/commands/lock/exports.js +139 -0
- package/lib/commands/lock/helpers.js +379 -0
- package/lib/commands/log.js +170 -166
- package/lib/commands/media-projection.js +75 -70
- package/lib/commands/memory.js +17 -29
- package/lib/commands/misc.js +94 -0
- package/lib/commands/network.js +209 -223
- package/lib/commands/performance.js +88 -73
- package/lib/commands/permissions.js +83 -84
- package/lib/commands/recordscreen.js +171 -170
- package/lib/commands/resources.js +96 -0
- package/lib/commands/shell.js +28 -42
- package/lib/commands/streamscreen.js +207 -206
- package/lib/commands/system-bars.js +76 -77
- package/lib/commands/time.js +36 -0
- package/lib/commands/touch.js +442 -346
- package/lib/commands/types.ts +123 -2
- package/lib/doctor/checks.js +24 -16
- package/lib/driver.ts +454 -12
- package/lib/index.ts +1 -13
- package/lib/logger.js +1 -1
- package/lib/method-map.js +0 -11
- package/lib/utils.js +40 -3
- package/package.json +1 -1
- package/build/lib/commands/actions.d.ts +0 -8
- package/build/lib/commands/actions.d.ts.map +0 -1
- package/build/lib/commands/actions.js +0 -207
- package/build/lib/commands/actions.js.map +0 -1
- package/build/lib/commands/alert.d.ts +0 -8
- package/build/lib/commands/alert.d.ts.map +0 -1
- package/build/lib/commands/alert.js +0 -29
- package/build/lib/commands/alert.js.map +0 -1
- package/build/lib/commands/context.d.ts +0 -10
- package/build/lib/commands/context.d.ts.map +0 -1
- package/build/lib/commands/context.js +0 -431
- package/build/lib/commands/context.js.map +0 -1
- package/build/lib/commands/emu-console.d.ts +0 -7
- package/build/lib/commands/emu-console.d.ts.map +0 -1
- package/build/lib/commands/emu-console.js +0 -27
- package/build/lib/commands/emu-console.js.map +0 -1
- package/build/lib/commands/general.d.ts +0 -9
- package/build/lib/commands/general.d.ts.map +0 -1
- package/build/lib/commands/general.js +0 -293
- package/build/lib/commands/general.js.map +0 -1
- package/build/lib/commands/index.d.ts +0 -28
- package/build/lib/commands/index.d.ts.map +0 -1
- package/build/lib/commands/index.js +0 -57
- package/build/lib/commands/index.js.map +0 -1
- package/build/lib/commands/mixins.d.ts +0 -747
- package/build/lib/commands/mixins.d.ts.map +0 -1
- package/build/lib/commands/mixins.js +0 -19
- package/build/lib/commands/mixins.js.map +0 -1
- package/build/lib/helpers/android.d.ts +0 -163
- package/build/lib/helpers/android.d.ts.map +0 -1
- package/build/lib/helpers/android.js +0 -818
- package/build/lib/helpers/android.js.map +0 -1
- package/build/lib/helpers/index.d.ts +0 -7
- package/build/lib/helpers/index.d.ts.map +0 -1
- package/build/lib/helpers/index.js +0 -29
- package/build/lib/helpers/index.js.map +0 -1
- package/build/lib/helpers/types.d.ts +0 -122
- package/build/lib/helpers/types.d.ts.map +0 -1
- package/build/lib/helpers/types.js +0 -3
- package/build/lib/helpers/types.js.map +0 -1
- package/build/lib/helpers/unlock.d.ts +0 -32
- package/build/lib/helpers/unlock.d.ts.map +0 -1
- package/build/lib/helpers/unlock.js +0 -273
- package/build/lib/helpers/unlock.js.map +0 -1
- package/build/lib/helpers/webview.d.ts +0 -74
- package/build/lib/helpers/webview.d.ts.map +0 -1
- package/build/lib/helpers/webview.js +0 -448
- package/build/lib/helpers/webview.js.map +0 -1
- package/lib/commands/actions.js +0 -244
- package/lib/commands/alert.js +0 -34
- package/lib/commands/context.js +0 -507
- package/lib/commands/emu-console.js +0 -31
- package/lib/commands/general.js +0 -343
- package/lib/commands/index.ts +0 -54
- package/lib/commands/mixins.ts +0 -976
- package/lib/helpers/android.ts +0 -1153
- package/lib/helpers/index.ts +0 -6
- package/lib/helpers/types.ts +0 -136
- package/lib/helpers/unlock.ts +0 -329
- package/lib/helpers/webview.ts +0 -610
|
@@ -1,176 +1,501 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import {util} from '@appium/support';
|
|
4
|
-
import {waitForCondition} from 'asyncbox';
|
|
2
|
+
import {waitForCondition, longSleep} from 'asyncbox';
|
|
5
3
|
import _ from 'lodash';
|
|
6
|
-
import {APP_STATE} from '../helpers';
|
|
7
4
|
import {requireArgs} from '../utils';
|
|
8
|
-
import {
|
|
5
|
+
import {EOL} from 'node:os';
|
|
6
|
+
import B from 'bluebird';
|
|
9
7
|
|
|
10
8
|
const APP_EXTENSIONS = ['.apk', '.apks'];
|
|
11
9
|
const RESOLVER_ACTIVITY_NAME = 'android/com.android.internal.app.ResolverActivity';
|
|
10
|
+
const PACKAGE_INSTALL_TIMEOUT_MS = 90000;
|
|
11
|
+
// These constants are in sync with
|
|
12
|
+
// https://developer.apple.com/documentation/xctest/xcuiapplicationstate/xcuiapplicationstaterunningbackground?language=objc
|
|
13
|
+
export const APP_STATE = /** @type {const} */ ({
|
|
14
|
+
NOT_INSTALLED: 0,
|
|
15
|
+
NOT_RUNNING: 1,
|
|
16
|
+
RUNNING_IN_BACKGROUND: 3,
|
|
17
|
+
RUNNING_IN_FOREGROUND: 4,
|
|
18
|
+
});
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
|
-
* @
|
|
15
|
-
* @
|
|
21
|
+
* @this {AndroidDriver}
|
|
22
|
+
* @param {string} appId
|
|
23
|
+
* @returns {Promise<boolean>}
|
|
16
24
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
25
|
+
export async function isAppInstalled(appId) {
|
|
26
|
+
return await this.adb.isAppInstalled(appId);
|
|
27
|
+
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @this {AndroidDriver}
|
|
31
|
+
* @param {import('./types').IsAppInstalledOpts} opts
|
|
32
|
+
* @returns {Promise<boolean>}
|
|
33
|
+
*/
|
|
34
|
+
export async function mobileIsAppInstalled(opts) {
|
|
35
|
+
const {appId} = requireArgs('appId', opts);
|
|
36
|
+
return await this.isAppInstalled(appId);
|
|
37
|
+
}
|
|
26
38
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
/**
|
|
40
|
+
* @this {AndroidDriver}
|
|
41
|
+
* @param {string} appId
|
|
42
|
+
* @returns {Promise<import('./types').AppState>}
|
|
43
|
+
*/
|
|
44
|
+
export async function queryAppState(appId) {
|
|
45
|
+
this.log.info(`Querying the state of '${appId}'`);
|
|
46
|
+
if (!(await this.adb.isAppInstalled(appId))) {
|
|
47
|
+
return APP_STATE.NOT_INSTALLED;
|
|
48
|
+
}
|
|
49
|
+
if (!(await this.adb.processExists(appId))) {
|
|
50
|
+
return APP_STATE.NOT_RUNNING;
|
|
51
|
+
}
|
|
52
|
+
const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
|
|
53
|
+
for (const line of (await this.adb.dumpWindows()).split('\n')) {
|
|
54
|
+
if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
|
|
55
|
+
return APP_STATE.RUNNING_IN_FOREGROUND;
|
|
31
56
|
}
|
|
32
|
-
|
|
33
|
-
|
|
57
|
+
}
|
|
58
|
+
return APP_STATE.RUNNING_IN_BACKGROUND;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @this {AndroidDriver}
|
|
63
|
+
* @param {import('./types').QueryAppStateOpts} opts
|
|
64
|
+
* @returns {Promise<import('./types').AppState>}
|
|
65
|
+
*/
|
|
66
|
+
export async function mobileQueryAppState(opts) {
|
|
67
|
+
const {appId} = requireArgs('appId', opts);
|
|
68
|
+
return await this.queryAppState(appId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @this {AndroidDriver}
|
|
73
|
+
* @param {string} appId
|
|
74
|
+
* @returns {Promise<void>}
|
|
75
|
+
*/
|
|
76
|
+
export async function activateApp(appId) {
|
|
77
|
+
this.log.debug(`Activating '${appId}'`);
|
|
78
|
+
const apiLevel = await this.adb.getApiLevel();
|
|
79
|
+
// Fallback to Monkey in older APIs
|
|
80
|
+
if (apiLevel < 24) {
|
|
81
|
+
// The monkey command could raise an issue as https://stackoverflow.com/questions/44860475/how-to-use-the-monkey-command-with-an-android-system-that-doesnt-have-physical
|
|
82
|
+
// but '--pct-syskeys 0' could cause another background process issue. https://github.com/appium/appium/issues/16941#issuecomment-1129837285
|
|
83
|
+
const cmd = ['monkey', '-p', appId, '-c', 'android.intent.category.LAUNCHER', '1'];
|
|
84
|
+
let output = '';
|
|
85
|
+
try {
|
|
86
|
+
output = await this.adb.shell(cmd);
|
|
87
|
+
this.log.debug(`Command stdout: ${output}`);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
this.log.errorAndThrow(
|
|
90
|
+
`Cannot activate '${appId}'. Original error: ${/** @type {Error} */ (e).message}`,
|
|
91
|
+
);
|
|
34
92
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
|
|
38
|
-
return APP_STATE.RUNNING_IN_FOREGROUND;
|
|
39
|
-
}
|
|
93
|
+
if (output.includes('monkey aborted')) {
|
|
94
|
+
this.log.errorAndThrow(`Cannot activate '${appId}'. Are you sure it is installed?`);
|
|
40
95
|
}
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let activityName = await this.adb.resolveLaunchableActivity(appId);
|
|
100
|
+
if (activityName === RESOLVER_ACTIVITY_NAME) {
|
|
101
|
+
// https://github.com/appium/appium/issues/17128
|
|
102
|
+
this.log.debug(
|
|
103
|
+
`The launchable activity name of '${appId}' was resolved to '${activityName}'. ` +
|
|
104
|
+
`Switching the resolver to not use cmd`,
|
|
105
|
+
);
|
|
106
|
+
activityName = await this.adb.resolveLaunchableActivity(appId, {preferCmd: false});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const stdout = await this.adb.shell([
|
|
110
|
+
'am',
|
|
111
|
+
apiLevel < 26 ? 'start' : 'start-activity',
|
|
112
|
+
'-a',
|
|
113
|
+
'android.intent.action.MAIN',
|
|
114
|
+
'-c',
|
|
115
|
+
'android.intent.category.LAUNCHER',
|
|
116
|
+
// FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
|
117
|
+
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
|
|
118
|
+
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
|
119
|
+
'-f',
|
|
120
|
+
'0x10200000',
|
|
121
|
+
'-n',
|
|
122
|
+
activityName,
|
|
123
|
+
]);
|
|
124
|
+
this.log.debug(stdout);
|
|
125
|
+
if (/^error:/im.test(stdout)) {
|
|
126
|
+
throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @this {AndroidDriver}
|
|
132
|
+
* @param {import('./types').ActivateAppOpts} opts
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
export async function mobileActivateApp(opts) {
|
|
136
|
+
const {appId} = requireArgs('appId', opts);
|
|
137
|
+
return await this.activateApp(appId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @this {AndroidDriver}
|
|
142
|
+
* @param {string} appId
|
|
143
|
+
* @param {Omit<import('appium-adb').UninstallOptions, 'appId'>} opts
|
|
144
|
+
* @returns {Promise<boolean>}
|
|
145
|
+
*/
|
|
146
|
+
export async function removeApp(appId, opts = {}) {
|
|
147
|
+
return await this.adb.uninstallApk(appId, opts);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @this {import('../driver').AndroidDriver}
|
|
152
|
+
* @param {import('./types').RemoveAppOpts} opts
|
|
153
|
+
* @returns {Promise<boolean>}
|
|
154
|
+
*/
|
|
155
|
+
export async function mobileRemoveApp(opts) {
|
|
156
|
+
const {appId} = requireArgs('appId', opts);
|
|
157
|
+
return await this.removeApp(appId, opts);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @this {AndroidDriver}
|
|
162
|
+
* @param {string} appId
|
|
163
|
+
* @param {Omit<import('./types').TerminateAppOpts, 'appId'>} [options={}]
|
|
164
|
+
* @returns {Promise<boolean>}
|
|
165
|
+
*/
|
|
166
|
+
export async function terminateApp(appId, options = {}) {
|
|
167
|
+
this.log.info(`Terminating '${appId}'`);
|
|
168
|
+
if (!(await this.adb.processExists(appId))) {
|
|
169
|
+
this.log.info(`The app '${appId}' is not running`);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
await this.adb.forceStop(appId);
|
|
173
|
+
const timeout =
|
|
174
|
+
util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
|
|
175
|
+
? parseInt(String(options.timeout), 10)
|
|
176
|
+
: 500;
|
|
177
|
+
|
|
178
|
+
if (timeout <= 0) {
|
|
179
|
+
this.log.info(
|
|
180
|
+
`'${appId}' has been terminated. Skip checking the application process state ` +
|
|
181
|
+
`since the timeout was set as ${timeout}ms`,
|
|
182
|
+
);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
await waitForCondition(async () => (await this.queryAppState(appId)) <= APP_STATE.NOT_RUNNING, {
|
|
188
|
+
waitMs: timeout,
|
|
189
|
+
intervalMs: 100,
|
|
190
|
+
});
|
|
191
|
+
} catch (e) {
|
|
192
|
+
this.log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
|
|
193
|
+
}
|
|
194
|
+
this.log.info(`'${appId}' has been successfully terminated`);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @this {AndroidDriver}
|
|
200
|
+
* @param {import('./types').TerminateAppOpts} opts
|
|
201
|
+
* @returns {Promise<boolean>}
|
|
202
|
+
*/
|
|
203
|
+
export async function mobileTerminateApp(opts) {
|
|
204
|
+
const {appId} = requireArgs('appId', opts);
|
|
205
|
+
return await this.terminateApp(appId, opts);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @this {AndroidDriver}
|
|
210
|
+
* @param {string} appPath
|
|
211
|
+
* @param {Omit<import('appium-adb').InstallOptions, 'appId'>} opts
|
|
212
|
+
* @returns {Promise<void>}
|
|
213
|
+
*/
|
|
214
|
+
export async function installApp(appPath, opts) {
|
|
215
|
+
const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
|
|
216
|
+
await this.adb.install(localPath, opts);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @this {AndroidDriver}
|
|
221
|
+
* @param {import('./types').InstallAppOpts} opts
|
|
222
|
+
* @returns {Promise<void>}
|
|
223
|
+
*/
|
|
224
|
+
export async function mobileInstallApp(opts) {
|
|
225
|
+
const {appPath} = requireArgs('appPath', opts);
|
|
226
|
+
return await this.installApp(appPath, opts);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @this {AndroidDriver}
|
|
231
|
+
* @param {import('./types').ClearAppOpts} opts
|
|
232
|
+
* @returns {Promise<void>}
|
|
233
|
+
*/
|
|
234
|
+
export async function mobileClearApp(opts) {
|
|
235
|
+
const {appId} = requireArgs('appId', opts);
|
|
236
|
+
await this.adb.clear(appId);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @this {AndroidDriver}
|
|
241
|
+
* @returns {Promise<string>}
|
|
242
|
+
*/
|
|
243
|
+
export async function getCurrentActivity() {
|
|
244
|
+
return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appActivity);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @this {AndroidDriver}
|
|
249
|
+
* @returns {Promise<string>}
|
|
250
|
+
*/
|
|
251
|
+
export async function getCurrentPackage() {
|
|
252
|
+
return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appPackage);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @this {AndroidDriver}
|
|
257
|
+
* @param {number} seconds
|
|
258
|
+
* @returns {Promise<string|true>}
|
|
259
|
+
*/
|
|
260
|
+
export async function background(seconds) {
|
|
261
|
+
if (seconds < 0) {
|
|
262
|
+
// if user passes in a negative seconds value, interpret that as the instruction
|
|
263
|
+
// to not bring the app back at all
|
|
264
|
+
await this.adb.goToHome();
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
|
|
268
|
+
await this.adb.goToHome();
|
|
269
|
+
|
|
270
|
+
// people can wait for a long time, so to be safe let's use the longSleep function and log
|
|
271
|
+
// progress periodically.
|
|
272
|
+
const sleepMs = seconds * 1000;
|
|
273
|
+
const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
|
|
274
|
+
// for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
|
|
275
|
+
const intervalMs = _.min([30 * 1000, parseInt(String(sleepMs / 100), 10)]);
|
|
276
|
+
/**
|
|
277
|
+
*
|
|
278
|
+
* @param {{elapsedMs: number, progress: number}} param0
|
|
279
|
+
*/
|
|
280
|
+
const progressCb = ({elapsedMs, progress}) => {
|
|
281
|
+
const waitSecs = (elapsedMs / 1000).toFixed(0);
|
|
282
|
+
const progressPct = (progress * 100).toFixed(2);
|
|
283
|
+
this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
|
|
284
|
+
};
|
|
285
|
+
await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
|
|
286
|
+
|
|
287
|
+
/** @type {import('appium-adb').StartAppOptions} */
|
|
288
|
+
let args;
|
|
289
|
+
if (this._cachedActivityArgs && this._cachedActivityArgs[`${appPackage}/${appActivity}`]) {
|
|
290
|
+
// the activity was started with `startActivity`, so use those args to restart
|
|
291
|
+
args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
|
|
292
|
+
} else {
|
|
293
|
+
try {
|
|
294
|
+
this.log.debug(`Activating app '${appPackage}' in order to restore it`);
|
|
295
|
+
await this.activateApp(/** @type {string} */ (appPackage));
|
|
296
|
+
return true;
|
|
297
|
+
} catch (ign) {}
|
|
298
|
+
args =
|
|
299
|
+
(appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
|
|
300
|
+
(appPackage === this.opts.appWaitPackage &&
|
|
301
|
+
(this.opts.appWaitActivity || '').split(',').includes(String(appActivity)))
|
|
302
|
+
? {
|
|
303
|
+
// the activity is the original session activity, so use the original args
|
|
304
|
+
pkg: /** @type {string} */ (this.opts.appPackage),
|
|
305
|
+
activity: this.opts.appActivity ?? undefined,
|
|
306
|
+
action: this.opts.intentAction,
|
|
307
|
+
category: this.opts.intentCategory,
|
|
308
|
+
flags: this.opts.intentFlags,
|
|
309
|
+
waitPkg: this.opts.appWaitPackage ?? undefined,
|
|
310
|
+
waitActivity: this.opts.appWaitActivity ?? undefined,
|
|
311
|
+
waitForLaunch: this.opts.appWaitForLaunch,
|
|
312
|
+
waitDuration: this.opts.appWaitDuration,
|
|
313
|
+
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
314
|
+
stopApp: false,
|
|
315
|
+
user: this.opts.userProfile,
|
|
316
|
+
}
|
|
317
|
+
: {
|
|
318
|
+
// the activity was started some other way, so use defaults
|
|
319
|
+
pkg: /** @type {string} */ (appPackage),
|
|
320
|
+
activity: appActivity ?? undefined,
|
|
321
|
+
waitPkg: appPackage ?? undefined,
|
|
322
|
+
waitActivity: appActivity ?? undefined,
|
|
323
|
+
stopApp: false,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
args = /** @type {import('appium-adb').StartAppOptions} */ (
|
|
327
|
+
_.pickBy(args, (value) => !_.isUndefined(value))
|
|
328
|
+
);
|
|
329
|
+
this.log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
|
|
330
|
+
return await this.adb.startApp(args);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @this {AndroidDriver}
|
|
335
|
+
* @param {import('../driver').AndroidDriverOpts?} [opts=null]
|
|
336
|
+
* @returns {Promise<void>}
|
|
337
|
+
*/
|
|
338
|
+
export async function resetApp(opts = null) {
|
|
339
|
+
const {
|
|
340
|
+
app,
|
|
341
|
+
appPackage,
|
|
342
|
+
fastReset,
|
|
343
|
+
fullReset,
|
|
344
|
+
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
345
|
+
autoGrantPermissions,
|
|
346
|
+
allowTestPackages,
|
|
347
|
+
} = opts ?? this.opts;
|
|
348
|
+
|
|
349
|
+
if (!appPackage) {
|
|
350
|
+
throw new Error("'appPackage' option is required");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const isInstalled = await this.adb.isAppInstalled(appPackage);
|
|
354
|
+
|
|
355
|
+
if (isInstalled) {
|
|
356
|
+
try {
|
|
357
|
+
await this.adb.forceStop(appPackage);
|
|
358
|
+
} catch (ign) {}
|
|
359
|
+
// fullReset has priority over fastReset
|
|
360
|
+
if (!fullReset && fastReset) {
|
|
361
|
+
const output = await this.adb.clear(appPackage);
|
|
362
|
+
if (_.isString(output) && output.toLowerCase().includes('failed')) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`Cannot clear the application data of '${appPackage}'. Original error: ${output}`,
|
|
64
365
|
);
|
|
65
366
|
}
|
|
66
|
-
|
|
67
|
-
|
|
367
|
+
// executing `shell pm clear` resets previously assigned application permissions as well
|
|
368
|
+
if (autoGrantPermissions) {
|
|
369
|
+
try {
|
|
370
|
+
await this.adb.grantAllPermissions(appPackage);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
this.log.error(`Unable to grant permissions requested. Original error: ${error.message}`);
|
|
373
|
+
}
|
|
68
374
|
}
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let activityName = await this.adb.resolveLaunchableActivity(appId);
|
|
73
|
-
if (activityName === RESOLVER_ACTIVITY_NAME) {
|
|
74
|
-
// https://github.com/appium/appium/issues/17128
|
|
75
375
|
this.log.debug(
|
|
76
|
-
`
|
|
77
|
-
`Switching the resolver to not use cmd`
|
|
376
|
+
`Performed fast reset on the installed '${appPackage}' application (stop and clear)`,
|
|
78
377
|
);
|
|
79
|
-
|
|
378
|
+
return;
|
|
80
379
|
}
|
|
380
|
+
}
|
|
81
381
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'android.intent.category.LAUNCHER',
|
|
89
|
-
// FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
|
90
|
-
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
|
|
91
|
-
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
|
92
|
-
'-f',
|
|
93
|
-
'0x10200000',
|
|
94
|
-
'-n',
|
|
95
|
-
activityName,
|
|
96
|
-
]);
|
|
97
|
-
this.log.debug(stdout);
|
|
98
|
-
if (/^error:/im.test(stdout)) {
|
|
99
|
-
throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
async mobileActivateApp(opts) {
|
|
104
|
-
const {appId} = requireArgs('appId', opts);
|
|
105
|
-
return await this.activateApp(appId);
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
async removeApp(appId, opts = {}) {
|
|
109
|
-
return await this.adb.uninstallApk(appId, opts);
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
async mobileRemoveApp(opts) {
|
|
113
|
-
const {appId} = requireArgs('appId', opts);
|
|
114
|
-
return await this.removeApp(appId, opts);
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
async terminateApp(appId, options = {}) {
|
|
118
|
-
this.log.info(`Terminating '${appId}'`);
|
|
119
|
-
if (!(await this.adb.processExists(appId))) {
|
|
120
|
-
this.log.info(`The app '${appId}' is not running`);
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
await this.adb.forceStop(appId);
|
|
124
|
-
const timeout =
|
|
125
|
-
util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
|
|
126
|
-
? parseInt(String(options.timeout), 10)
|
|
127
|
-
: 500;
|
|
128
|
-
|
|
129
|
-
if (timeout <= 0) {
|
|
130
|
-
this.log.info(
|
|
131
|
-
`'${appId}' has been terminated. Skip checking the application process state ` +
|
|
132
|
-
`since the timeout was set as ${timeout}ms`
|
|
133
|
-
);
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
382
|
+
if (!app) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`Either provide 'app' option to install '${appPackage}' or ` +
|
|
385
|
+
`consider setting 'noReset' to 'true' if '${appPackage}' is supposed to be preinstalled.`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
136
388
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
389
|
+
this.log.debug(`Running full reset on '${appPackage}' (reinstall)`);
|
|
390
|
+
if (isInstalled) {
|
|
391
|
+
await this.adb.uninstallApk(appPackage);
|
|
392
|
+
}
|
|
393
|
+
await this.adb.install(app, {
|
|
394
|
+
grantPermissions: autoGrantPermissions,
|
|
395
|
+
timeout: androidInstallTimeout,
|
|
396
|
+
allowTestPackages,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export async function installApk(opts = null) {
|
|
401
|
+
const {
|
|
402
|
+
app,
|
|
403
|
+
appPackage,
|
|
404
|
+
fastReset,
|
|
405
|
+
fullReset,
|
|
406
|
+
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
407
|
+
autoGrantPermissions,
|
|
408
|
+
allowTestPackages,
|
|
409
|
+
enforceAppInstall,
|
|
410
|
+
} = opts ?? this.opts;
|
|
411
|
+
|
|
412
|
+
if (!app || !appPackage) {
|
|
413
|
+
throw new Error("'app' and 'appPackage' options are required");
|
|
414
|
+
}
|
|
148
415
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
}
|
|
416
|
+
if (fullReset) {
|
|
417
|
+
await this.resetApp(opts);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
153
420
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
421
|
+
const {appState, wasUninstalled} = await this.adb.installOrUpgrade(app, appPackage, {
|
|
422
|
+
grantPermissions: autoGrantPermissions,
|
|
423
|
+
timeout: androidInstallTimeout,
|
|
424
|
+
allowTestPackages,
|
|
425
|
+
enforceCurrentBuild: enforceAppInstall,
|
|
426
|
+
});
|
|
158
427
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
428
|
+
// There is no need to reset the newly installed app
|
|
429
|
+
const isInstalledOverExistingApp =
|
|
430
|
+
!wasUninstalled && appState !== this.adb.APP_INSTALL_STATE.NOT_INSTALLED;
|
|
431
|
+
if (fastReset && isInstalledOverExistingApp) {
|
|
432
|
+
this.log.info(`Performing fast reset on '${appPackage}'`);
|
|
433
|
+
await this.resetApp(opts);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
163
436
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
437
|
+
/**
|
|
438
|
+
* @this {AndroidDriver}
|
|
439
|
+
* @param {string[]} otherApps
|
|
440
|
+
* @param {import('../driver').AndroidDriverOpts?} [opts=null]
|
|
441
|
+
* @returns {Promise<void>}
|
|
442
|
+
*/
|
|
443
|
+
export async function installOtherApks(otherApps, opts = null) {
|
|
444
|
+
const {
|
|
445
|
+
androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
|
|
446
|
+
autoGrantPermissions,
|
|
447
|
+
allowTestPackages,
|
|
448
|
+
} = opts ?? this.opts;
|
|
169
449
|
|
|
170
|
-
|
|
450
|
+
// Install all of the APK's asynchronously
|
|
451
|
+
await B.all(
|
|
452
|
+
otherApps.map((otherApp) => {
|
|
453
|
+
this.log.debug(`Installing app: ${otherApp}`);
|
|
454
|
+
return this.adb.installOrUpgrade(otherApp, undefined, {
|
|
455
|
+
grantPermissions: autoGrantPermissions,
|
|
456
|
+
timeout: androidInstallTimeout,
|
|
457
|
+
allowTestPackages,
|
|
458
|
+
});
|
|
459
|
+
}),
|
|
460
|
+
);
|
|
461
|
+
}
|
|
171
462
|
|
|
172
|
-
|
|
463
|
+
/**
|
|
464
|
+
* @this {AndroidDriver}
|
|
465
|
+
* @param {string[]} appPackages
|
|
466
|
+
* @param {string[]} [filterPackages=[]]
|
|
467
|
+
* @returns {Promise<void>}
|
|
468
|
+
*/
|
|
469
|
+
export async function uninstallOtherPackages(appPackages, filterPackages = []) {
|
|
470
|
+
if (appPackages.includes('*')) {
|
|
471
|
+
this.log.debug('Uninstall third party packages');
|
|
472
|
+
appPackages = await getThirdPartyPackages.bind(this)(filterPackages);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
this.log.debug(`Uninstalling packages: ${appPackages}`);
|
|
476
|
+
await B.all(appPackages.map((appPackage) => this.adb.uninstallApk(appPackage)));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @this {AndroidDriver}
|
|
481
|
+
* @param {string[]} [filterPackages=[]]
|
|
482
|
+
* @returns {Promise<string[]>}
|
|
483
|
+
*/
|
|
484
|
+
export async function getThirdPartyPackages(filterPackages = []) {
|
|
485
|
+
try {
|
|
486
|
+
const packagesString = await this.adb.shell(['pm', 'list', 'packages', '-3']);
|
|
487
|
+
const appPackagesArray = packagesString
|
|
488
|
+
.trim()
|
|
489
|
+
.replace(/package:/g, '')
|
|
490
|
+
.split(EOL);
|
|
491
|
+
this.log.debug(`'${appPackagesArray}' filtered with '${filterPackages}'`);
|
|
492
|
+
return _.difference(appPackagesArray, filterPackages);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
this.log.warn(`Unable to get packages with 'adb shell pm list packages -3': ${err.message}`);
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
}
|
|
173
498
|
|
|
174
499
|
/**
|
|
175
|
-
* @typedef {import('
|
|
500
|
+
* @typedef {import('../driver').AndroidDriver} AndroidDriver
|
|
176
501
|
*/
|