appium-uiautomator2-driver 2.29.10 → 2.30.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/build/index.d.ts +4 -0
  3. package/build/index.d.ts.map +1 -0
  4. package/build/index.js +8 -16
  5. package/build/index.js.map +1 -0
  6. package/build/lib/commands/actions.d.ts +2 -0
  7. package/build/lib/commands/actions.d.ts.map +1 -0
  8. package/build/lib/commands/actions.js +67 -65
  9. package/build/lib/commands/actions.js.map +1 -1
  10. package/build/lib/commands/alert.d.ts +2 -0
  11. package/build/lib/commands/alert.d.ts.map +1 -0
  12. package/build/lib/commands/alert.js +28 -29
  13. package/build/lib/commands/alert.js.map +1 -1
  14. package/build/lib/commands/app-strings.d.ts +3 -0
  15. package/build/lib/commands/app-strings.d.ts.map +1 -0
  16. package/build/lib/commands/app-strings.js +86 -58
  17. package/build/lib/commands/app-strings.js.map +1 -1
  18. package/build/lib/commands/battery.d.ts +2 -0
  19. package/build/lib/commands/battery.d.ts.map +1 -0
  20. package/build/lib/commands/battery.js +26 -18
  21. package/build/lib/commands/battery.js.map +1 -1
  22. package/build/lib/commands/element.d.ts +2 -0
  23. package/build/lib/commands/element.d.ts.map +1 -0
  24. package/build/lib/commands/element.js +140 -162
  25. package/build/lib/commands/element.js.map +1 -1
  26. package/build/lib/commands/find.d.ts +2 -0
  27. package/build/lib/commands/find.d.ts.map +1 -0
  28. package/build/lib/commands/find.js +39 -27
  29. package/build/lib/commands/find.js.map +1 -1
  30. package/build/lib/commands/general.d.ts +4 -0
  31. package/build/lib/commands/general.d.ts.map +1 -0
  32. package/build/lib/commands/general.js +217 -216
  33. package/build/lib/commands/general.js.map +1 -1
  34. package/build/lib/commands/gestures.d.ts +2 -0
  35. package/build/lib/commands/gestures.d.ts.map +1 -0
  36. package/build/lib/commands/gestures.js +206 -194
  37. package/build/lib/commands/gestures.js.map +1 -1
  38. package/build/lib/commands/index.d.ts +2 -0
  39. package/build/lib/commands/index.d.ts.map +1 -0
  40. package/build/lib/commands/index.js +13 -23
  41. package/build/lib/commands/index.js.map +1 -1
  42. package/build/lib/commands/mixins.d.ts +84 -0
  43. package/build/lib/commands/mixins.d.ts.map +1 -0
  44. package/build/lib/commands/mixins.js +23 -0
  45. package/build/lib/commands/mixins.js.map +1 -0
  46. package/build/lib/commands/screenshot.d.ts +2 -0
  47. package/build/lib/commands/screenshot.d.ts.map +1 -0
  48. package/build/lib/commands/screenshot.js +77 -63
  49. package/build/lib/commands/screenshot.js.map +1 -1
  50. package/build/lib/commands/touch.d.ts +2 -0
  51. package/build/lib/commands/touch.d.ts.map +1 -0
  52. package/build/lib/commands/touch.js +48 -39
  53. package/build/lib/commands/touch.js.map +1 -1
  54. package/build/lib/commands/types.d.ts +452 -0
  55. package/build/lib/commands/types.d.ts.map +1 -0
  56. package/build/lib/commands/types.js +3 -0
  57. package/build/lib/commands/types.js.map +1 -0
  58. package/build/lib/commands/viewport.d.ts +2 -0
  59. package/build/lib/commands/viewport.d.ts.map +1 -0
  60. package/build/lib/commands/viewport.js +37 -37
  61. package/build/lib/commands/viewport.js.map +1 -1
  62. package/build/lib/constraints.d.ts +334 -0
  63. package/build/lib/constraints.d.ts.map +1 -0
  64. package/build/lib/constraints.js +51 -0
  65. package/build/lib/constraints.js.map +1 -0
  66. package/build/lib/css-converter.d.ts +45 -0
  67. package/build/lib/css-converter.d.ts.map +1 -0
  68. package/build/lib/css-converter.js +272 -176
  69. package/build/lib/css-converter.js.map +1 -1
  70. package/build/lib/driver.d.ts +912 -0
  71. package/build/lib/driver.d.ts.map +1 -0
  72. package/build/lib/driver.js +738 -483
  73. package/build/lib/driver.js.map +1 -1
  74. package/build/lib/execute-method-map.d.ts +477 -0
  75. package/build/lib/execute-method-map.d.ts.map +1 -0
  76. package/build/lib/execute-method-map.js +542 -0
  77. package/build/lib/execute-method-map.js.map +1 -0
  78. package/build/lib/extensions.d.ts +3 -0
  79. package/build/lib/extensions.d.ts.map +1 -0
  80. package/build/lib/extensions.js +3 -7
  81. package/build/lib/extensions.js.map +1 -1
  82. package/build/lib/helpers.d.ts +7 -0
  83. package/build/lib/helpers.d.ts.map +1 -0
  84. package/build/lib/helpers.js +36 -30
  85. package/build/lib/helpers.js.map +1 -1
  86. package/build/lib/logger.d.ts +3 -0
  87. package/build/lib/logger.d.ts.map +1 -0
  88. package/build/lib/logger.js +5 -11
  89. package/build/lib/logger.js.map +1 -1
  90. package/build/lib/method-map.d.ts +389 -0
  91. package/build/lib/method-map.d.ts.map +1 -0
  92. package/build/lib/method-map.js +11 -18
  93. package/build/lib/method-map.js.map +1 -1
  94. package/build/lib/types.d.ts +45 -0
  95. package/build/lib/types.d.ts.map +1 -0
  96. package/build/lib/types.js +3 -0
  97. package/build/lib/types.js.map +1 -0
  98. package/build/lib/uiautomator2.d.ts +45 -0
  99. package/build/lib/uiautomator2.d.ts.map +1 -0
  100. package/build/lib/uiautomator2.js +334 -297
  101. package/build/lib/uiautomator2.js.map +1 -1
  102. package/build/lib/utils.d.ts +10 -0
  103. package/build/lib/utils.d.ts.map +1 -0
  104. package/build/lib/utils.js +23 -16
  105. package/build/lib/utils.js.map +1 -1
  106. package/build/tsconfig.tsbuildinfo +1 -0
  107. package/index.js +5 -3
  108. package/lib/commands/actions.js +115 -101
  109. package/lib/commands/alert.js +36 -44
  110. package/lib/commands/app-strings.js +79 -58
  111. package/lib/commands/battery.js +27 -28
  112. package/lib/commands/element.js +231 -134
  113. package/lib/commands/find.js +40 -21
  114. package/lib/commands/general.js +271 -336
  115. package/lib/commands/gestures.js +252 -366
  116. package/lib/commands/index.js +11 -31
  117. package/lib/commands/mixins.ts +167 -0
  118. package/lib/commands/screenshot.js +80 -76
  119. package/lib/commands/touch.js +64 -31
  120. package/lib/commands/types.ts +473 -0
  121. package/lib/commands/viewport.js +43 -31
  122. package/lib/constraints.ts +53 -0
  123. package/lib/css-converter.js +9 -1
  124. package/lib/{driver.js → driver.ts} +383 -225
  125. package/lib/execute-method-map.ts +573 -0
  126. package/lib/method-map.ts +11 -0
  127. package/lib/types.ts +59 -0
  128. package/lib/uiautomator2.js +21 -2
  129. package/lib/utils.js +2 -2
  130. package/npm-shrinkwrap.json +396 -528
  131. package/package.json +96 -70
  132. package/build/lib/desired-caps.js +0 -72
  133. package/build/lib/desired-caps.js.map +0 -1
  134. package/lib/desired-caps.js +0 -70
  135. package/lib/method-map.js +0 -11
@@ -1,356 +1,291 @@
1
+ // @ts-check
2
+
1
3
  import _ from 'lodash';
2
4
  import B from 'bluebird';
3
- import { errors } from 'appium/driver';
4
- import { APK_EXTENSION } from '../extensions';
5
-
6
- let extensions = {},
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
- * Overriding appium-android-driver's wrapBootstrapDisconnect,
247
- * unlike in appium-android-driver avoiding adb restarting as it intern
248
- * kills UiAutomator2 server running in the device.
249
- **/
250
- helpers.wrapBootstrapDisconnect = async function (wrapped) {
251
- await wrapped();
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
- * Types the given Unicode string.
277
- * It is expected that the focus is already put
278
- * to the destination input field before this method is called.
279
- *
280
- * @param {TypingOptions} opts
281
- * @returns {boolean} `true` if the input text has been successfully sent to adb
282
- * @throws {Error} if `text` property has not been provided
283
- */
284
- commands.mobileType = async function mobileType (opts = {}) {
285
- const {
286
- text,
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
- * @typedef {Object} InstallOptions
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
- * @typedef {Object} InstallMultipleApksOptions
314
- * @property {Array<string>} apks - The list of APKs to install. Each APK should be a path to a apk
315
- * or downloadable URL as HTTP/HTTPS.
316
- * @property {InstallOptions} options
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
- * Install multiple APKs with `install-multiple` option.
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
- commands.mobileInstallMultipleApks = async function (opts = {}) {
326
- if (!_.isArray(opts.apks) || _.isEmpty(opts.apks)) {
327
- throw new errors.InvalidArgumentError('No apks are given to install');
328
- }
329
- const apks = await B.all(opts.apks
330
- .map((app) => this.helpers.configureApp(app, [APK_EXTENSION])));
331
- await this.adb.installMultipleApks(apks, opts.options);
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
+ /**
206
+ * Overriding appium-android-driver's wrapBootstrapDisconnect,
207
+ * unlike in appium-android-driver avoiding adb restarting as it intern
208
+ * kills UiAutomator2 server running in the device.
209
+ **/
210
+ async wrapBootstrapDisconnect(wrapped) {
211
+ await wrapped();
212
+ },
213
+
214
+ // Stop proxying to any Chromedriver and redirect to uiautomator2
215
+ suspendChromedriverProxy() {
216
+ this.chromedriver = undefined;
217
+ this.proxyReqRes = /** @type {UiAutomator2Server} */ (this.uiautomator2).proxyReqRes.bind(
218
+ this.uiautomator2
219
+ );
220
+ this.proxyCommand = /** @type {typeof this.proxyCommand} */ (
221
+ /** @type {UiAutomator2Server} */ (this.uiautomator2).proxyCommand.bind(this.uiautomator2)
222
+ );
223
+ this.jwpProxyActive = true;
224
+ },
225
+
226
+ /**
227
+ * The list of available info entries can be found at
228
+ * https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java
229
+ */
230
+ async mobileGetDeviceInfo() {
231
+ return /** @type {StringRecord} */ (
232
+ await /** @type {UiAutomator2Server} */ (this.uiautomator2).jwproxy.command(
233
+ '/appium/device/info',
234
+ 'GET'
235
+ )
236
+ );
237
+ },
238
+
239
+ /**
240
+ * Types the given Unicode string.
241
+ * It is expected that the focus is already put
242
+ * to the destination input field before this method is called.
243
+ *
244
+ * @returns `true` if the input text has been successfully sent to adb
245
+ * @throws {Error} if `text` property has not been provided
246
+ */
247
+ async mobileType(opts) {
248
+ const {text} = opts;
249
+ if (_.isUndefined(text)) {
250
+ this.log.errorAndThrow(`The 'text' argument is mandatory`);
251
+ throw new Error(); // unreachable
252
+ }
253
+ return await /** @type {ADB} */ (this.adb).typeUnicode(String(text));
254
+ },
255
+
256
+ /**
257
+ * Install multiple APKs with `install-multiple` option.
258
+ *
259
+ * @throws {Error} if an error occured while installing the given APKs.
260
+ */
261
+ async mobileInstallMultipleApks(opts) {
262
+ if (!_.isArray(opts.apks) || _.isEmpty(opts.apks)) {
263
+ throw new errors.InvalidArgumentError('No apks are given to install');
264
+ }
265
+ const apks = await B.all(
266
+ opts.apks.map((app) => this.helpers.configureApp(app, [APK_EXTENSION]))
267
+ );
268
+ await /** @type {ADB} */ (this.adb).installMultipleApks(apks, opts.options);
269
+ },
270
+
271
+ /**
272
+ * Puts the app to background and waits the given number of seconds Then restores the app
273
+ * if necessary. The call is blocking.
274
+ */
275
+ async mobileBackgroundApp(opts = {}) {
276
+ const {seconds = -1} = opts;
277
+ await this.background(seconds);
278
+ },
332
279
  };
333
280
 
281
+ mixin(GeneralMixin);
282
+
334
283
  /**
335
- * @typedef {Object} BackgroundAppOptions
336
- * @property {number} seconds The amount of seconds to wait between
337
- * putting the app to background and restoring it. Any negative value
338
- * means to not restore the app after putting it to background.
284
+ * @typedef {import('../uiautomator2').UiAutomator2Server} UiAutomator2Server
285
+ * @typedef {import('appium-adb').ADB} ADB
339
286
  */
340
287
 
341
288
  /**
342
- * Puts the app to background and waits the given number of seconds Then restores the app
343
- * if necessary. The call is blocking.
344
- *
345
- * @param {BackgroundAppOptions} opts
289
+ * @template [T=any]
290
+ * @typedef {import('@appium/types').StringRecord<T>} StringRecord
346
291
  */
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;