appium-android-driver 5.14.7 → 6.0.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/CHANGELOG.md +7 -0
- package/build/lib/commands/actions.d.ts +6 -224
- package/build/lib/commands/actions.d.ts.map +1 -1
- package/build/lib/commands/actions.js +306 -405
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/alert.d.ts +7 -9
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +24 -18
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +7 -313
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +135 -293
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/context.d.ts +8 -92
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +381 -439
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/element.d.ts +8 -35
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +153 -136
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/emu-console.d.ts +6 -48
- package/build/lib/commands/emu-console.d.ts.map +1 -1
- package/build/lib/commands/emu-console.js +19 -34
- package/build/lib/commands/emu-console.js.map +1 -1
- package/build/lib/commands/execute.d.ts +6 -5
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +77 -66
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts +7 -128
- package/build/lib/commands/file-actions.d.ts.map +1 -1
- package/build/lib/commands/file-actions.js +183 -219
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +8 -12
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +19 -23
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +9 -132
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +281 -312
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/ime.d.ts +7 -10
- package/build/lib/commands/ime.d.ts.map +1 -1
- package/build/lib/commands/ime.js +47 -35
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/index.d.ts +27 -2
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/commands/index.js +41 -19
- package/build/lib/commands/index.js.map +1 -1
- package/build/lib/commands/intent.d.ts +7 -417
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +104 -216
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +6 -5
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +16 -8
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/log.d.ts +7 -44
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +146 -108
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +7 -143
- package/build/lib/commands/media-projection.d.ts.map +1 -1
- package/build/lib/commands/media-projection.js +113 -140
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/mixins.d.ts +740 -0
- package/build/lib/commands/mixins.d.ts.map +1 -0
- package/build/lib/commands/mixins.js +19 -0
- package/build/lib/commands/mixins.js.map +1 -0
- package/build/lib/commands/network.d.ts +7 -138
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +212 -254
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/performance.d.ts +24 -70
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +144 -100
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +8 -92
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +75 -87
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +7 -193
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +151 -182
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/shell.d.ts +7 -7
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +40 -33
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +9 -103
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +261 -218
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts +22 -90
- package/build/lib/commands/system-bars.d.ts.map +1 -1
- package/build/lib/commands/system-bars.js +76 -74
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/touch.d.ts +10 -29
- package/build/lib/commands/touch.d.ts.map +1 -1
- package/build/lib/commands/touch.js +301 -285
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/commands/types.d.ts +978 -0
- package/build/lib/commands/types.d.ts.map +1 -0
- package/build/lib/commands/types.js +3 -0
- package/build/lib/commands/types.js.map +1 -0
- package/build/lib/constraints.d.ts +291 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/{desired-caps.js → constraints.js} +103 -102
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/driver.d.ts +68 -37
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +123 -80
- package/build/lib/driver.js.map +1 -1
- package/build/lib/helpers/android.d.ts +164 -0
- package/build/lib/helpers/android.d.ts.map +1 -0
- package/build/lib/helpers/android.js +819 -0
- package/build/lib/helpers/android.js.map +1 -0
- package/build/lib/helpers/index.d.ts +7 -0
- package/build/lib/helpers/index.d.ts.map +1 -0
- package/build/lib/helpers/index.js +29 -0
- package/build/lib/helpers/index.js.map +1 -0
- package/build/lib/helpers/types.d.ts +121 -0
- package/build/lib/helpers/types.d.ts.map +1 -0
- package/build/lib/helpers/types.js +3 -0
- package/build/lib/helpers/types.js.map +1 -0
- package/build/lib/helpers/unlock.d.ts +32 -0
- package/build/lib/helpers/unlock.d.ts.map +1 -0
- package/build/lib/helpers/unlock.js +273 -0
- package/build/lib/helpers/unlock.js.map +1 -0
- package/build/lib/helpers/webview.d.ts +74 -0
- package/build/lib/helpers/webview.d.ts.map +1 -0
- package/build/lib/helpers/webview.js +421 -0
- package/build/lib/helpers/webview.js.map +1 -0
- package/build/lib/index.d.ts +9 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +37 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/method-map.d.ts +0 -8
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +63 -74
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/stubs.d.ts +0 -1
- package/build/lib/stubs.d.ts.map +1 -1
- package/build/lib/stubs.js +1 -0
- package/build/lib/stubs.js.map +1 -1
- package/build/lib/utils.d.ts +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/lib/commands/actions.js +351 -464
- package/lib/commands/alert.js +27 -17
- package/lib/commands/app-management.js +156 -314
- package/lib/commands/context.js +457 -441
- package/lib/commands/element.js +201 -157
- package/lib/commands/emu-console.js +25 -45
- package/lib/commands/execute.js +106 -90
- package/lib/commands/file-actions.js +222 -240
- package/lib/commands/find.ts +103 -0
- package/lib/commands/general.js +327 -339
- package/lib/commands/ime.js +50 -34
- package/lib/commands/{index.js → index.ts} +20 -24
- package/lib/commands/intent.js +108 -249
- package/lib/commands/keyboard.js +20 -8
- package/lib/commands/log.js +172 -116
- package/lib/commands/media-projection.js +134 -161
- package/lib/commands/mixins.ts +966 -0
- package/lib/commands/network.js +252 -281
- package/lib/commands/performance.js +203 -132
- package/lib/commands/permissions.js +108 -109
- package/lib/commands/recordscreen.js +212 -209
- package/lib/commands/shell.js +51 -40
- package/lib/commands/streamscreen.js +355 -289
- package/lib/commands/system-bars.js +92 -83
- package/lib/commands/touch.js +357 -294
- package/lib/commands/types.ts +1097 -0
- package/lib/{desired-caps.js → constraints.ts} +106 -103
- package/lib/{driver.js → driver.ts} +278 -132
- package/lib/helpers/android.ts +1143 -0
- package/lib/helpers/index.ts +6 -0
- package/lib/helpers/types.ts +134 -0
- package/lib/helpers/unlock.ts +329 -0
- package/lib/helpers/webview.ts +582 -0
- package/lib/index.ts +18 -0
- package/lib/method-map.js +87 -98
- package/lib/stubs.ts +0 -1
- package/package.json +26 -19
- package/build/index.js +0 -51
- package/build/lib/android-helpers.d.ts +0 -136
- package/build/lib/android-helpers.d.ts.map +0 -1
- package/build/lib/android-helpers.js +0 -855
- package/build/lib/android-helpers.js.map +0 -1
- package/build/lib/commands/coverage.d.ts +0 -5
- package/build/lib/commands/coverage.d.ts.map +0 -1
- package/build/lib/commands/coverage.js +0 -19
- package/build/lib/commands/coverage.js.map +0 -1
- package/build/lib/desired-caps.d.ts +0 -353
- package/build/lib/desired-caps.d.ts.map +0 -1
- package/build/lib/desired-caps.js.map +0 -1
- package/build/lib/unlock-helpers.d.ts +0 -38
- package/build/lib/unlock-helpers.d.ts.map +0 -1
- package/build/lib/unlock-helpers.js +0 -266
- package/build/lib/unlock-helpers.js.map +0 -1
- package/build/lib/webview-helpers.d.ts +0 -224
- package/build/lib/webview-helpers.d.ts.map +0 -1
- package/build/lib/webview-helpers.js +0 -528
- package/build/lib/webview-helpers.js.map +0 -1
- package/index.js +0 -24
- package/lib/android-helpers.js +0 -983
- package/lib/commands/coverage.js +0 -18
- package/lib/commands/find.js +0 -82
- package/lib/unlock-helpers.js +0 -278
- package/lib/webview-helpers.js +0 -602
package/lib/commands/general.js
CHANGED
|
@@ -1,361 +1,349 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import {util} from '@appium/support';
|
|
3
|
+
import {longSleep} from 'asyncbox';
|
|
1
4
|
import _ from 'lodash';
|
|
2
|
-
import androidHelpers from '../android-helpers';
|
|
3
|
-
import { util } from '@appium/support';
|
|
4
5
|
import moment from 'moment';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
6
|
+
import androidHelpers from '../helpers/android';
|
|
7
|
+
import {requireArgs} from '../utils';
|
|
8
|
+
import {mixin} from './mixins';
|
|
7
9
|
|
|
8
10
|
const MOMENT_FORMAT_ISO8601 = 'YYYY-MM-DDTHH:mm:ssZ';
|
|
9
11
|
|
|
10
|
-
let commands = {}, helpers = {}, extensions = {};
|
|
11
|
-
|
|
12
|
-
commands.keys = async function keys (keys) {
|
|
13
|
-
// Protocol sends an array; rethink approach
|
|
14
|
-
keys = _.isArray(keys) ? keys.join('') : keys;
|
|
15
|
-
let params = {
|
|
16
|
-
text: keys,
|
|
17
|
-
replace: false
|
|
18
|
-
};
|
|
19
|
-
if (this.opts.unicodeKeyboard) {
|
|
20
|
-
params.unicodeKeyboard = true;
|
|
21
|
-
}
|
|
22
|
-
await this.doSendKeys(params);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
commands.doSendKeys = async function doSendKeys (params) {
|
|
26
|
-
return await this.bootstrap.sendAction('setText', params);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Retrieves the current device's timestamp.
|
|
31
|
-
*
|
|
32
|
-
* @param {string} format - The set of format specifiers. Read
|
|
33
|
-
* https://momentjs.com/docs/ to get the full list of supported
|
|
34
|
-
* datetime format specifiers. The default format is
|
|
35
|
-
* `YYYY-MM-DDTHH:mm:ssZ`, which complies to ISO-8601
|
|
36
|
-
* @return {string} Formatted datetime string or the raw command output if formatting fails
|
|
37
|
-
*/
|
|
38
|
-
commands.getDeviceTime = async function getDeviceTime (format = MOMENT_FORMAT_ISO8601) {
|
|
39
|
-
this.log.debug('Attempting to capture android device date and time. ' +
|
|
40
|
-
`The format specifier is '${format}'`);
|
|
41
|
-
const deviceTimestamp = (await this.adb.shell(['date', '+%Y-%m-%dT%T%z'])).trim();
|
|
42
|
-
this.log.debug(`Got device timestamp: ${deviceTimestamp}`);
|
|
43
|
-
const parsedTimestamp = moment.utc(deviceTimestamp, 'YYYY-MM-DDTHH:mm:ssZZ');
|
|
44
|
-
if (!parsedTimestamp.isValid()) {
|
|
45
|
-
this.log.warn('Cannot parse the returned timestamp. Returning as is');
|
|
46
|
-
return deviceTimestamp;
|
|
47
|
-
}
|
|
48
|
-
return parsedTimestamp.utcOffset(parsedTimestamp._tzm || 0).format(format);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {Object} DeviceTimeOptions
|
|
53
|
-
* @property {string} format [YYYY-MM-DDTHH:mm:ssZ] - See getDeviceTime#format
|
|
54
|
-
*/
|
|
55
|
-
|
|
56
12
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* @param {DeviceTimeOptions} opts
|
|
60
|
-
* @return {string} Formatted datetime string or the raw command output if formatting fails
|
|
13
|
+
* @type {import('./mixins').GeneralMixin & ThisType<import('../driver').AndroidDriver>}
|
|
14
|
+
* @satisfies {import('@appium/types').ExternalDriver}
|
|
61
15
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
16
|
+
const GeneralMixin = {
|
|
17
|
+
_cachedActivityArgs: {},
|
|
18
|
+
async keys(keys) {
|
|
19
|
+
// Protocol sends an array; rethink approach
|
|
20
|
+
keys = _.isArray(keys) ? keys.join('') : keys;
|
|
21
|
+
/**
|
|
22
|
+
* @type {import('./types').SendKeysOpts}
|
|
23
|
+
*/
|
|
24
|
+
const params = {
|
|
25
|
+
text: keys,
|
|
26
|
+
replace: false,
|
|
27
|
+
};
|
|
28
|
+
if (this.opts.unicodeKeyboard) {
|
|
29
|
+
params.unicodeKeyboard = true;
|
|
30
|
+
}
|
|
31
|
+
await this.doSendKeys(params);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async doSendKeys(params) {
|
|
35
|
+
return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('setText', params);
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async getDeviceTime(format = MOMENT_FORMAT_ISO8601) {
|
|
39
|
+
this.log.debug(
|
|
40
|
+
'Attempting to capture android device date and time. ' + `The format specifier is '${format}'`
|
|
41
|
+
);
|
|
42
|
+
const deviceTimestamp = (
|
|
43
|
+
await /** @type {ADB} */ (this.adb).shell(['date', '+%Y-%m-%dT%T%z'])
|
|
44
|
+
).trim();
|
|
45
|
+
this.log.debug(`Got device timestamp: ${deviceTimestamp}`);
|
|
46
|
+
const parsedTimestamp = moment.utc(deviceTimestamp, 'YYYY-MM-DDTHH:mm:ssZZ');
|
|
47
|
+
if (!parsedTimestamp.isValid()) {
|
|
48
|
+
this.log.warn('Cannot parse the returned timestamp. Returning as is');
|
|
49
|
+
return deviceTimestamp;
|
|
50
|
+
}
|
|
51
|
+
// @ts-expect-error private API
|
|
52
|
+
return parsedTimestamp.utcOffset(parsedTimestamp._tzm || 0).format(format);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
async mobileGetDeviceTime(opts = {}) {
|
|
56
|
+
return await this.getDeviceTime(opts.format);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
async getPageSource() {
|
|
60
|
+
return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('source');
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async back() {
|
|
64
|
+
return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('pressBack');
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async openSettingsActivity(setting) {
|
|
68
|
+
const adb = /** @type {ADB} */ (this.adb);
|
|
69
|
+
let {appPackage, appActivity} = await adb.getFocusedPackageAndActivity();
|
|
70
|
+
await adb.shell(['am', 'start', '-a', `android.settings.${setting}`]);
|
|
71
|
+
await adb.waitForNotActivity(/** @type {string} */ (appPackage), appActivity, 5000);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async getWindowSize() {
|
|
75
|
+
return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('getDeviceSize');
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// For W3C
|
|
79
|
+
async getWindowRect() {
|
|
80
|
+
const {width, height} = await this.getWindowSize();
|
|
81
|
+
return {
|
|
82
|
+
width,
|
|
83
|
+
height,
|
|
84
|
+
x: 0,
|
|
85
|
+
y: 0,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async getCurrentActivity() {
|
|
90
|
+
return (await /** @type {ADB} */ (this.adb).getFocusedPackageAndActivity()).appActivity;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async getCurrentPackage() {
|
|
94
|
+
return (await /** @type {ADB} */ (this.adb).getFocusedPackageAndActivity()).appPackage;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
async background(seconds) {
|
|
98
|
+
const adb = /** @type {ADB} */ (this.adb);
|
|
99
|
+
if (seconds < 0) {
|
|
100
|
+
// if user passes in a negative seconds value, interpret that as the instruction
|
|
101
|
+
// to not bring the app back at all
|
|
102
|
+
await adb.goToHome();
|
|
134
103
|
return true;
|
|
135
|
-
} catch (ign) {}
|
|
136
|
-
args = ((appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
|
|
137
|
-
(appPackage === this.opts.appWaitPackage && (this.opts.appWaitActivity || '').split(',').includes(appActivity)))
|
|
138
|
-
? {// the activity is the original session activity, so use the original args
|
|
139
|
-
pkg: this.opts.appPackage,
|
|
140
|
-
activity: this.opts.appActivity,
|
|
141
|
-
action: this.opts.intentAction,
|
|
142
|
-
category: this.opts.intentCategory,
|
|
143
|
-
flags: this.opts.intentFlags,
|
|
144
|
-
waitPkg: this.opts.appWaitPackage,
|
|
145
|
-
waitActivity: this.opts.appWaitActivity,
|
|
146
|
-
waitForLaunch: this.opts.appWaitForLaunch,
|
|
147
|
-
waitDuration: this.opts.appWaitDuration,
|
|
148
|
-
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
149
|
-
stopApp: false,
|
|
150
|
-
user: this.opts.userProfile}
|
|
151
|
-
: {// the activity was started some other way, so use defaults
|
|
152
|
-
pkg: appPackage,
|
|
153
|
-
activity: appActivity,
|
|
154
|
-
waitPkg: appPackage,
|
|
155
|
-
waitActivity: appActivity,
|
|
156
|
-
stopApp: false};
|
|
157
|
-
}
|
|
158
|
-
args = await util.filterObject(args);
|
|
159
|
-
this.log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
|
|
160
|
-
return await this.adb.startApp(args);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
commands.getStrings = async function getStrings (language) {
|
|
164
|
-
if (!language) {
|
|
165
|
-
language = await this.adb.getDeviceLanguage();
|
|
166
|
-
this.log.info(`No language specified, returning strings for: ${language}`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Clients require the resulting mapping to have both keys
|
|
170
|
-
// and values of type string
|
|
171
|
-
const preprocessStringsMap = (mapping) => {
|
|
172
|
-
const result = {};
|
|
173
|
-
for (const [key, value] of _.toPairs(mapping)) {
|
|
174
|
-
result[key] = _.isString(value) ? value : JSON.stringify(value);
|
|
175
104
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
105
|
+
let {appPackage, appActivity} = await adb.getFocusedPackageAndActivity();
|
|
106
|
+
await adb.goToHome();
|
|
107
|
+
|
|
108
|
+
// people can wait for a long time, so to be safe let's use the longSleep function and log
|
|
109
|
+
// progress periodically.
|
|
110
|
+
const sleepMs = seconds * 1000;
|
|
111
|
+
const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
|
|
112
|
+
// for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
|
|
113
|
+
const intervalMs = _.min([30 * 1000, parseInt(String(sleepMs / 100), 10)]);
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {{elapsedMs: number, progress: number}} param0
|
|
117
|
+
*/
|
|
118
|
+
const progressCb = ({elapsedMs, progress}) => {
|
|
119
|
+
const waitSecs = (elapsedMs / 1000).toFixed(0);
|
|
120
|
+
const progressPct = (progress * 100).toFixed(2);
|
|
121
|
+
this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
|
|
122
|
+
};
|
|
123
|
+
await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
|
|
124
|
+
|
|
125
|
+
/** @type {import('appium-adb').StartAppOptions} */
|
|
126
|
+
let args;
|
|
127
|
+
if (this._cachedActivityArgs && this._cachedActivityArgs[`${appPackage}/${appActivity}`]) {
|
|
128
|
+
// the activity was started with `startActivity`, so use those args to restart
|
|
129
|
+
args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
|
|
130
|
+
} else {
|
|
131
|
+
try {
|
|
132
|
+
this.log.debug(`Activating app '${appPackage}' in order to restore it`);
|
|
133
|
+
await this.activateApp(/** @type {string} */ (appPackage));
|
|
134
|
+
return true;
|
|
135
|
+
} catch (ign) {}
|
|
136
|
+
args =
|
|
137
|
+
(appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
|
|
138
|
+
(appPackage === this.opts.appWaitPackage &&
|
|
139
|
+
(this.opts.appWaitActivity || '').split(',').includes(String(appActivity)))
|
|
140
|
+
? {
|
|
141
|
+
// the activity is the original session activity, so use the original args
|
|
142
|
+
pkg: /** @type {string} */ (this.opts.appPackage),
|
|
143
|
+
activity: this.opts.appActivity,
|
|
144
|
+
action: this.opts.intentAction,
|
|
145
|
+
category: this.opts.intentCategory,
|
|
146
|
+
flags: this.opts.intentFlags,
|
|
147
|
+
waitPkg: this.opts.appWaitPackage,
|
|
148
|
+
waitActivity: this.opts.appWaitActivity,
|
|
149
|
+
waitForLaunch: this.opts.appWaitForLaunch,
|
|
150
|
+
waitDuration: this.opts.appWaitDuration,
|
|
151
|
+
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
152
|
+
stopApp: false,
|
|
153
|
+
user: this.opts.userProfile,
|
|
154
|
+
}
|
|
155
|
+
: {
|
|
156
|
+
// the activity was started some other way, so use defaults
|
|
157
|
+
pkg: /** @type {string} */ (appPackage),
|
|
158
|
+
activity: appActivity,
|
|
159
|
+
waitPkg: appPackage,
|
|
160
|
+
waitActivity: appActivity,
|
|
161
|
+
stopApp: false,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
args = /** @type {import('appium-adb').StartAppOptions} */ (
|
|
165
|
+
_.pickBy(args, (value) => !_.isUndefined(value))
|
|
166
|
+
);
|
|
167
|
+
this.log.debug(
|
|
168
|
+
`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`
|
|
169
|
+
);
|
|
170
|
+
return await adb.startApp(args);
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
async getStrings(language) {
|
|
174
|
+
const adb = /** @type {ADB} */ (this.adb);
|
|
175
|
+
if (!language) {
|
|
176
|
+
language = await adb.getDeviceLanguage();
|
|
177
|
+
this.log.info(`No language specified, returning strings for: ${language}`);
|
|
178
|
+
}
|
|
189
179
|
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
// Clients require the resulting mapping to have both keys
|
|
181
|
+
// and values of type string
|
|
182
|
+
/** @param {StringRecord} mapping */
|
|
183
|
+
const preprocessStringsMap = (mapping) => {
|
|
184
|
+
/** @type {StringRecord} */
|
|
185
|
+
const result = {};
|
|
186
|
+
for (const [key, value] of _.toPairs(mapping)) {
|
|
187
|
+
result[key] = _.isString(value) ? value : JSON.stringify(value);
|
|
188
|
+
}
|
|
189
|
+
return result;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (this.apkStrings[language]) {
|
|
193
|
+
// Return cached strings
|
|
194
|
+
return preprocessStringsMap(this.apkStrings[language]);
|
|
195
|
+
}
|
|
192
196
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
this.apkStrings[language] = await androidHelpers.pushStrings(language, adb, this.opts);
|
|
198
|
+
if (this.bootstrap) {
|
|
199
|
+
// TODO: This is mutating the current language, but it's how appium currently works
|
|
200
|
+
await this.bootstrap.sendAction('updateStrings');
|
|
201
|
+
}
|
|
197
202
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
action: intentAction,
|
|
215
|
-
category: intentCategory,
|
|
216
|
-
flags: intentFlags,
|
|
203
|
+
return preprocessStringsMap(this.apkStrings[language]);
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
async launchApp() {
|
|
207
|
+
await this.initAUT();
|
|
208
|
+
await this.startAUT();
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async startActivity(
|
|
212
|
+
appPackage,
|
|
213
|
+
appActivity,
|
|
214
|
+
appWaitPackage,
|
|
215
|
+
appWaitActivity,
|
|
216
|
+
intentAction,
|
|
217
|
+
intentCategory,
|
|
218
|
+
intentFlags,
|
|
217
219
|
optionalIntentArguments,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// reset context since we don't know what kind on context we will end up after app launch.
|
|
228
|
-
await this.setContext();
|
|
229
|
-
return await this.isChromeSession ? this.startChromeSession() : this.startAUT();
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
commands.startAUT = async function startAUT () {
|
|
233
|
-
await this.adb.startApp({
|
|
234
|
-
pkg: this.opts.appPackage,
|
|
235
|
-
activity: this.opts.appActivity,
|
|
236
|
-
action: this.opts.intentAction,
|
|
237
|
-
category: this.opts.intentCategory,
|
|
238
|
-
flags: this.opts.intentFlags,
|
|
239
|
-
waitPkg: this.opts.appWaitPackage,
|
|
240
|
-
waitActivity: this.opts.appWaitActivity,
|
|
241
|
-
waitForLaunch: this.opts.appWaitForLaunch,
|
|
242
|
-
waitDuration: this.opts.appWaitDuration,
|
|
243
|
-
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
244
|
-
stopApp: !this.opts.dontStopAppOnReset,
|
|
245
|
-
user: this.opts.userProfile,
|
|
246
|
-
});
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// we override setUrl to take an android URI which can be used for deep-linking
|
|
250
|
-
// inside an app, similar to starting an intent
|
|
251
|
-
commands.setUrl = async function setUrl (uri) {
|
|
252
|
-
await this.adb.startUri(uri, this.opts.appPackage);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
// closing app using force stop
|
|
256
|
-
commands.closeApp = async function closeApp () {
|
|
257
|
-
await this.adb.forceStop(this.opts.appPackage);
|
|
258
|
-
// reset context since we don't know what kind on context we will end up after app launch.
|
|
259
|
-
await this.setContext();
|
|
260
|
-
};
|
|
220
|
+
dontStopAppOnReset
|
|
221
|
+
) {
|
|
222
|
+
this.log.debug(`Starting package '${appPackage}' and activity '${appActivity}'`);
|
|
223
|
+
|
|
224
|
+
// dontStopAppOnReset is both an argument here, and a desired capability
|
|
225
|
+
// if the argument is set, use it, otherwise use the cap
|
|
226
|
+
if (!util.hasValue(dontStopAppOnReset)) {
|
|
227
|
+
dontStopAppOnReset = !!this.opts.dontStopAppOnReset;
|
|
228
|
+
}
|
|
261
229
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
230
|
+
/** @type {import('appium-adb').StartAppOptions} */
|
|
231
|
+
let args = {
|
|
232
|
+
pkg: appPackage,
|
|
233
|
+
activity: appActivity,
|
|
234
|
+
waitPkg: appWaitPackage || appPackage,
|
|
235
|
+
waitActivity: appWaitActivity || appActivity,
|
|
236
|
+
action: intentAction,
|
|
237
|
+
category: intentCategory,
|
|
238
|
+
flags: intentFlags,
|
|
239
|
+
optionalIntentArguments,
|
|
240
|
+
stopApp: !dontStopAppOnReset,
|
|
241
|
+
};
|
|
242
|
+
this._cachedActivityArgs = this._cachedActivityArgs || {};
|
|
243
|
+
this._cachedActivityArgs[`${args.waitPkg}/${args.waitActivity}`] = args;
|
|
244
|
+
await /** @type {ADB} */ (this.adb).startApp(args);
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
async reset() {
|
|
248
|
+
await androidHelpers.resetApp(
|
|
249
|
+
/** @type {ADB} */ (this.adb),
|
|
250
|
+
Object.assign({}, this.opts, {fastReset: true})
|
|
251
|
+
);
|
|
252
|
+
// reset context since we don't know what kind on context we will end up after app launch.
|
|
253
|
+
await this.setContext();
|
|
254
|
+
return this.isChromeSession ? this.startChromeSession() : this.startAUT();
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
async startAUT() {
|
|
258
|
+
await /** @type {ADB} */ (this.adb).startApp({
|
|
259
|
+
pkg: /** @type {string} */ (this.opts.appPackage),
|
|
260
|
+
activity: this.opts.appActivity,
|
|
261
|
+
action: this.opts.intentAction,
|
|
262
|
+
category: this.opts.intentCategory,
|
|
263
|
+
flags: this.opts.intentFlags,
|
|
264
|
+
waitPkg: this.opts.appWaitPackage,
|
|
265
|
+
waitActivity: this.opts.appWaitActivity,
|
|
266
|
+
waitForLaunch: this.opts.appWaitForLaunch,
|
|
267
|
+
waitDuration: this.opts.appWaitDuration,
|
|
268
|
+
optionalIntentArguments: this.opts.optionalIntentArguments,
|
|
269
|
+
stopApp: !this.opts.dontStopAppOnReset,
|
|
270
|
+
user: this.opts.userProfile,
|
|
271
|
+
});
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// we override setUrl to take an android URI which can be used for deep-linking
|
|
275
|
+
// inside an app, similar to starting an intent
|
|
276
|
+
async setUrl(uri) {
|
|
277
|
+
await /** @type {ADB} */ (this.adb).startUri(uri, /** @type {string} */ (this.opts.appPackage));
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// closing app using force stop
|
|
281
|
+
async closeApp() {
|
|
282
|
+
await /** @type {ADB} */ (this.adb).forceStop(/** @type {string} */ (this.opts.appPackage));
|
|
283
|
+
// reset context since we don't know what kind on context we will end up after app launch.
|
|
284
|
+
await this.setContext();
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
async getDisplayDensity() {
|
|
288
|
+
// first try the property for devices
|
|
289
|
+
let out = await /** @type {ADB} */ (this.adb).shell(['getprop', 'ro.sf.lcd_density']);
|
|
290
|
+
if (out) {
|
|
291
|
+
let val = parseInt(out, 10);
|
|
292
|
+
// if the value is NaN, try getting the emulator property
|
|
293
|
+
if (!isNaN(val)) {
|
|
294
|
+
return val;
|
|
295
|
+
}
|
|
296
|
+
this.log.debug(`Parsed density value was NaN: "${out}"`);
|
|
270
297
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
298
|
+
// fallback to trying property for emulators
|
|
299
|
+
out = await /** @type {ADB} */ (this.adb).shell(['getprop', 'qemu.sf.lcd_density']);
|
|
300
|
+
if (out) {
|
|
301
|
+
let val = parseInt(out, 10);
|
|
302
|
+
if (!isNaN(val)) {
|
|
303
|
+
return val;
|
|
304
|
+
}
|
|
305
|
+
this.log.debug(`Parsed density value was NaN: "${out}"`);
|
|
279
306
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
307
|
+
// couldn't get anything, so error out
|
|
308
|
+
this.log.errorAndThrow('Failed to get display density property.');
|
|
309
|
+
throw new Error(); // unreachable
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
async mobilePerformEditorAction(opts) {
|
|
313
|
+
const {action} = requireArgs('action', opts);
|
|
314
|
+
await /** @type {ADB} */ (this.adb).performEditorAction(action);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
async mobileGetNotifications() {
|
|
318
|
+
return await /** @type {ADB} */ (this.adb).getNotifications();
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
async mobileListSms(opts) {
|
|
322
|
+
return await /** @type {ADB} */ (this.adb).getSmsList(opts);
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
async mobileUnlock(opts = {}) {
|
|
326
|
+
const {key, type, strategy, timeoutMs} = opts;
|
|
327
|
+
if (!key && !type) {
|
|
328
|
+
await this.unlock();
|
|
329
|
+
} else {
|
|
330
|
+
// @ts-expect-error XXX: these caps should be defined in the constraints!!
|
|
331
|
+
await androidHelpers.unlock(this, /** @type {ADB} */ (this.adb), {
|
|
332
|
+
unlockKey: key,
|
|
333
|
+
unlockType: type,
|
|
334
|
+
unlockStrategy: strategy,
|
|
335
|
+
unlockSuccessTimeout: timeoutMs,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
},
|
|
293
339
|
};
|
|
294
340
|
|
|
295
|
-
|
|
296
|
-
* Retrieves the list of recent system notifications.
|
|
297
|
-
*
|
|
298
|
-
* @returns {Object} See the documentation on `adb.getNotifications` for
|
|
299
|
-
* more details
|
|
300
|
-
*/
|
|
301
|
-
commands.mobileGetNotifications = async function mobileGetNotifications () {
|
|
302
|
-
return await this.adb.getNotifications();
|
|
303
|
-
};
|
|
341
|
+
mixin(GeneralMixin);
|
|
304
342
|
|
|
305
|
-
|
|
306
|
-
* @typedef {Object} SmsListOptions
|
|
307
|
-
* @property {number} max [100] - The maximum count of recent SMS messages
|
|
308
|
-
* to retrieve
|
|
309
|
-
*/
|
|
343
|
+
export default GeneralMixin;
|
|
310
344
|
|
|
311
345
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* @
|
|
315
|
-
* @returns {Object} See the documentation on `adb.getSmsList` for
|
|
316
|
-
* more details
|
|
346
|
+
* @typedef {import('../bootstrap').AndroidBootstrap} AndroidBootstrap
|
|
347
|
+
* @typedef {import('appium-adb').ADB} ADB
|
|
348
|
+
* @typedef {import('@appium/types').StringRecord} StringRecord
|
|
317
349
|
*/
|
|
318
|
-
commands.mobileListSms = async function mobileListSms (opts = {}) {
|
|
319
|
-
return await this.adb.getSmsList(opts);
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* @typedef {Object} UnlockOptions
|
|
324
|
-
* @property {string?} key The unlock key. The value of this key depends
|
|
325
|
-
* on the actual unlock type and could be a pin/password/pattern value or
|
|
326
|
-
* a biometric finger id.
|
|
327
|
-
* If not provided then the corresponding value from session capabilities is used.
|
|
328
|
-
* @property {string?} type The unlock type. The following unlock types
|
|
329
|
-
* are supported: `pin`, `pinWithKeyEvent`, `password`, `pattern` and `fingerprint`.
|
|
330
|
-
* If not provided then the corresponding value from session capabilities is used.
|
|
331
|
-
* @property {string?} strategy Either 'locksettings' (default) or 'uiautomator'.
|
|
332
|
-
* Setting it to 'uiautomator' will enforce the driver to avoid using special
|
|
333
|
-
* ADB shortcuts in order to speed up the unlock procedure.
|
|
334
|
-
* @property {number?} timeoutMs [2000] The maximum time in milliseconds
|
|
335
|
-
* to wait until the screen gets unlocked
|
|
336
|
-
*/
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Unlocks the device if it is locked. Noop if the device's screen is not locked.
|
|
340
|
-
*
|
|
341
|
-
* @param {UnlockOptions} opts
|
|
342
|
-
* @throws {Error} if unlock operation fails or the provided
|
|
343
|
-
* arguments are not valid
|
|
344
|
-
*/
|
|
345
|
-
commands.mobileUnlock = async function mobileUnlock (opts = {}) {
|
|
346
|
-
const { key, type, strategy, timeoutMs } = opts;
|
|
347
|
-
if (!key && !type) {
|
|
348
|
-
await this.unlock();
|
|
349
|
-
} else {
|
|
350
|
-
await androidHelpers.unlock(this, this.adb, {
|
|
351
|
-
unlockKey: key,
|
|
352
|
-
unlockType: type,
|
|
353
|
-
unlockStrategy: strategy,
|
|
354
|
-
unlockSuccessTimeout: timeoutMs,
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
Object.assign(extensions, commands, helpers);
|
|
360
|
-
export { commands, helpers };
|
|
361
|
-
export default extensions;
|