appium-uiautomator2-driver 2.42.2 → 2.43.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 (114) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/lib/commands/actions.d.ts +22 -1
  3. package/build/lib/commands/actions.d.ts.map +1 -1
  4. package/build/lib/commands/actions.js +30 -62
  5. package/build/lib/commands/actions.js.map +1 -1
  6. package/build/lib/commands/alert.d.ts +28 -1
  7. package/build/lib/commands/alert.d.ts.map +1 -1
  8. package/build/lib/commands/alert.js +42 -23
  9. package/build/lib/commands/alert.js.map +1 -1
  10. package/build/lib/commands/app-management.d.ts +19 -0
  11. package/build/lib/commands/app-management.d.ts.map +1 -0
  12. package/build/lib/commands/app-management.js +45 -0
  13. package/build/lib/commands/app-management.js.map +1 -0
  14. package/build/lib/commands/app-strings.d.ts +9 -0
  15. package/build/lib/commands/app-strings.d.ts.map +1 -1
  16. package/build/lib/commands/app-strings.js +11 -79
  17. package/build/lib/commands/app-strings.js.map +1 -1
  18. package/build/lib/commands/battery.d.ts +7 -0
  19. package/build/lib/commands/battery.d.ts.map +1 -1
  20. package/build/lib/commands/battery.js +14 -20
  21. package/build/lib/commands/battery.js.map +1 -1
  22. package/build/lib/commands/element.d.ts +100 -1
  23. package/build/lib/commands/element.d.ts.map +1 -1
  24. package/build/lib/commands/element.js +175 -125
  25. package/build/lib/commands/element.js.map +1 -1
  26. package/build/lib/commands/execute.d.ts +25 -0
  27. package/build/lib/commands/execute.d.ts.map +1 -0
  28. package/build/lib/commands/execute.js +109 -0
  29. package/build/lib/commands/execute.js.map +1 -0
  30. package/build/lib/commands/find.d.ts +10 -0
  31. package/build/lib/commands/find.d.ts.map +1 -1
  32. package/build/lib/commands/find.js +25 -27
  33. package/build/lib/commands/find.js.map +1 -1
  34. package/build/lib/commands/gestures.d.ts +103 -1
  35. package/build/lib/commands/gestures.d.ts.map +1 -1
  36. package/build/lib/commands/gestures.js +202 -173
  37. package/build/lib/commands/gestures.js.map +1 -1
  38. package/build/lib/commands/keyboard.d.ts +47 -0
  39. package/build/lib/commands/keyboard.d.ts.map +1 -0
  40. package/build/lib/commands/keyboard.js +92 -0
  41. package/build/lib/commands/keyboard.js.map +1 -0
  42. package/build/lib/commands/misc.d.ts +48 -0
  43. package/build/lib/commands/misc.d.ts.map +1 -0
  44. package/build/lib/commands/misc.js +75 -0
  45. package/build/lib/commands/misc.js.map +1 -0
  46. package/build/lib/commands/navigation.d.ts +20 -0
  47. package/build/lib/commands/navigation.d.ts.map +1 -0
  48. package/build/lib/commands/navigation.js +35 -0
  49. package/build/lib/commands/navigation.js.map +1 -0
  50. package/build/lib/commands/screenshot.d.ts +24 -1
  51. package/build/lib/commands/screenshot.d.ts.map +1 -1
  52. package/build/lib/commands/screenshot.js +87 -64
  53. package/build/lib/commands/screenshot.js.map +1 -1
  54. package/build/lib/commands/touch.d.ts +81 -0
  55. package/build/lib/commands/touch.d.ts.map +1 -1
  56. package/build/lib/commands/touch.js +158 -41
  57. package/build/lib/commands/touch.js.map +1 -1
  58. package/build/lib/commands/viewport.d.ts +37 -1
  59. package/build/lib/commands/viewport.d.ts.map +1 -1
  60. package/build/lib/commands/viewport.js +80 -36
  61. package/build/lib/commands/viewport.js.map +1 -1
  62. package/build/lib/driver.d.ts +94 -24
  63. package/build/lib/driver.d.ts.map +1 -1
  64. package/build/lib/driver.js +114 -28
  65. package/build/lib/driver.js.map +1 -1
  66. package/build/lib/helpers.d.ts +12 -6
  67. package/build/lib/helpers.d.ts.map +1 -1
  68. package/build/lib/helpers.js +18 -18
  69. package/build/lib/helpers.js.map +1 -1
  70. package/build/lib/method-map.d.ts +0 -23
  71. package/build/lib/method-map.d.ts.map +1 -1
  72. package/build/lib/uiautomator2.js +3 -3
  73. package/build/lib/uiautomator2.js.map +1 -1
  74. package/build/tsconfig.tsbuildinfo +1 -1
  75. package/lib/commands/actions.js +37 -114
  76. package/lib/commands/alert.js +51 -37
  77. package/lib/commands/app-management.js +42 -0
  78. package/lib/commands/app-strings.js +9 -89
  79. package/lib/commands/battery.js +16 -26
  80. package/lib/commands/element.js +235 -214
  81. package/lib/commands/execute.js +120 -0
  82. package/lib/commands/find.js +31 -37
  83. package/lib/commands/gestures.js +252 -234
  84. package/lib/commands/keyboard.js +103 -0
  85. package/lib/commands/misc.js +106 -0
  86. package/lib/commands/navigation.js +31 -0
  87. package/lib/commands/screenshot.js +96 -77
  88. package/lib/commands/touch.js +190 -48
  89. package/lib/commands/viewport.js +100 -50
  90. package/lib/driver.ts +225 -36
  91. package/lib/helpers.js +15 -22
  92. package/lib/uiautomator2.js +3 -3
  93. package/npm-shrinkwrap.json +31 -31
  94. package/package.json +2 -2
  95. package/build/lib/commands/general.d.ts +0 -4
  96. package/build/lib/commands/general.d.ts.map +0 -1
  97. package/build/lib/commands/general.js +0 -214
  98. package/build/lib/commands/general.js.map +0 -1
  99. package/build/lib/commands/index.d.ts +0 -2
  100. package/build/lib/commands/index.d.ts.map +0 -1
  101. package/build/lib/commands/index.js +0 -14
  102. package/build/lib/commands/index.js.map +0 -1
  103. package/build/lib/commands/mixins.d.ts +0 -87
  104. package/build/lib/commands/mixins.d.ts.map +0 -1
  105. package/build/lib/commands/mixins.js +0 -26
  106. package/build/lib/commands/mixins.js.map +0 -1
  107. package/build/lib/utils.d.ts +0 -10
  108. package/build/lib/utils.d.ts.map +0 -1
  109. package/build/lib/utils.js +0 -26
  110. package/build/lib/utils.js.map +0 -1
  111. package/lib/commands/general.js +0 -289
  112. package/lib/commands/index.js +0 -11
  113. package/lib/commands/mixins.ts +0 -169
  114. package/lib/utils.js +0 -19
@@ -1,241 +1,262 @@
1
- // @ts-check
2
-
3
1
  import B from 'bluebird';
4
2
  import _ from 'lodash';
5
- import {util} from 'appium/support';
6
- import {PROTOCOLS, W3C_ELEMENT_KEY} from 'appium/driver';
7
- import {requireArgs} from '../utils';
8
- import {mixin} from './mixins';
3
+ import {PROTOCOLS} from 'appium/driver';
4
+ import {utils} from 'appium-android-driver';
9
5
 
10
6
  /**
11
- * @param {any} s
12
- * @returns {boolean}
7
+ * @this {AndroidUiautomator2Driver}
8
+ * @returns {Promise<import('@appium/types').Element>}
13
9
  */
14
- function toBool(s) {
15
- return _.isString(s) ? s.toLowerCase() === 'true' : !!s;
10
+ export async function active() {
11
+ return /** @type {import('@appium/types').Element} */ (
12
+ await this.uiautomator2.jwproxy.command(
13
+ '/element/active',
14
+ 'GET'
15
+ )
16
+ );
16
17
  }
17
18
 
18
19
  /**
19
- * @type {import('./mixins').UIA2ElementMixin}
20
- * @satisfies {import('@appium/types').ExternalDriver}
20
+ * @this {AndroidUiautomator2Driver}
21
+ * @param {string} attribute
22
+ * @param {string} elementId
23
+ * @returns {Promise<string?>}
21
24
  */
22
- const ElementMixin = {
23
- async active() {
24
- return /** @type {import('@appium/types').Element} */ (
25
- await this.uiautomator2.jwproxy.command(
26
- '/element/active',
27
- 'GET'
28
- )
29
- );
30
- },
31
- async getAttribute(attribute, elementId) {
32
- return String(
33
- await this.uiautomator2.jwproxy.command(
34
- `/element/${elementId}/attribute/${attribute}`,
35
- 'GET',
36
- {}
37
- )
38
- );
39
- },
40
- async elementDisplayed(elementId) {
41
- return toBool(await this.getAttribute('displayed', elementId));
42
- },
43
- async elementEnabled(elementId) {
44
- return toBool(await this.getAttribute('enabled', elementId));
45
- },
46
- async elementSelected(elementId) {
47
- return toBool(await this.getAttribute('selected', elementId));
48
- },
49
- async getName(elementId) {
50
- return /** @type {string} */ (
51
- await this.uiautomator2.jwproxy.command(
52
- `/element/${elementId}/name`,
53
- 'GET',
54
- {}
55
- )
56
- );
57
- },
58
- async getLocation(elementId) {
59
- return /** @type {import('@appium/types').Position} */ (
60
- await this.uiautomator2.jwproxy.command(
61
- `/element/${elementId}/location`,
62
- 'GET',
63
- {}
64
- )
65
- );
66
- },
67
- async getSize(elementId) {
68
- return /** @type {import('@appium/types').Size} */ (
69
- await this.uiautomator2.jwproxy.command(
70
- `/element/${elementId}/size`,
71
- 'GET',
72
- {}
73
- )
74
- );
75
- },
76
- async touchLongClick(element, x, y, duration) {
77
- let params = {element, x, y, duration};
78
- await this.uiautomator2.jwproxy.command(
79
- `/touch/longclick`,
80
- 'POST',
81
- {params}
82
- );
83
- },
84
- async touchDown(element, x, y) {
85
- let params = {element, x, y};
86
- await this.uiautomator2.jwproxy.command(
87
- `/touch/down`,
88
- 'POST',
89
- {params}
90
- );
91
- },
92
- async touchUp(element, x, y) {
93
- let params = {element, x, y};
25
+ export async function getAttribute(attribute, elementId) {
26
+ return String(
94
27
  await this.uiautomator2.jwproxy.command(
95
- `/touch/up`,
96
- 'POST',
97
- {params}
98
- );
99
- },
100
- async touchMove(element, x, y) {
101
- let params = {element, x, y};
102
- await this.uiautomator2.jwproxy.command(
103
- `/touch/move`,
104
- 'POST',
105
- {params}
106
- );
107
- },
108
- async doSetElementValue(params) {
28
+ `/element/${elementId}/attribute/${attribute}`,
29
+ 'GET',
30
+ {}
31
+ )
32
+ );
33
+ }
34
+
35
+ /**
36
+ * @this {AndroidUiautomator2Driver}
37
+ * @param {string} elementId
38
+ * @returns {Promise<boolean>}
39
+ */
40
+ export async function elementDisplayed(elementId) {
41
+ return toBool(await this.getAttribute('displayed', elementId));
42
+ }
43
+
44
+ /**
45
+ * @this {AndroidUiautomator2Driver}
46
+ * @param {string} elementId
47
+ * @returns {Promise<boolean>}
48
+ */
49
+ export async function elementEnabled(elementId) {
50
+ return toBool(await this.getAttribute('enabled', elementId));
51
+ }
52
+
53
+ /**
54
+ * @this {AndroidUiautomator2Driver}
55
+ * @param {string} elementId
56
+ * @returns {Promise<boolean>}
57
+ */
58
+ export async function elementSelected(elementId) {
59
+ return toBool(await this.getAttribute('selected', elementId));
60
+ }
61
+
62
+ /**
63
+ * @this {AndroidUiautomator2Driver}
64
+ * @param {string} elementId
65
+ * @returns {Promise<string>}
66
+ */
67
+ export async function getName(elementId) {
68
+ return /** @type {string} */ (
109
69
  await this.uiautomator2.jwproxy.command(
110
- `/element/${params.elementId}/value`,
111
- 'POST',
112
- params
113
- );
114
- },
115
- async setValueImmediate(keys, elementId) {
70
+ `/element/${elementId}/name`,
71
+ 'GET',
72
+ {}
73
+ )
74
+ );
75
+ }
76
+
77
+ /**
78
+ * @this {AndroidUiautomator2Driver}
79
+ * @param {string} elementId
80
+ * @returns {Promise<import('@appium/types').Position>}
81
+ */
82
+ export async function getLocation(elementId) {
83
+ return /** @type {import('@appium/types').Position} */ (
116
84
  await this.uiautomator2.jwproxy.command(
117
- `/element/${elementId}/value`,
118
- 'POST',
119
- {
120
- elementId,
121
- text: _.isArray(keys) ? keys.join('') : keys,
122
- replace: false,
123
- }
124
- );
125
- },
126
- async getText(elementId) {
127
- return String(
128
- await this.uiautomator2.jwproxy.command(
129
- `/element/${elementId}/text`,
130
- 'GET',
131
- {}
132
- )
133
- );
134
- },
135
- async click(element) {
85
+ `/element/${elementId}/location`,
86
+ 'GET',
87
+ {}
88
+ )
89
+ );
90
+ }
91
+
92
+ /**
93
+ * @this {AndroidUiautomator2Driver}
94
+ * @param {string} elementId
95
+ * @returns {Promise<import('@appium/types').Size>}
96
+ */
97
+ export async function getSize(elementId) {
98
+ return /** @type {import('@appium/types').Size} */ (
136
99
  await this.uiautomator2.jwproxy.command(
137
- `/element/${element}/click`,
138
- 'POST',
139
- {element}
140
- );
141
- },
142
- async getElementScreenshot(element) {
143
- return String(
144
- await this.uiautomator2.jwproxy.command(
145
- `/element/${element}/screenshot`,
146
- 'GET',
147
- {}
148
- )
149
- );
150
- },
100
+ `/element/${elementId}/size`,
101
+ 'GET',
102
+ {}
103
+ )
104
+ );
105
+ }
151
106
 
152
- async tap(elementId = null, x = null, y = null, count = 1) {
153
- const areCoordinatesDefined = util.hasValue(x) && util.hasValue(y);
154
- if (!util.hasValue(elementId) && !areCoordinatesDefined) {
155
- throw new Error(`Either element id to tap or both absolute coordinates should be defined`);
156
- }
107
+ /**
108
+ * @this {AndroidUiautomator2Driver}
109
+ * @param {import('appium-android-driver').DoSetElementValueOpts} params
110
+ * @returns {Promise<void>}
111
+ */
112
+ export async function doSetElementValue(params) {
113
+ await this.uiautomator2.jwproxy.command(
114
+ `/element/${params.elementId}/value`,
115
+ 'POST',
116
+ params
117
+ );
118
+ }
157
119
 
158
- for (let i = 0; i < count; i++) {
159
- if (util.hasValue(elementId) && !areCoordinatesDefined) {
160
- // we are either tapping on the default location of the element
161
- // or an offset from the top left corner
162
- await this.uiautomator2.jwproxy.command(
163
- `/element/${elementId}/click`,
164
- 'POST'
165
- );
166
- } else {
167
- await this.uiautomator2.jwproxy.command(
168
- `/appium/tap`,
169
- 'POST',
170
- {
171
- x,
172
- y,
173
- [W3C_ELEMENT_KEY]: elementId,
174
- }
175
- );
176
- }
120
+ /**
121
+ * @this {AndroidUiautomator2Driver}
122
+ * @param {string|string[]} keys
123
+ * @param {string} elementId
124
+ * @returns {Promise<void>}
125
+ */
126
+ export async function setValueImmediate(keys, elementId) {
127
+ await this.uiautomator2.jwproxy.command(
128
+ `/element/${elementId}/value`,
129
+ 'POST',
130
+ {
131
+ elementId,
132
+ text: _.isArray(keys) ? keys.join('') : keys,
133
+ replace: false,
177
134
  }
178
- },
135
+ );
136
+ }
137
+
138
+ /**
139
+ * @this {AndroidUiautomator2Driver}
140
+ * @param {string} elementId
141
+ * @returns {Promise<string>}
142
+ */
143
+ export async function getText(elementId) {
144
+ return String(
145
+ await this.uiautomator2.jwproxy.command(
146
+ `/element/${elementId}/text`,
147
+ 'GET',
148
+ {}
149
+ )
150
+ );
151
+ }
152
+
153
+ /**
154
+ * @this {AndroidUiautomator2Driver}
155
+ * @param {string} element
156
+ * @returns {Promise<void>}
157
+ */
158
+ export async function click(element) {
159
+ await this.uiautomator2.jwproxy.command(
160
+ `/element/${element}/click`,
161
+ 'POST',
162
+ {element}
163
+ );
164
+ }
179
165
 
180
- async clear(elementId) {
166
+ /**
167
+ * @this {AndroidUiautomator2Driver}
168
+ * @param {string} element
169
+ * @returns {Promise<string>}
170
+ */
171
+ export async function getElementScreenshot(element) {
172
+ return String(
181
173
  await this.uiautomator2.jwproxy.command(
182
- `/element/${elementId}/clear`,
183
- 'POST',
184
- {
185
- elementId,
186
- }
174
+ `/element/${element}/screenshot`,
175
+ 'GET',
176
+ {}
177
+ )
178
+ );
179
+ }
180
+
181
+ /**
182
+ * @this {AndroidUiautomator2Driver}
183
+ * @param {string} elementId
184
+ * @returns {Promise<void>}
185
+ */
186
+ export async function clear(elementId) {
187
+ await this.uiautomator2.jwproxy.command(
188
+ `/element/${elementId}/clear`,
189
+ 'POST',
190
+ {
191
+ elementId,
192
+ }
193
+ );
194
+ }
195
+
196
+ /**
197
+ * @this {AndroidUiautomator2Driver}
198
+ * @param {string} elementId
199
+ * @returns {Promise<import('@appium/types').Rect>}
200
+ */
201
+ export async function getElementRect(elementId) {
202
+ const chromedriver = /** @type {import('appium-chromedriver').default} */ (this.chromedriver);
203
+ if (this.isWebContext()) {
204
+ this.log.debug(
205
+ `Detected downstream chromedriver protocol: ${chromedriver.jwproxy.downstreamProtocol}`
187
206
  );
188
- },
189
-
190
- async getElementRect(elementId) {
191
- const chromedriver = /** @type {import('appium-chromedriver').default} */ (this.chromedriver);
192
- if (this.isWebContext()) {
193
- this.log.debug(
194
- `Detected downstream chromedriver protocol: ${chromedriver.jwproxy.downstreamProtocol}`
195
- );
196
- if (chromedriver.jwproxy.downstreamProtocol === PROTOCOLS.MJSONWP) {
197
- const [{x, y}, {width, height}] =
198
- /** @type {[import('@appium/types').Position, import('@appium/types').Size]} */ (
199
- await B.all([
200
- chromedriver.jwproxy.command(`/element/${elementId}/location`, 'GET'),
201
- chromedriver.jwproxy.command(`/element/${elementId}/size`, 'GET'),
202
- ])
203
- );
204
- return {x, y, width, height};
205
- }
206
- return /** @type {import('@appium/types').Rect} */ (
207
- await chromedriver.jwproxy.command(`/element/${elementId}/rect`, 'GET')
208
- );
207
+ if (chromedriver.jwproxy.downstreamProtocol === PROTOCOLS.MJSONWP) {
208
+ const [{x, y}, {width, height}] =
209
+ /** @type {[import('@appium/types').Position, import('@appium/types').Size]} */ (
210
+ await B.all([
211
+ chromedriver.jwproxy.command(`/element/${elementId}/location`, 'GET'),
212
+ chromedriver.jwproxy.command(`/element/${elementId}/size`, 'GET'),
213
+ ])
214
+ );
215
+ return {x, y, width, height};
209
216
  }
210
217
  return /** @type {import('@appium/types').Rect} */ (
211
- await this.uiautomator2.jwproxy.command(
212
- `/element/${elementId}/rect`,
213
- 'GET'
214
- )
218
+ await chromedriver.jwproxy.command(`/element/${elementId}/rect`, 'GET')
215
219
  );
216
- },
217
-
218
- /**
219
- * Sends text to the given element by replacing its previous content
220
- *
221
- * @param {import('./types').ReplaceValueOptions} opts
222
- * @throws {Error} If there was a faulre while setting the text
223
- */
224
- async mobileReplaceElementValue(opts) {
225
- const {elementId, text} = requireArgs(['elementId', 'text'], opts);
220
+ }
221
+ return /** @type {import('@appium/types').Rect} */ (
226
222
  await this.uiautomator2.jwproxy.command(
227
- `/element/${elementId}/value`,
228
- 'POST',
229
- {
230
- text,
231
- replace: true,
232
- }
233
- );
234
- },
235
- };
223
+ `/element/${elementId}/rect`,
224
+ 'GET'
225
+ )
226
+ );
227
+ }
228
+
229
+ /**
230
+ * Sends text to the given element by replacing its previous content
231
+ * @this {AndroidUiautomator2Driver}
232
+ * @param {import('./types').ReplaceValueOptions} opts
233
+ * @throws {Error} If there was a faulre while setting the text
234
+ * @returns {Promise<void>}
235
+ */
236
+ export async function mobileReplaceElementValue(opts) {
237
+ const {elementId, text} = utils.requireArgs(['elementId', 'text'], opts);
238
+ await this.uiautomator2.jwproxy.command(
239
+ `/element/${elementId}/value`,
240
+ 'POST',
241
+ {
242
+ text,
243
+ replace: true,
244
+ }
245
+ );
246
+ }
247
+
248
+ // #region Internal Helpers
249
+
250
+ /**
251
+ * @param {any} s
252
+ * @returns {boolean}
253
+ */
254
+ function toBool(s) {
255
+ return _.isString(s) ? s.toLowerCase() === 'true' : !!s;
256
+ }
236
257
 
237
- mixin(ElementMixin);
258
+ // #endregion
238
259
 
239
260
  /**
240
- * @typedef {import('../uiautomator2').UiAutomator2Server} UiAutomator2Server
261
+ * @typedef {import('../driver').AndroidUiautomator2Driver} AndroidUiautomator2Driver
241
262
  */
@@ -0,0 +1,120 @@
1
+ import _ from 'lodash';
2
+ import {errors, PROTOCOLS} from 'appium/driver';
3
+ import {AndroidUiautomator2Driver} from '../driver';
4
+
5
+ const MOBILE_SCRIPT_NAME_PREFIX = 'mobile:';
6
+
7
+ /**
8
+ * @override
9
+ * @privateRemarks Because the "mobile" commands (execute methods) in this
10
+ * driver universally accept an options object, this method will _not_ call
11
+ * into `BaseDriver.executeMethod`.
12
+ * @this {AndroidUiautomator2Driver}
13
+ * @param {string} script
14
+ * @param {any[]} [args]
15
+ * @returns {Promise<any>}
16
+ */
17
+ export async function execute(script, args) {
18
+ const mobileScriptName = toExecuteMethodName(script);
19
+ const isWebContext = this.isWebContext();
20
+ if (mobileScriptName && isWebContext || !isWebContext) {
21
+ if (mobileScriptName) {
22
+ const executeMethodArgs = preprocessExecuteMethodArgs(args);
23
+ this.log.info(`Executing method '${mobileScriptName}'`);
24
+ return await this.executeMobile(mobileScriptName, executeMethodArgs);
25
+ }
26
+ // Just pass the script name through and let it fail with a proper error message
27
+ return await this.executeMobile(`${script}`, {});
28
+ }
29
+ const endpoint =
30
+ /** @type {import('appium-chromedriver').Chromedriver} */ (this.chromedriver).jwproxy
31
+ .downstreamProtocol === PROTOCOLS.MJSONWP
32
+ ? '/execute'
33
+ : '/execute/sync';
34
+ return await /** @type {import('appium-chromedriver').Chromedriver} */ (
35
+ this.chromedriver
36
+ ).jwproxy.command(endpoint, 'POST', {
37
+ script,
38
+ args,
39
+ });
40
+ }
41
+
42
+ /**
43
+ * @override
44
+ * @this {AndroidUiautomator2Driver}
45
+ * @param {string} script Must be of the form `mobile: <something>`, which
46
+ * differs from its parent class implementation.
47
+ * @param {import('@appium/types').StringRecord} [opts={}]
48
+ * @returns {Promise<any>}
49
+ */
50
+ export async function executeMobile(script, opts = {}) {
51
+ if (!(script in AndroidUiautomator2Driver.executeMethodMap)) {
52
+ const commandNames = _.map(
53
+ _.keys(AndroidUiautomator2Driver.executeMethodMap),
54
+ (value) => value.slice(8)
55
+ );
56
+ throw new errors.UnknownCommandError(
57
+ `Unknown mobile command "${script}". ` +
58
+ `Only ${commandNames.join(', ')} commands are supported.`
59
+ );
60
+ }
61
+ const methodName =
62
+ AndroidUiautomator2Driver.executeMethodMap[
63
+ /** @type {keyof import('../execute-method-map').Uiautomator2ExecuteMethodMap} */ (script)
64
+ ].command;
65
+
66
+ return await /** @type {(opts?: any) => Promise<unknown>} */ (this[methodName])(opts);
67
+ }
68
+
69
+ // #region Internal Helpers
70
+
71
+ /**
72
+ * Messages the arguments going into an execute method.
73
+ * @remarks A similar method is implemented in `appium-xcuitest-driver`, but it
74
+ * appears the methods in here handle unwrapping of `Element` objects, so we do
75
+ * not do that here.
76
+ * @param {readonly any[] | readonly [StringRecord] | Readonly<StringRecord>} [args]
77
+ * @internal
78
+ * @returns {StringRecord<unknown>}
79
+ */
80
+ function preprocessExecuteMethodArgs(args) {
81
+ if (_.isArray(args)) {
82
+ args = _.first(args);
83
+ }
84
+ const executeMethodArgs = /** @type {StringRecord<unknown>} */ (args ?? {});
85
+ /**
86
+ * Renames the deprecated `element` key to `elementId`. Historically,
87
+ * all of the pre-Execute-Method-Map execute methods accepted an `element` _or_ and `elementId` param.
88
+ * This assigns the `element` value to `elementId` if `elementId` is not already present.
89
+ */
90
+ if (!('elementId' in executeMethodArgs) && 'element' in executeMethodArgs) {
91
+ executeMethodArgs.elementId = executeMethodArgs.element;
92
+ delete executeMethodArgs.element;
93
+ }
94
+
95
+ return executeMethodArgs;
96
+ }
97
+
98
+ /**
99
+ * Type guard to check if a script is an execute method.
100
+ * @param {any} script
101
+ * @internal
102
+ * @returns {string?}
103
+ */
104
+ function toExecuteMethodName(script) {
105
+ return _.startsWith(script, MOBILE_SCRIPT_NAME_PREFIX)
106
+ ? script.replace(new RegExp(`${MOBILE_SCRIPT_NAME_PREFIX}\\s*`), `${MOBILE_SCRIPT_NAME_PREFIX} `)
107
+ : null;
108
+ }
109
+
110
+ // #endregion
111
+
112
+ /**
113
+ * @typedef {import('../uiautomator2').UiAutomator2Server} UiAutomator2Server
114
+ * @typedef {import('appium-adb').ADB} ADB
115
+ */
116
+
117
+ /**
118
+ * @template [T=any]
119
+ * @typedef {import('@appium/types').StringRecord<T>} StringRecord
120
+ */
@@ -1,7 +1,4 @@
1
- // @ts-check
2
-
3
1
  import CssConverter from '../css-converter';
4
- import {mixin} from './mixins';
5
2
 
6
3
  // we override the xpath search for this first-visible-child selector, which
7
4
  // looks like /*[@firstVisible="true"]
@@ -11,43 +8,40 @@ const MAGIC_SCROLLABLE_SEL = /\/\/\*\[@scrollable ?= ?('|")true\1\]/;
11
8
  const MAGIC_SCROLLABLE_BY = 'new UiSelector().scrollable(true)';
12
9
 
13
10
  /**
14
- * @type {import('./mixins').UIA2FindMixin}
15
- * @satisfies {import('@appium/types').ExternalDriver}
11
+ * @privateRemarks Overriding helpers.doFindElementOrEls functionality of appium-android-driver,
12
+ * this.element initialized in find.js of appium-android-drive.
13
+ *
14
+ * @this {AndroidUiautomator2Driver}
15
+ * @param {import('appium-android-driver').FindElementOpts} params
16
+ * @returns {Promise<Element | Element[]>}
16
17
  */
17
- const FindMixin = {
18
- /**
19
- * @privateRemarks Overriding helpers.doFindElementOrEls functionality of appium-android-driver,
20
- * this.element initialized in find.js of appium-android-drive.
21
- */
22
- async doFindElementOrEls(params) {
23
- const uiautomator2 = /** @type {import('../uiautomator2').UiAutomator2Server} */ (
24
- this.uiautomator2
25
- );
26
- if (params.strategy === 'xpath' && MAGIC_FIRST_VIS_CHILD_SEL.test(params.selector)) {
27
- let elementId = params.context;
28
- return /** @type {Element} */ (
29
- await uiautomator2.jwproxy.command(`/appium/element/${elementId}/first_visible`, 'GET', {})
30
- );
31
- }
32
- if (params.strategy === 'xpath' && MAGIC_SCROLLABLE_SEL.test(params.selector)) {
33
- params.strategy = '-android uiautomator';
34
- params.selector = MAGIC_SCROLLABLE_BY;
35
- }
36
- if (params.strategy === 'css selector') {
37
- params.strategy = '-android uiautomator';
38
- params.selector = new CssConverter(
39
- params.selector,
40
- this.opts.appPackage
41
- ).toUiAutomatorSelector();
42
- }
43
- return /** @type {Element|Element[]} */ (
44
- await uiautomator2.jwproxy.command(`/element${params.multiple ? 's' : ''}`, 'POST', params)
18
+ export async function doFindElementOrEls(params) {
19
+ const uiautomator2 = /** @type {import('../uiautomator2').UiAutomator2Server} */ (
20
+ this.uiautomator2
21
+ );
22
+ if (params.strategy === 'xpath' && MAGIC_FIRST_VIS_CHILD_SEL.test(params.selector)) {
23
+ let elementId = params.context;
24
+ return /** @type {Element} */ (
25
+ await uiautomator2.jwproxy.command(`/appium/element/${elementId}/first_visible`, 'GET', {})
45
26
  );
46
- },
47
- };
48
-
49
- mixin(FindMixin);
27
+ }
28
+ if (params.strategy === 'xpath' && MAGIC_SCROLLABLE_SEL.test(params.selector)) {
29
+ params.strategy = '-android uiautomator';
30
+ params.selector = MAGIC_SCROLLABLE_BY;
31
+ }
32
+ if (params.strategy === 'css selector') {
33
+ params.strategy = '-android uiautomator';
34
+ params.selector = new CssConverter(
35
+ params.selector,
36
+ this.opts.appPackage
37
+ ).toUiAutomatorSelector();
38
+ }
39
+ return /** @type {Element|Element[]} */ (
40
+ await uiautomator2.jwproxy.command(`/element${params.multiple ? 's' : ''}`, 'POST', params)
41
+ );
42
+ }
50
43
 
51
44
  /**
52
45
  * @typedef {import('@appium/types').Element} Element
46
+ * @typedef {import('../driver').AndroidUiautomator2Driver} AndroidUiautomator2Driver
53
47
  */