appium-uiautomator2-driver 2.29.11 → 2.31.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 +14 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +8 -15
- package/build/index.js.map +1 -0
- package/build/lib/commands/actions.d.ts +2 -0
- package/build/lib/commands/actions.d.ts.map +1 -0
- package/build/lib/commands/actions.js +67 -62
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/alert.d.ts +2 -0
- package/build/lib/commands/alert.d.ts.map +1 -0
- package/build/lib/commands/alert.js +28 -26
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-strings.d.ts +3 -0
- package/build/lib/commands/app-strings.d.ts.map +1 -0
- package/build/lib/commands/app-strings.js +86 -57
- package/build/lib/commands/app-strings.js.map +1 -1
- package/build/lib/commands/battery.d.ts +2 -0
- package/build/lib/commands/battery.d.ts.map +1 -0
- package/build/lib/commands/battery.js +26 -16
- package/build/lib/commands/battery.js.map +1 -1
- package/build/lib/commands/element.d.ts +2 -0
- package/build/lib/commands/element.d.ts.map +1 -0
- package/build/lib/commands/element.js +140 -159
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/find.d.ts +2 -0
- package/build/lib/commands/find.d.ts.map +1 -0
- package/build/lib/commands/find.js +39 -25
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +4 -0
- package/build/lib/commands/general.d.ts.map +1 -0
- package/build/lib/commands/general.js +209 -215
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/gestures.d.ts +2 -0
- package/build/lib/commands/gestures.d.ts.map +1 -0
- package/build/lib/commands/gestures.js +206 -193
- package/build/lib/commands/gestures.js.map +1 -1
- package/build/lib/commands/index.d.ts +2 -0
- package/build/lib/commands/index.d.ts.map +1 -0
- package/build/lib/commands/index.js +13 -22
- package/build/lib/commands/index.js.map +1 -1
- package/build/lib/commands/mixins.d.ts +87 -0
- package/build/lib/commands/mixins.d.ts.map +1 -0
- package/build/lib/commands/mixins.js +26 -0
- package/build/lib/commands/mixins.js.map +1 -0
- package/build/lib/commands/screenshot.d.ts +2 -0
- package/build/lib/commands/screenshot.d.ts.map +1 -0
- package/build/lib/commands/screenshot.js +77 -62
- package/build/lib/commands/screenshot.js.map +1 -1
- package/build/lib/commands/touch.d.ts +2 -0
- package/build/lib/commands/touch.d.ts.map +1 -0
- package/build/lib/commands/touch.js +48 -38
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/commands/types.d.ts +452 -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/commands/viewport.d.ts +2 -0
- package/build/lib/commands/viewport.d.ts.map +1 -0
- package/build/lib/commands/viewport.js +37 -35
- package/build/lib/commands/viewport.js.map +1 -1
- package/build/lib/constraints.d.ts +325 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/constraints.js +51 -0
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/css-converter.d.ts +45 -0
- package/build/lib/css-converter.d.ts.map +1 -0
- package/build/lib/css-converter.js +272 -175
- package/build/lib/css-converter.js.map +1 -1
- package/build/lib/driver.d.ts +904 -0
- package/build/lib/driver.d.ts.map +1 -0
- package/build/lib/driver.js +726 -485
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts +477 -0
- package/build/lib/execute-method-map.d.ts.map +1 -0
- package/build/lib/execute-method-map.js +542 -0
- package/build/lib/execute-method-map.js.map +1 -0
- package/build/lib/extensions.d.ts +3 -0
- package/build/lib/extensions.d.ts.map +1 -0
- package/build/lib/extensions.js +7 -9
- package/build/lib/extensions.js.map +1 -1
- package/build/lib/helpers.d.ts +7 -0
- package/build/lib/helpers.d.ts.map +1 -0
- package/build/lib/helpers.js +36 -29
- package/build/lib/helpers.js.map +1 -1
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +5 -10
- package/build/lib/logger.js.map +1 -1
- package/build/lib/method-map.d.ts +389 -0
- package/build/lib/method-map.d.ts.map +1 -0
- package/build/lib/method-map.js +11 -17
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/types.d.ts +44 -0
- package/build/lib/types.d.ts.map +1 -0
- package/build/lib/types.js +3 -0
- package/build/lib/types.js.map +1 -0
- package/build/lib/uiautomator2.d.ts +45 -0
- package/build/lib/uiautomator2.d.ts.map +1 -0
- package/build/lib/uiautomator2.js +340 -299
- package/build/lib/uiautomator2.js.map +1 -1
- package/build/lib/utils.d.ts +10 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +23 -16
- package/build/lib/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -0
- package/index.js +5 -3
- package/lib/commands/actions.js +115 -101
- package/lib/commands/alert.js +36 -44
- package/lib/commands/app-strings.js +79 -58
- package/lib/commands/battery.js +27 -28
- package/lib/commands/element.js +231 -134
- package/lib/commands/find.js +40 -21
- package/lib/commands/general.js +262 -336
- package/lib/commands/gestures.js +252 -366
- package/lib/commands/index.js +11 -31
- package/lib/commands/mixins.ts +169 -0
- package/lib/commands/screenshot.js +80 -76
- package/lib/commands/touch.js +64 -31
- package/lib/commands/types.ts +473 -0
- package/lib/commands/viewport.js +43 -31
- package/lib/constraints.ts +53 -0
- package/lib/css-converter.js +9 -1
- package/lib/{driver.js → driver.ts} +374 -239
- package/lib/execute-method-map.ts +573 -0
- package/lib/method-map.ts +11 -0
- package/lib/types.ts +57 -0
- package/lib/uiautomator2.js +21 -2
- package/lib/utils.js +2 -2
- package/npm-shrinkwrap.json +395 -528
- package/package.json +96 -70
- package/build/lib/desired-caps.js +0 -71
- package/build/lib/desired-caps.js.map +0 -1
- package/lib/desired-caps.js +0 -70
- package/lib/method-map.js +0 -11
package/lib/commands/general.js
CHANGED
|
@@ -1,356 +1,282 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import _ from 'lodash';
|
|
2
4
|
import B from 'bluebird';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
commands = {},
|
|
8
|
-
helpers = {};
|
|
9
|
-
|
|
10
|
-
commands.getPageSource = async function () {
|
|
11
|
-
return await this.uiautomator2.jwproxy.command('/source', 'GET', {});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
commands.getClipboard = async function () {
|
|
15
|
-
return (await this.adb.getApiLevel() < 29)
|
|
16
|
-
? (await this.uiautomator2.jwproxy.command('/appium/device/get_clipboard', 'POST', {}))
|
|
17
|
-
: (await this.adb.getClipboard());
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Need to override this for correct unicode support
|
|
21
|
-
commands.doSendKeys = async function (params) {
|
|
22
|
-
await this.uiautomator2.jwproxy.command('/keys', 'POST', params);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// uiautomator2 doesn't support metastate for keyevents
|
|
26
|
-
commands.keyevent = async function (keycode, metastate) {
|
|
27
|
-
this.log.debug(`Ignoring metastate ${metastate}`);
|
|
28
|
-
await this.adb.keyevent(keycode);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Use ADB since we don't have UiAutomator
|
|
32
|
-
commands.back = async function () {
|
|
33
|
-
await this.adb.keyevent(4);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
commands.getDisplayDensity = async function getDisplayDensity () {
|
|
37
|
-
return await this.uiautomator2.jwproxy.command('/appium/device/display_density', 'GET', {});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// memoized in constructor
|
|
41
|
-
commands.getWindowSize = async function () {
|
|
42
|
-
return await this.uiautomator2.jwproxy.command('/window/current/size', 'GET', {});
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// For W3C
|
|
46
|
-
commands.getWindowRect = async function () {
|
|
47
|
-
const {width, height} = await this.getWindowSize();
|
|
48
|
-
return {
|
|
49
|
-
width,
|
|
50
|
-
height,
|
|
51
|
-
x: 0,
|
|
52
|
-
y: 0,
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
extensions.executeMobile = async function (mobileCommand, opts = {}) {
|
|
57
|
-
const mobileCommandsMapping = {
|
|
58
|
-
shell: 'mobileShell',
|
|
59
|
-
|
|
60
|
-
execEmuConsoleCommand: 'mobileExecEmuConsoleCommand',
|
|
61
|
-
|
|
62
|
-
dragGesture: 'mobileDragGesture',
|
|
63
|
-
flingGesture: 'mobileFlingGesture',
|
|
64
|
-
doubleClickGesture: 'mobileDoubleClickGesture',
|
|
65
|
-
clickGesture: 'mobileClickGesture',
|
|
66
|
-
longClickGesture: 'mobileLongClickGesture',
|
|
67
|
-
pinchCloseGesture: 'mobilePinchCloseGesture',
|
|
68
|
-
pinchOpenGesture: 'mobilePinchOpenGesture',
|
|
69
|
-
swipeGesture: 'mobileSwipeGesture',
|
|
70
|
-
scrollGesture: 'mobileScrollGesture',
|
|
71
|
-
scrollBackTo: 'mobileScrollBackTo',
|
|
72
|
-
scroll: 'mobileScroll',
|
|
73
|
-
viewportScreenshot: 'mobileViewportScreenshot',
|
|
74
|
-
viewportRect: 'mobileViewPortRect',
|
|
75
|
-
|
|
76
|
-
deepLink: 'mobileDeepLink',
|
|
77
|
-
|
|
78
|
-
startLogsBroadcast: 'mobileStartLogsBroadcast',
|
|
79
|
-
stopLogsBroadcast: 'mobileStopLogsBroadcast',
|
|
80
|
-
|
|
81
|
-
acceptAlert: 'mobileAcceptAlert',
|
|
82
|
-
dismissAlert: 'mobileDismissAlert',
|
|
83
|
-
|
|
84
|
-
batteryInfo: 'mobileGetBatteryInfo',
|
|
85
|
-
|
|
86
|
-
deviceInfo: 'mobileGetDeviceInfo',
|
|
87
|
-
|
|
88
|
-
getDeviceTime: 'mobileGetDeviceTime',
|
|
89
|
-
|
|
90
|
-
changePermissions: 'mobileChangePermissions',
|
|
91
|
-
getPermissions: 'mobileGetPermissions',
|
|
92
|
-
|
|
93
|
-
performEditorAction: 'mobilePerformEditorAction',
|
|
94
|
-
|
|
95
|
-
startScreenStreaming: 'mobileStartScreenStreaming',
|
|
96
|
-
stopScreenStreaming: 'mobileStopScreenStreaming',
|
|
97
|
-
|
|
98
|
-
getNotifications: 'mobileGetNotifications',
|
|
99
|
-
openNotifications: 'openNotifications',
|
|
100
|
-
|
|
101
|
-
listSms: 'mobileListSms',
|
|
102
|
-
|
|
103
|
-
type: 'mobileType',
|
|
104
|
-
replaceElementValue: 'mobileReplaceElementValue',
|
|
105
|
-
|
|
106
|
-
pushFile: 'mobilePushFile',
|
|
107
|
-
pullFile: 'mobilePullFile',
|
|
108
|
-
pullFolder: 'mobilePullFolder',
|
|
109
|
-
deleteFile: 'mobileDeleteFile',
|
|
110
|
-
|
|
111
|
-
isAppInstalled: 'mobileIsAppInstalled',
|
|
112
|
-
queryAppState: 'mobileQueryAppState',
|
|
113
|
-
activateApp: 'mobileActivateApp',
|
|
114
|
-
removeApp: 'mobileRemoveApp',
|
|
115
|
-
terminateApp: 'mobileTerminateApp',
|
|
116
|
-
installApp: 'mobileInstallApp',
|
|
117
|
-
clearApp: 'mobileClearApp',
|
|
118
|
-
backgroundApp: 'mobileBackgroundApp',
|
|
119
|
-
getCurrentActivity: 'getCurrentActivity',
|
|
120
|
-
getCurrentPackage: 'getCurrentPackage',
|
|
121
|
-
|
|
122
|
-
startActivity: 'mobileStartActivity',
|
|
123
|
-
startService: 'mobileStartService',
|
|
124
|
-
stopService: 'mobileStopService',
|
|
125
|
-
broadcast: 'mobileBroadcast',
|
|
126
|
-
|
|
127
|
-
getContexts: 'mobileGetContexts',
|
|
128
|
-
|
|
129
|
-
getAppStrings: 'mobileGetAppStrings',
|
|
130
|
-
|
|
131
|
-
installMultipleApks: 'mobileInstallMultipleApks',
|
|
132
|
-
|
|
133
|
-
lock: 'mobileLock',
|
|
134
|
-
unlock: 'mobileUnlock',
|
|
135
|
-
isLocked: 'isLocked',
|
|
136
|
-
|
|
137
|
-
refreshGpsCache: 'mobileRefreshGpsCache',
|
|
138
|
-
|
|
139
|
-
startMediaProjectionRecording: 'mobileStartMediaProjectionRecording',
|
|
140
|
-
isMediaProjectionRecordingRunning: 'mobileIsMediaProjectionRecordingRunning',
|
|
141
|
-
stopMediaProjectionRecording: 'mobileStopMediaProjectionRecording',
|
|
142
|
-
|
|
143
|
-
getConnectivity: 'mobileGetConnectivity',
|
|
144
|
-
setConnectivity: 'mobileSetConnectivity',
|
|
145
|
-
toggleGps: 'toggleLocationServices',
|
|
146
|
-
isGpsEnables: 'isLocationServicesEnabled',
|
|
147
|
-
|
|
148
|
-
hideKeyboard: 'hideKeyboard',
|
|
149
|
-
isKeyboardShown: 'isKeyboardShown',
|
|
150
|
-
|
|
151
|
-
pressKey: 'mobilePressKey',
|
|
152
|
-
|
|
153
|
-
getDisplayDensity: 'getDisplayDensity',
|
|
154
|
-
getSystemBars: 'getSystemBars',
|
|
155
|
-
|
|
156
|
-
fingerprint: 'mobileFingerprint',
|
|
157
|
-
sendSms: 'mobileSendSms',
|
|
158
|
-
gsmCall: 'mobileGsmCall',
|
|
159
|
-
gsmSignal: 'mobileGsmSignal',
|
|
160
|
-
gsmVoice: 'mobileGsmVoice',
|
|
161
|
-
powerAc: 'mobilePowerAC',
|
|
162
|
-
powerCapacity: 'mobilePowerCapacity',
|
|
163
|
-
networkSpeed: 'mobileNetworkSpeed',
|
|
164
|
-
sensorSet: 'sensorSet',
|
|
165
|
-
|
|
166
|
-
getPerformanceData: 'mobileGetPerformanceData',
|
|
167
|
-
getPerformanceDataTypes: 'getPerformanceDataTypes',
|
|
168
|
-
|
|
169
|
-
statusBar: 'mobilePerformStatusBarCommand',
|
|
170
|
-
|
|
171
|
-
screenshots: 'mobileScreenshots',
|
|
172
|
-
|
|
173
|
-
scheduleAction: 'mobileScheduleAction',
|
|
174
|
-
getActionHistory: 'mobileGetActionHistory',
|
|
175
|
-
unscheduleAction: 'mobileUnscheduleAction',
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
if (!_.has(mobileCommandsMapping, mobileCommand)) {
|
|
179
|
-
throw new errors.UnknownCommandError(`Unknown mobile command "${mobileCommand}". ` +
|
|
180
|
-
`Only ${_.keys(mobileCommandsMapping)} commands are supported.`);
|
|
181
|
-
}
|
|
182
|
-
return await this[mobileCommandsMapping[mobileCommand]](opts);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
commands.mobileViewportScreenshot = async function () {
|
|
186
|
-
return await this.getViewportScreenshot();
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* @typedef {object} Rectangle
|
|
191
|
-
* @property {number} left - The left coordinate of the Rectangle.
|
|
192
|
-
* @property {number} top - The top coordinate of the Rectangle.
|
|
193
|
-
* @property {number} width - The width of Rectangle.
|
|
194
|
-
* @property {number} height - The height of Rectangle.
|
|
195
|
-
*/
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Returns the viewport coordinates.
|
|
199
|
-
* @returns {Rectangle} The viewport coordinates.
|
|
200
|
-
*/
|
|
201
|
-
commands.mobileViewPortRect = async function mobileViewPortRect () {
|
|
202
|
-
return await this.getViewPortRect();
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
commands.setUrl = async function (url) {
|
|
206
|
-
await this.adb.startUri(url, this.opts.appPackage);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* @typedef {object} DeepLinkOpts
|
|
211
|
-
* @property {!string} url - The name of URL to start.
|
|
212
|
-
* @property {!string} package - The name of the package to start the URI with.
|
|
213
|
-
* @property {?boolean} waitForLaunch [true] - if `false` then adb won't wait
|
|
214
|
-
* for the started activity to return the control
|
|
215
|
-
*/
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Start URL that take users directly to specific content in the app
|
|
219
|
-
* @param {DeepLinkOpts} opts
|
|
220
|
-
*/
|
|
221
|
-
commands.mobileDeepLink = async function (opts = {}) {
|
|
222
|
-
const {
|
|
223
|
-
url,
|
|
224
|
-
package: pkg,
|
|
225
|
-
waitForLaunch,
|
|
226
|
-
} = opts;
|
|
227
|
-
return await this.adb.startUri(url, pkg, { waitForLaunch });
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
commands.openNotifications = async function () {
|
|
231
|
-
return await this.uiautomator2.jwproxy.command('/appium/device/open_notifications', 'POST', {});
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
commands.updateSettings = async function (settings) {
|
|
235
|
-
await this.settings.update(settings);
|
|
236
|
-
await this.uiautomator2.jwproxy.command('/appium/settings', 'POST', {settings});
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
commands.getSettings = async function () {
|
|
240
|
-
const driverSettings = this.settings.getSettings();
|
|
241
|
-
const serverSettings = await this.uiautomator2.jwproxy.command('/appium/settings', 'GET');
|
|
242
|
-
return {...driverSettings, ...serverSettings};
|
|
243
|
-
};
|
|
5
|
+
import {errors, PROTOCOLS} from 'appium/driver';
|
|
6
|
+
import {APK_EXTENSION} from '../extensions';
|
|
7
|
+
import {mixin} from './mixins';
|
|
8
|
+
import {AndroidUiautomator2Driver} from '../driver';
|
|
244
9
|
|
|
245
10
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Stop proxying to any Chromedriver and redirect to uiautomator2
|
|
255
|
-
helpers.suspendChromedriverProxy = function () {
|
|
256
|
-
this.chromedriver = null;
|
|
257
|
-
this.proxyReqRes = this.uiautomator2.proxyReqRes.bind(this.uiautomator2);
|
|
258
|
-
this.proxyCommand = this.uiautomator2.proxyCommand.bind(this.uiautomator2);
|
|
259
|
-
this.jwpProxyActive = true;
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* The list of available info entries can be found at
|
|
264
|
-
* https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java
|
|
265
|
-
*/
|
|
266
|
-
commands.mobileGetDeviceInfo = async function () {
|
|
267
|
-
return await this.uiautomator2.jwproxy.command('/appium/device/info', 'GET');
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* @typedef {Object} TypingOptions
|
|
272
|
-
* @property {!string|number|boolean} text - The text to type
|
|
11
|
+
* Massages the arguments going into an execute method.
|
|
12
|
+
* @remarks A similar method is implemented in `appium-xcuitest-driver`, but it
|
|
13
|
+
* appears the methods in here handle unwrapping of `Element` objects, so we do
|
|
14
|
+
* not do that here.
|
|
15
|
+
* @param {readonly any[] | readonly [StringRecord] | Readonly<StringRecord>} [args]
|
|
16
|
+
* @internal
|
|
17
|
+
* @returns {StringRecord<unknown>}
|
|
273
18
|
*/
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
} = opts;
|
|
288
|
-
if (_.isUndefined(text)) {
|
|
289
|
-
this.log.errorAndThrow(`The 'text' argument is mandatory`);
|
|
19
|
+
function preprocessExecuteMethodArgs(args) {
|
|
20
|
+
if (_.isArray(args)) {
|
|
21
|
+
args = _.first(args);
|
|
22
|
+
}
|
|
23
|
+
const executeMethodArgs = /** @type {StringRecord<unknown>} */ (args ?? {});
|
|
24
|
+
/**
|
|
25
|
+
* Renames the deprecated `element` key to `elementId`. Historically,
|
|
26
|
+
* all of the pre-Execute-Method-Map execute methods accepted an `element` _or_ and `elementId` param.
|
|
27
|
+
* This assigns the `element` value to `elementId` if `elementId` is not already present.
|
|
28
|
+
*/
|
|
29
|
+
if (!('elementId' in executeMethodArgs) && 'element' in executeMethodArgs) {
|
|
30
|
+
executeMethodArgs.elementId = executeMethodArgs.element;
|
|
31
|
+
delete executeMethodArgs.element;
|
|
290
32
|
}
|
|
291
|
-
return await this.adb.typeUnicode(text);
|
|
292
|
-
};
|
|
293
|
-
|
|
294
33
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
* @property {boolean} allowTestPackages [false] - Set to true in order to allow test
|
|
298
|
-
* packages installation.
|
|
299
|
-
* @property {boolean} useSdcard [false] - Set to true to install the app on sdcard
|
|
300
|
-
* instead of the device memory.
|
|
301
|
-
* @property {boolean} grantPermissions [false] - Set to true in order to grant all the
|
|
302
|
-
* permissions requested in the application's manifest
|
|
303
|
-
* automatically after the installation is completed
|
|
304
|
-
* under Android 6+.
|
|
305
|
-
* @property {boolean} replace [true] - Set it to false if you don't want
|
|
306
|
-
* the application to be upgraded/reinstalled
|
|
307
|
-
* if it is already present on the device.
|
|
308
|
-
* @property {boolean} partialInstall [false] - Install apks partially. It is used for 'install-multiple'.
|
|
309
|
-
* https://android.stackexchange.com/questions/111064/what-is-a-partial-application-install-via-adb
|
|
310
|
-
*/
|
|
34
|
+
return executeMethodArgs;
|
|
35
|
+
}
|
|
311
36
|
|
|
312
37
|
/**
|
|
313
|
-
*
|
|
314
|
-
* @
|
|
315
|
-
*
|
|
316
|
-
* @
|
|
38
|
+
* Type guard to check if a script is an execute method.
|
|
39
|
+
* @param {any} script
|
|
40
|
+
* @internal
|
|
41
|
+
* @returns {script is keyof import('../execute-method-map').Uiautomator2ExecuteMethodMap}
|
|
317
42
|
*/
|
|
43
|
+
function isExecuteMethod(script) {
|
|
44
|
+
return script in AndroidUiautomator2Driver.executeMethodMap;
|
|
45
|
+
}
|
|
318
46
|
|
|
319
47
|
/**
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* @param {InstallMultipleApksOptions} opts
|
|
323
|
-
* @throws {Error} if an error occured while installing the given APKs.
|
|
48
|
+
* @type {import('./mixins').UIA2GeneralMixin}
|
|
49
|
+
* @satisfies {import('@appium/types').ExternalDriver}
|
|
324
50
|
*/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
51
|
+
const GeneralMixin = {
|
|
52
|
+
async getPageSource() {
|
|
53
|
+
return String(
|
|
54
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
55
|
+
'/source',
|
|
56
|
+
'GET',
|
|
57
|
+
{}
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
async getClipboard() {
|
|
63
|
+
const adb = /** @type {ADB} */ (this.adb);
|
|
64
|
+
return String(
|
|
65
|
+
(await adb.getApiLevel()) < 29
|
|
66
|
+
? await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
67
|
+
'/appium/device/get_clipboard',
|
|
68
|
+
'POST',
|
|
69
|
+
{}
|
|
70
|
+
)
|
|
71
|
+
: await adb.getClipboard()
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async doSendKeys(params) {
|
|
76
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
77
|
+
'/keys',
|
|
78
|
+
'POST',
|
|
79
|
+
params
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async keyevent(keycode, metastate) {
|
|
84
|
+
this.log.debug(`Ignoring metastate ${metastate}`);
|
|
85
|
+
await /** @type {ADB} */ (this.adb).keyevent(keycode);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async back() {
|
|
89
|
+
await /** @type {ADB} */ (this.adb).keyevent(4);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async getDisplayDensity() {
|
|
93
|
+
return /** @type {number} */ (
|
|
94
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
95
|
+
'/appium/device/display_density',
|
|
96
|
+
'GET',
|
|
97
|
+
{}
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async getWindowSize() {
|
|
103
|
+
return /** @type {import('@appium/types').Size} */ (
|
|
104
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
105
|
+
'/window/current/size',
|
|
106
|
+
'GET',
|
|
107
|
+
{}
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// For W3C
|
|
113
|
+
async getWindowRect() {
|
|
114
|
+
const {width, height} = await this.getWindowSize();
|
|
115
|
+
return {
|
|
116
|
+
width,
|
|
117
|
+
height,
|
|
118
|
+
x: 0,
|
|
119
|
+
y: 0,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @override
|
|
125
|
+
* @privateRemarks Because the "mobile" commands (execute methods) in this
|
|
126
|
+
* driver universally accept an options object, this method will _not_ call
|
|
127
|
+
* into `BaseDriver.executeMethod`.
|
|
128
|
+
*/
|
|
129
|
+
async execute(script, args) {
|
|
130
|
+
if (isExecuteMethod(script)) {
|
|
131
|
+
const executeMethodArgs = preprocessExecuteMethodArgs(args);
|
|
132
|
+
this.log.info(`Executing native command '${script}'`);
|
|
133
|
+
return await this.executeMobile(script, executeMethodArgs);
|
|
134
|
+
}
|
|
135
|
+
if (!this.isWebContext()) {
|
|
136
|
+
throw new errors.NotImplementedError();
|
|
137
|
+
}
|
|
138
|
+
const endpoint =
|
|
139
|
+
/** @type {import('appium-chromedriver').Chromedriver} */ (this.chromedriver).jwproxy
|
|
140
|
+
.downstreamProtocol === PROTOCOLS.MJSONWP
|
|
141
|
+
? '/execute'
|
|
142
|
+
: '/execute/sync';
|
|
143
|
+
return await /** @type {import('appium-chromedriver').Chromedriver} */ (
|
|
144
|
+
this.chromedriver
|
|
145
|
+
).jwproxy.command(endpoint, 'POST', {
|
|
146
|
+
script,
|
|
147
|
+
args,
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param script Must be of the form `mobile: <something>`, which differs from its parent class implementation.
|
|
153
|
+
* @override
|
|
154
|
+
*/
|
|
155
|
+
async executeMobile(script, opts = {}) {
|
|
156
|
+
if (!isExecuteMethod(script)) {
|
|
157
|
+
const commandNames = _.map(_.keys(AndroidUiautomator2Driver.executeMethodMap), (value) =>
|
|
158
|
+
value.slice(8)
|
|
159
|
+
);
|
|
160
|
+
throw new errors.UnknownCommandError(
|
|
161
|
+
`Unknown mobile command "${script}". ` +
|
|
162
|
+
`Only ${commandNames.join(', ')} commands are supported.`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const methodName =
|
|
166
|
+
AndroidUiautomator2Driver.executeMethodMap[
|
|
167
|
+
/** @type {keyof import('../execute-method-map').Uiautomator2ExecuteMethodMap} */ (script)
|
|
168
|
+
].command;
|
|
169
|
+
|
|
170
|
+
return await /** @type {(opts?: any) => Promise<unknown>} */ (this[methodName])(opts);
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
async mobileViewportScreenshot() {
|
|
174
|
+
return await this.getViewportScreenshot();
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns the viewport coordinates.
|
|
179
|
+
* @returns The viewport coordinates.
|
|
180
|
+
*/
|
|
181
|
+
async mobileViewPortRect() {
|
|
182
|
+
return await this.getViewPortRect();
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
async setUrl(url) {
|
|
186
|
+
await /** @type {ADB} */ (this.adb).startUri(url, /** @type {string} */ (this.opts.appPackage));
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Start URL that take users directly to specific content in the app
|
|
191
|
+
*/
|
|
192
|
+
async mobileDeepLink(opts) {
|
|
193
|
+
const {url, package: pkg, waitForLaunch} = opts;
|
|
194
|
+
return await /** @type {ADB} */ (this.adb).startUri(url, pkg, {waitForLaunch});
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
async openNotifications() {
|
|
198
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
199
|
+
'/appium/device/open_notifications',
|
|
200
|
+
'POST',
|
|
201
|
+
{}
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
// Stop proxying to any Chromedriver and redirect to uiautomator2
|
|
206
|
+
suspendChromedriverProxy() {
|
|
207
|
+
this.chromedriver = undefined;
|
|
208
|
+
this.proxyReqRes = /** @type {UiAutomator2Server} */ (this.uiautomator2).proxyReqRes.bind(
|
|
209
|
+
this.uiautomator2
|
|
210
|
+
);
|
|
211
|
+
this.proxyCommand = /** @type {typeof this.proxyCommand} */ (
|
|
212
|
+
/** @type {UiAutomator2Server} */ (this.uiautomator2).proxyCommand.bind(this.uiautomator2)
|
|
213
|
+
);
|
|
214
|
+
this.jwpProxyActive = true;
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The list of available info entries can be found at
|
|
219
|
+
* https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java
|
|
220
|
+
*/
|
|
221
|
+
async mobileGetDeviceInfo() {
|
|
222
|
+
return /** @type {StringRecord} */ (
|
|
223
|
+
await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
|
|
224
|
+
'/appium/device/info',
|
|
225
|
+
'GET'
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Types the given Unicode string.
|
|
232
|
+
* It is expected that the focus is already put
|
|
233
|
+
* to the destination input field before this method is called.
|
|
234
|
+
*
|
|
235
|
+
* @returns `true` if the input text has been successfully sent to adb
|
|
236
|
+
* @throws {Error} if `text` property has not been provided
|
|
237
|
+
*/
|
|
238
|
+
async mobileType(opts) {
|
|
239
|
+
const {text} = opts;
|
|
240
|
+
if (_.isUndefined(text)) {
|
|
241
|
+
this.log.errorAndThrow(`The 'text' argument is mandatory`);
|
|
242
|
+
throw new Error(); // unreachable
|
|
243
|
+
}
|
|
244
|
+
return await /** @type {ADB} */ (this.adb).typeUnicode(String(text));
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Install multiple APKs with `install-multiple` option.
|
|
249
|
+
*
|
|
250
|
+
* @throws {Error} if an error occured while installing the given APKs.
|
|
251
|
+
*/
|
|
252
|
+
async mobileInstallMultipleApks(opts) {
|
|
253
|
+
if (!_.isArray(opts.apks) || _.isEmpty(opts.apks)) {
|
|
254
|
+
throw new errors.InvalidArgumentError('No apks are given to install');
|
|
255
|
+
}
|
|
256
|
+
const apks = await B.all(
|
|
257
|
+
opts.apks.map((app) => this.helpers.configureApp(app, [APK_EXTENSION]))
|
|
258
|
+
);
|
|
259
|
+
await /** @type {ADB} */ (this.adb).installMultipleApks(apks, opts.options);
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Puts the app to background and waits the given number of seconds Then restores the app
|
|
264
|
+
* if necessary. The call is blocking.
|
|
265
|
+
*/
|
|
266
|
+
async mobileBackgroundApp(opts = {}) {
|
|
267
|
+
const {seconds = -1} = opts;
|
|
268
|
+
await this.background(seconds);
|
|
269
|
+
},
|
|
332
270
|
};
|
|
333
271
|
|
|
272
|
+
mixin(GeneralMixin);
|
|
273
|
+
|
|
334
274
|
/**
|
|
335
|
-
* @typedef {
|
|
336
|
-
* @
|
|
337
|
-
* putting the app to background and restoring it. Any negative value
|
|
338
|
-
* means to not restore the app after putting it to background.
|
|
275
|
+
* @typedef {import('../uiautomator2').UiAutomator2Server} UiAutomator2Server
|
|
276
|
+
* @typedef {import('appium-adb').ADB} ADB
|
|
339
277
|
*/
|
|
340
278
|
|
|
341
279
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* @param {BackgroundAppOptions} opts
|
|
280
|
+
* @template [T=any]
|
|
281
|
+
* @typedef {import('@appium/types').StringRecord<T>} StringRecord
|
|
346
282
|
*/
|
|
347
|
-
commands.mobileBackgroundApp = async function mobileBackgroundApp (opts = {}) {
|
|
348
|
-
const {
|
|
349
|
-
seconds = -1,
|
|
350
|
-
} = opts;
|
|
351
|
-
return await this.background(seconds);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
Object.assign(extensions, commands, helpers);
|
|
355
|
-
|
|
356
|
-
export default extensions;
|