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.
Files changed (210) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/lib/commands/actions.d.ts +6 -224
  3. package/build/lib/commands/actions.d.ts.map +1 -1
  4. package/build/lib/commands/actions.js +306 -405
  5. package/build/lib/commands/actions.js.map +1 -1
  6. package/build/lib/commands/alert.d.ts +7 -9
  7. package/build/lib/commands/alert.d.ts.map +1 -1
  8. package/build/lib/commands/alert.js +24 -18
  9. package/build/lib/commands/alert.js.map +1 -1
  10. package/build/lib/commands/app-management.d.ts +7 -313
  11. package/build/lib/commands/app-management.d.ts.map +1 -1
  12. package/build/lib/commands/app-management.js +135 -293
  13. package/build/lib/commands/app-management.js.map +1 -1
  14. package/build/lib/commands/context.d.ts +8 -92
  15. package/build/lib/commands/context.d.ts.map +1 -1
  16. package/build/lib/commands/context.js +381 -439
  17. package/build/lib/commands/context.js.map +1 -1
  18. package/build/lib/commands/element.d.ts +8 -35
  19. package/build/lib/commands/element.d.ts.map +1 -1
  20. package/build/lib/commands/element.js +153 -136
  21. package/build/lib/commands/element.js.map +1 -1
  22. package/build/lib/commands/emu-console.d.ts +6 -48
  23. package/build/lib/commands/emu-console.d.ts.map +1 -1
  24. package/build/lib/commands/emu-console.js +19 -34
  25. package/build/lib/commands/emu-console.js.map +1 -1
  26. package/build/lib/commands/execute.d.ts +6 -5
  27. package/build/lib/commands/execute.d.ts.map +1 -1
  28. package/build/lib/commands/execute.js +77 -66
  29. package/build/lib/commands/execute.js.map +1 -1
  30. package/build/lib/commands/file-actions.d.ts +7 -128
  31. package/build/lib/commands/file-actions.d.ts.map +1 -1
  32. package/build/lib/commands/file-actions.js +183 -219
  33. package/build/lib/commands/file-actions.js.map +1 -1
  34. package/build/lib/commands/find.d.ts +8 -12
  35. package/build/lib/commands/find.d.ts.map +1 -1
  36. package/build/lib/commands/find.js +19 -23
  37. package/build/lib/commands/find.js.map +1 -1
  38. package/build/lib/commands/general.d.ts +9 -132
  39. package/build/lib/commands/general.d.ts.map +1 -1
  40. package/build/lib/commands/general.js +281 -312
  41. package/build/lib/commands/general.js.map +1 -1
  42. package/build/lib/commands/ime.d.ts +7 -10
  43. package/build/lib/commands/ime.d.ts.map +1 -1
  44. package/build/lib/commands/ime.js +47 -35
  45. package/build/lib/commands/ime.js.map +1 -1
  46. package/build/lib/commands/index.d.ts +27 -2
  47. package/build/lib/commands/index.d.ts.map +1 -1
  48. package/build/lib/commands/index.js +41 -19
  49. package/build/lib/commands/index.js.map +1 -1
  50. package/build/lib/commands/intent.d.ts +7 -417
  51. package/build/lib/commands/intent.d.ts.map +1 -1
  52. package/build/lib/commands/intent.js +104 -216
  53. package/build/lib/commands/intent.js.map +1 -1
  54. package/build/lib/commands/keyboard.d.ts +6 -5
  55. package/build/lib/commands/keyboard.d.ts.map +1 -1
  56. package/build/lib/commands/keyboard.js +16 -8
  57. package/build/lib/commands/keyboard.js.map +1 -1
  58. package/build/lib/commands/log.d.ts +7 -44
  59. package/build/lib/commands/log.d.ts.map +1 -1
  60. package/build/lib/commands/log.js +146 -108
  61. package/build/lib/commands/log.js.map +1 -1
  62. package/build/lib/commands/media-projection.d.ts +7 -143
  63. package/build/lib/commands/media-projection.d.ts.map +1 -1
  64. package/build/lib/commands/media-projection.js +113 -140
  65. package/build/lib/commands/media-projection.js.map +1 -1
  66. package/build/lib/commands/mixins.d.ts +740 -0
  67. package/build/lib/commands/mixins.d.ts.map +1 -0
  68. package/build/lib/commands/mixins.js +19 -0
  69. package/build/lib/commands/mixins.js.map +1 -0
  70. package/build/lib/commands/network.d.ts +7 -138
  71. package/build/lib/commands/network.d.ts.map +1 -1
  72. package/build/lib/commands/network.js +212 -254
  73. package/build/lib/commands/network.js.map +1 -1
  74. package/build/lib/commands/performance.d.ts +24 -70
  75. package/build/lib/commands/performance.d.ts.map +1 -1
  76. package/build/lib/commands/performance.js +144 -100
  77. package/build/lib/commands/performance.js.map +1 -1
  78. package/build/lib/commands/permissions.d.ts +8 -92
  79. package/build/lib/commands/permissions.d.ts.map +1 -1
  80. package/build/lib/commands/permissions.js +75 -87
  81. package/build/lib/commands/permissions.js.map +1 -1
  82. package/build/lib/commands/recordscreen.d.ts +7 -193
  83. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  84. package/build/lib/commands/recordscreen.js +151 -182
  85. package/build/lib/commands/recordscreen.js.map +1 -1
  86. package/build/lib/commands/shell.d.ts +7 -7
  87. package/build/lib/commands/shell.d.ts.map +1 -1
  88. package/build/lib/commands/shell.js +40 -33
  89. package/build/lib/commands/shell.js.map +1 -1
  90. package/build/lib/commands/streamscreen.d.ts +9 -103
  91. package/build/lib/commands/streamscreen.d.ts.map +1 -1
  92. package/build/lib/commands/streamscreen.js +261 -218
  93. package/build/lib/commands/streamscreen.js.map +1 -1
  94. package/build/lib/commands/system-bars.d.ts +22 -90
  95. package/build/lib/commands/system-bars.d.ts.map +1 -1
  96. package/build/lib/commands/system-bars.js +76 -74
  97. package/build/lib/commands/system-bars.js.map +1 -1
  98. package/build/lib/commands/touch.d.ts +10 -29
  99. package/build/lib/commands/touch.d.ts.map +1 -1
  100. package/build/lib/commands/touch.js +301 -285
  101. package/build/lib/commands/touch.js.map +1 -1
  102. package/build/lib/commands/types.d.ts +978 -0
  103. package/build/lib/commands/types.d.ts.map +1 -0
  104. package/build/lib/commands/types.js +3 -0
  105. package/build/lib/commands/types.js.map +1 -0
  106. package/build/lib/constraints.d.ts +291 -0
  107. package/build/lib/constraints.d.ts.map +1 -0
  108. package/build/lib/{desired-caps.js → constraints.js} +103 -102
  109. package/build/lib/constraints.js.map +1 -0
  110. package/build/lib/driver.d.ts +68 -37
  111. package/build/lib/driver.d.ts.map +1 -1
  112. package/build/lib/driver.js +123 -80
  113. package/build/lib/driver.js.map +1 -1
  114. package/build/lib/helpers/android.d.ts +164 -0
  115. package/build/lib/helpers/android.d.ts.map +1 -0
  116. package/build/lib/helpers/android.js +819 -0
  117. package/build/lib/helpers/android.js.map +1 -0
  118. package/build/lib/helpers/index.d.ts +7 -0
  119. package/build/lib/helpers/index.d.ts.map +1 -0
  120. package/build/lib/helpers/index.js +29 -0
  121. package/build/lib/helpers/index.js.map +1 -0
  122. package/build/lib/helpers/types.d.ts +121 -0
  123. package/build/lib/helpers/types.d.ts.map +1 -0
  124. package/build/lib/helpers/types.js +3 -0
  125. package/build/lib/helpers/types.js.map +1 -0
  126. package/build/lib/helpers/unlock.d.ts +32 -0
  127. package/build/lib/helpers/unlock.d.ts.map +1 -0
  128. package/build/lib/helpers/unlock.js +273 -0
  129. package/build/lib/helpers/unlock.js.map +1 -0
  130. package/build/lib/helpers/webview.d.ts +74 -0
  131. package/build/lib/helpers/webview.d.ts.map +1 -0
  132. package/build/lib/helpers/webview.js +421 -0
  133. package/build/lib/helpers/webview.js.map +1 -0
  134. package/build/lib/index.d.ts +9 -0
  135. package/build/lib/index.d.ts.map +1 -0
  136. package/build/lib/index.js +37 -0
  137. package/build/lib/index.js.map +1 -0
  138. package/build/lib/method-map.d.ts +0 -8
  139. package/build/lib/method-map.d.ts.map +1 -1
  140. package/build/lib/method-map.js +63 -74
  141. package/build/lib/method-map.js.map +1 -1
  142. package/build/lib/stubs.d.ts +0 -1
  143. package/build/lib/stubs.d.ts.map +1 -1
  144. package/build/lib/stubs.js +1 -0
  145. package/build/lib/stubs.js.map +1 -1
  146. package/build/lib/utils.d.ts +1 -1
  147. package/build/lib/utils.d.ts.map +1 -1
  148. package/lib/commands/actions.js +351 -464
  149. package/lib/commands/alert.js +27 -17
  150. package/lib/commands/app-management.js +156 -314
  151. package/lib/commands/context.js +457 -441
  152. package/lib/commands/element.js +201 -157
  153. package/lib/commands/emu-console.js +25 -45
  154. package/lib/commands/execute.js +106 -90
  155. package/lib/commands/file-actions.js +222 -240
  156. package/lib/commands/find.ts +103 -0
  157. package/lib/commands/general.js +327 -339
  158. package/lib/commands/ime.js +50 -34
  159. package/lib/commands/{index.js → index.ts} +20 -24
  160. package/lib/commands/intent.js +108 -249
  161. package/lib/commands/keyboard.js +20 -8
  162. package/lib/commands/log.js +172 -116
  163. package/lib/commands/media-projection.js +134 -161
  164. package/lib/commands/mixins.ts +966 -0
  165. package/lib/commands/network.js +252 -281
  166. package/lib/commands/performance.js +203 -132
  167. package/lib/commands/permissions.js +108 -109
  168. package/lib/commands/recordscreen.js +212 -209
  169. package/lib/commands/shell.js +51 -40
  170. package/lib/commands/streamscreen.js +355 -289
  171. package/lib/commands/system-bars.js +92 -83
  172. package/lib/commands/touch.js +357 -294
  173. package/lib/commands/types.ts +1097 -0
  174. package/lib/{desired-caps.js → constraints.ts} +106 -103
  175. package/lib/{driver.js → driver.ts} +278 -132
  176. package/lib/helpers/android.ts +1143 -0
  177. package/lib/helpers/index.ts +6 -0
  178. package/lib/helpers/types.ts +134 -0
  179. package/lib/helpers/unlock.ts +329 -0
  180. package/lib/helpers/webview.ts +582 -0
  181. package/lib/index.ts +18 -0
  182. package/lib/method-map.js +87 -98
  183. package/lib/stubs.ts +0 -1
  184. package/package.json +26 -19
  185. package/build/index.js +0 -51
  186. package/build/lib/android-helpers.d.ts +0 -136
  187. package/build/lib/android-helpers.d.ts.map +0 -1
  188. package/build/lib/android-helpers.js +0 -855
  189. package/build/lib/android-helpers.js.map +0 -1
  190. package/build/lib/commands/coverage.d.ts +0 -5
  191. package/build/lib/commands/coverage.d.ts.map +0 -1
  192. package/build/lib/commands/coverage.js +0 -19
  193. package/build/lib/commands/coverage.js.map +0 -1
  194. package/build/lib/desired-caps.d.ts +0 -353
  195. package/build/lib/desired-caps.d.ts.map +0 -1
  196. package/build/lib/desired-caps.js.map +0 -1
  197. package/build/lib/unlock-helpers.d.ts +0 -38
  198. package/build/lib/unlock-helpers.d.ts.map +0 -1
  199. package/build/lib/unlock-helpers.js +0 -266
  200. package/build/lib/unlock-helpers.js.map +0 -1
  201. package/build/lib/webview-helpers.d.ts +0 -224
  202. package/build/lib/webview-helpers.d.ts.map +0 -1
  203. package/build/lib/webview-helpers.js +0 -528
  204. package/build/lib/webview-helpers.js.map +0 -1
  205. package/index.js +0 -24
  206. package/lib/android-helpers.js +0 -983
  207. package/lib/commands/coverage.js +0 -18
  208. package/lib/commands/find.js +0 -82
  209. package/lib/unlock-helpers.js +0 -278
  210. package/lib/webview-helpers.js +0 -602
@@ -1,20 +1,31 @@
1
- import _ from 'lodash';
2
- import androidHelpers from '../android-helpers';
3
- import B from 'bluebird';
4
- import { errors, isErrorType } from 'appium/driver';
5
- import { asyncmap } from 'asyncbox';
6
- import { util } from '@appium/support';
1
+ // @ts-check
7
2
 
8
- let commands = {}, helpers = {}, extensions = {};
3
+ import {util} from '@appium/support';
4
+ import {errors, isErrorType} from 'appium/driver';
5
+ import {asyncmap} from 'asyncbox';
6
+ import B from 'bluebird';
7
+ import _ from 'lodash';
8
+ import androidHelpers from '../helpers/android';
9
+ import {mixin} from './mixins';
9
10
 
10
- function getCoordDefault (val) {
11
+ /**
12
+ *
13
+ * @param {number|null} [val]
14
+ * @returns {number}
15
+ */
16
+ function getCoordDefault(val) {
11
17
  // going the long way and checking for undefined and null since
12
18
  // we can't be assured `elId` is a string and not an int. Same
13
19
  // thing with destElement below.
14
20
  return util.hasValue(val) ? val : 0.5;
15
21
  }
16
22
 
17
- function getSwipeTouchDuration (waitGesture) {
23
+ /**
24
+ *
25
+ * @param {NonReleaseTouchAction} waitGesture
26
+ * @returns {number}
27
+ */
28
+ function getSwipeTouchDuration(waitGesture) {
18
29
  // the touch action api uses ms, we want seconds
19
30
  // 0.8 is the default time for the operation
20
31
  let duration = 0.8;
@@ -29,341 +40,393 @@ function getSwipeTouchDuration (waitGesture) {
29
40
  return duration;
30
41
  }
31
42
 
32
- commands.doTouchAction = async function doTouchAction (action, opts = {}) {
33
- const { element, x, y, count, ms, duration } = opts;
34
- // parseTouch precalculates absolute element positions
35
- // so there is no need to pass `element` to the affected gestures
36
- switch (action) {
37
- case 'tap':
38
- return await this.tap(null, x, y, count);
39
- case 'press':
40
- return await this.touchDown(null, x, y);
41
- case 'release':
42
- return await this.touchUp(element, x, y);
43
- case 'moveTo':
44
- return await this.touchMove(null, x, y);
45
- case 'wait':
46
- return await B.delay(ms);
47
- case 'longPress':
48
- return await this.touchLongClick(null, x, y, duration || 1000);
49
- case 'cancel':
50
- // TODO: clarify behavior of 'cancel' action and fix this
51
- this.log.warn('Cancel action currently has no effect');
52
- break;
53
- default:
54
- this.log.errorAndThrow(`unknown action ${action}`);
55
- }
56
- };
43
+ /**
44
+ * @type {import('./mixins').TouchMixin & ThisType<import('../driver').AndroidDriver>}
45
+ * @satisfies {import('@appium/types').ExternalDriver}
46
+ */
47
+ const TouchMixin = {
48
+ async doTouchAction(action, opts = {}) {
49
+ const {element, x, y, count, ms, duration} = opts;
50
+ // parseTouch precalculates absolute element positions
51
+ // so there is no need to pass `element` to the affected gestures
52
+ switch (action) {
53
+ case 'tap':
54
+ return await this.tap('', x, y, count);
55
+ case 'press':
56
+ return await this.touchDown('', /** @type {number} */ (x), /** @type {number} */ (y));
57
+ case 'release':
58
+ return await this.touchUp(
59
+ String(element),
60
+ /** @type {number} */ (x),
61
+ /** @type {number} */ (y)
62
+ );
63
+ case 'moveTo':
64
+ return await this.touchMove('', /** @type {number} */ (x), /** @type {number} */ (y));
65
+ case 'wait':
66
+ return await B.delay(/** @type {number} */ (ms));
67
+ case 'longPress':
68
+ return await this.touchLongClick(
69
+ '',
70
+ /** @type {number} */ (x),
71
+ /** @type {number} */ (y),
72
+ duration ?? 1000
73
+ );
74
+ case 'cancel':
75
+ // TODO: clarify behavior of 'cancel' action and fix this
76
+ this.log.warn('Cancel action currently has no effect');
77
+ break;
78
+ default:
79
+ this.log.errorAndThrow(`unknown action ${action}`);
80
+ }
81
+ },
57
82
 
58
- // drag is *not* press-move-release, so we need to translate
59
- // drag works fine for scroll, as well
60
- helpers.doTouchDrag = async function doTouchDrag (gestures) {
61
- let longPress = gestures[0];
62
- let moveTo = gestures[1];
63
- let startX = longPress.options.x || 0,
83
+ async doTouchDrag(gestures) {
84
+ let longPress = gestures[0];
85
+ let moveTo = gestures[1];
86
+ let startX = longPress.options.x || 0,
64
87
  startY = longPress.options.y || 0,
65
88
  endX = moveTo.options.x || 0,
66
89
  endY = moveTo.options.y || 0;
67
- if (longPress.options.element) {
68
- let {x, y} = await this.getLocationInView(longPress.options.element);
69
- startX += x || 0;
70
- startY += y || 0;
71
- }
72
- if (moveTo.options.element) {
73
- let {x, y} = await this.getLocationInView(moveTo.options.element);
74
- endX += x || 0;
75
- endY += y || 0;
76
- }
90
+ if (longPress.options.element) {
91
+ let {x, y} = await this.getLocationInView(longPress.options.element);
92
+ startX += x || 0;
93
+ startY += y || 0;
94
+ }
95
+ if (moveTo.options.element) {
96
+ let {x, y} = await this.getLocationInView(moveTo.options.element);
97
+ endX += x || 0;
98
+ endY += y || 0;
99
+ }
77
100
 
78
- let apiLevel = await this.adb.getApiLevel();
79
- // lollipop takes a little longer to get things rolling
80
- let duration = apiLevel >= 5 ? 2 : 1;
81
- // make sure that if the long press has a duration, we use it.
82
- if (longPress.options && longPress.options.duration) {
83
- duration = Math.max(longPress.options.duration / 1000, duration);
84
- }
101
+ let apiLevel = await /** @type {ADB} */ (this.adb).getApiLevel();
102
+ // lollipop takes a little longer to get things rolling
103
+ let duration = apiLevel >= 5 ? 2 : 1;
104
+ // make sure that if the long press has a duration, we use it.
105
+ if (longPress.options && longPress.options.duration) {
106
+ duration = Math.max(longPress.options.duration / 1000, duration);
107
+ }
85
108
 
86
- // `drag` will take care of whether there is an element or not at that level
87
- return await this.drag(startX, startY, endX, endY, duration, 1, longPress.options.element, moveTo.options.element);
88
- };
109
+ // `drag` will take care of whether there is an element or not at that level
110
+ return await this.drag(
111
+ startX,
112
+ startY,
113
+ endX,
114
+ endY,
115
+ duration,
116
+ 1,
117
+ longPress.options.element,
118
+ moveTo.options.element
119
+ );
120
+ },
89
121
 
90
- // Release gesture needs element or co-ordinates to release it from that position
91
- // or else release gesture is performed from center of the screen, so to fix it
92
- // This method sets co-ordinates/element to release gesture if it has no options set already.
93
- helpers.fixRelease = async function fixRelease (gestures) {
94
- let release = _.last(gestures);
95
- // sometimes there are no options
96
- release.options = release.options || {};
97
- // nothing to do if release options are already set
98
- if (release.options.element || (release.options.x && release.options.y)) {
99
- return;
100
- }
101
- // without coordinates, `release` uses the center of the screen, which,
102
- // generally speaking, is not what we want
103
- // therefore: loop backwards and use the last command with an element and/or
104
- // offset coordinates
105
- gestures = _.clone(gestures);
106
- let ref = null;
107
- for (let gesture of gestures.reverse()) {
108
- let opts = gesture.options;
109
- if (opts.element || (opts.x && opts.y)) {
110
- ref = gesture;
111
- break;
122
+ async fixRelease(gestures) {
123
+ let release = /** @type {import('./types').ReleaseTouchAction} */ (_.last(gestures));
124
+ // sometimes there are no options
125
+ release.options = release.options || {};
126
+ // nothing to do if release options are already set
127
+ if (release.options.element || (release.options.x && release.options.y)) {
128
+ return;
112
129
  }
113
- }
114
- if (ref) {
115
- let opts = ref.options;
116
- if (opts.element) {
117
- let loc = await this.getLocationInView(opts.element);
118
- if (opts.x && opts.y) {
119
- // this is an offset from the element
120
- release.options = {
121
- x: loc.x + opts.x,
122
- y: loc.y + opts.y
123
- };
130
+ // without coordinates, `release` uses the center of the screen, which,
131
+ // generally speaking, is not what we want
132
+ // therefore: loop backwards and use the last command with an element and/or
133
+ // offset coordinates
134
+ gestures = _.clone(gestures);
135
+ let ref = null;
136
+ for (let gesture of /** @type {NonReleaseTouchAction[]} */ (gestures.reverse())) {
137
+ let opts = gesture.options;
138
+ if (opts.element || (opts.x && opts.y)) {
139
+ ref = gesture;
140
+ break;
141
+ }
142
+ }
143
+ if (ref) {
144
+ let opts = ref.options;
145
+ if (opts.element) {
146
+ let loc = await this.getLocationInView(opts.element);
147
+ if (opts.x && opts.y) {
148
+ // this is an offset from the element
149
+ release.options = {
150
+ x: loc.x + opts.x,
151
+ y: loc.y + opts.y,
152
+ };
153
+ } else {
154
+ // this is the center of the element
155
+ let size = await this.getSize(opts.element);
156
+ release.options = {
157
+ x: loc.x + size.width / 2,
158
+ y: loc.y + size.height / 2,
159
+ };
160
+ }
124
161
  } else {
125
- // this is the center of the element
126
- let size = await this.getSize(opts.element);
127
- release.options = {
128
- x: loc.x + size.width / 2,
129
- y: loc.y + size.height / 2
130
- };
162
+ release.options = _.pick(opts, 'x', 'y');
131
163
  }
132
- } else {
133
- release.options = _.pick(opts, 'x', 'y');
134
164
  }
135
- }
136
- return release;
137
- };
165
+ return release;
166
+ },
138
167
 
139
- // Perform one gesture
140
- helpers.performGesture = async function performGesture (gesture) {
141
- try {
142
- return await this.doTouchAction(gesture.action, gesture.options || {});
143
- } catch (e) {
144
- // sometime the element is not available when releasing, retry without it
145
- if (isErrorType(e, errors.NoSuchElementError) && gesture.action === 'release' &&
146
- gesture.options.element) {
147
- delete gesture.options.element;
148
- this.log.debug(`retrying release without element opts: ${gesture.options}.`);
168
+ async performGesture(gesture) {
169
+ try {
149
170
  return await this.doTouchAction(gesture.action, gesture.options || {});
171
+ } catch (e) {
172
+ // sometime the element is not available when releasing, retry without it
173
+ if (
174
+ isErrorType(e, errors.NoSuchElementError) &&
175
+ gesture.action === 'release' &&
176
+ gesture.options?.element
177
+ ) {
178
+ delete gesture.options.element;
179
+ this.log.debug(`retrying release without element opts: ${gesture.options}.`);
180
+ return await this.doTouchAction(gesture.action, gesture.options || {});
181
+ }
182
+ throw e;
150
183
  }
151
- throw e;
152
- }
153
- };
184
+ },
154
185
 
155
- commands.getSwipeOptions = async function getSwipeOptions (gestures, touchCount = 1) {
156
- let startX = getCoordDefault(gestures[0].options.x),
186
+ async getSwipeOptions(gestures, touchCount = 1) {
187
+ let startX = getCoordDefault(gestures[0].options.x),
157
188
  startY = getCoordDefault(gestures[0].options.y),
158
189
  endX = getCoordDefault(gestures[2].options.x),
159
190
  endY = getCoordDefault(gestures[2].options.y),
160
191
  duration = getSwipeTouchDuration(gestures[1]),
161
- element = gestures[0].options.element,
192
+ element = /** @type {string} */ (gestures[0].options.element),
162
193
  destElement = gestures[2].options.element || gestures[0].options.element;
163
194
 
164
- // there's no destination element handling in bootstrap and since it applies to all platforms, we handle it here
165
- if (util.hasValue(destElement)) {
166
- let locResult = await this.getLocationInView(destElement);
167
- let sizeResult = await this.getSize(destElement);
168
- let offsetX = (Math.abs(endX) < 1 && Math.abs(endX) > 0) ? sizeResult.width * endX : endX;
169
- let offsetY = (Math.abs(endY) < 1 && Math.abs(endY) > 0) ? sizeResult.height * endY : endY;
170
- endX = locResult.x + offsetX;
171
- endY = locResult.y + offsetY;
172
- // if the target element was provided, the coordinates for the destination need to be relative to it.
173
- if (util.hasValue(element)) {
174
- let firstElLocation = await this.getLocationInView(element);
175
- endX -= firstElLocation.x;
176
- endY -= firstElLocation.y;
195
+ // there's no destination element handling in bootstrap and since it applies to all platforms, we handle it here
196
+ if (util.hasValue(destElement)) {
197
+ let locResult = await this.getLocationInView(destElement);
198
+ let sizeResult = await this.getSize(destElement);
199
+ let offsetX = Math.abs(endX) < 1 && Math.abs(endX) > 0 ? sizeResult.width * endX : endX;
200
+ let offsetY = Math.abs(endY) < 1 && Math.abs(endY) > 0 ? sizeResult.height * endY : endY;
201
+ endX = locResult.x + offsetX;
202
+ endY = locResult.y + offsetY;
203
+ // if the target element was provided, the coordinates for the destination need to be relative to it.
204
+ if (util.hasValue(element)) {
205
+ let firstElLocation = await this.getLocationInView(element);
206
+ endX -= firstElLocation.x;
207
+ endY -= firstElLocation.y;
208
+ }
177
209
  }
178
- }
179
- // clients are responsible to use these options correctly
180
- return {startX, startY, endX, endY, duration, touchCount, element};
181
- };
210
+ // clients are responsible to use these options correctly
211
+ return {startX, startY, endX, endY, duration, touchCount, element};
212
+ },
182
213
 
183
- commands.performTouch = async function performTouch (gestures) {
184
- // press-wait-moveTo-release is `swipe`, so use native method
185
- if (gestures.length === 4 &&
214
+ async performTouch(gestures) {
215
+ // press-wait-moveTo-release is `swipe`, so use native method
216
+ if (
217
+ gestures.length === 4 &&
186
218
  gestures[0].action === 'press' &&
187
219
  gestures[1].action === 'wait' &&
188
220
  gestures[2].action === 'moveTo' &&
189
- gestures[3].action === 'release') {
221
+ gestures[3].action === 'release'
222
+ ) {
223
+ let swipeOpts = await this.getSwipeOptions(
224
+ /** @type {import('./types').SwipeAction} */ (gestures)
225
+ );
226
+ return await this.swipe(
227
+ swipeOpts.startX,
228
+ swipeOpts.startY,
229
+ swipeOpts.endX,
230
+ swipeOpts.endY,
231
+ swipeOpts.duration,
232
+ swipeOpts.touchCount,
233
+ swipeOpts.element
234
+ );
235
+ }
236
+ let actions = /** @type {(import('./types').TouchActionKind|TouchAction)[]} */ (
237
+ _.map(gestures, 'action')
238
+ );
190
239
 
191
- let swipeOpts = await this.getSwipeOptions(gestures);
192
- return await this.swipe(swipeOpts.startX, swipeOpts.startY, swipeOpts.endX,
193
- swipeOpts.endY, swipeOpts.duration, swipeOpts.touchCount,
194
- swipeOpts.element);
195
- }
196
- let actions = _.map(gestures, 'action');
240
+ if (actions[0] === 'longPress' && actions[1] === 'moveTo' && actions[2] === 'release') {
241
+ // some things are special
242
+ return await this.doTouchDrag(/** @type {import('./types').TouchDragAction} */ (gestures));
243
+ } else {
244
+ if (actions.length === 2) {
245
+ // `press` without a wait is too slow and gets interpretted as a `longPress`
246
+ if (_.head(actions) === 'press' && _.last(actions) === 'release') {
247
+ actions[0] = 'tap';
248
+ gestures[0].action = 'tap';
249
+ }
197
250
 
198
- if (actions[0] === 'longPress' && actions[1] === 'moveTo' && actions[2] === 'release') {
199
- // some things are special
200
- return await this.doTouchDrag(gestures);
201
- } else {
202
- if (actions.length === 2) {
203
- // `press` without a wait is too slow and gets interpretted as a `longPress`
204
- if (_.head(actions) === 'press' && _.last(actions) === 'release') {
205
- actions[0] = 'tap';
206
- gestures[0].action = 'tap';
207
- }
251
+ // the `longPress` and `tap` methods release on their own
252
+ if (
253
+ (_.head(actions) === 'tap' || _.head(actions) === 'longPress') &&
254
+ _.last(actions) === 'release'
255
+ ) {
256
+ gestures.pop();
257
+ actions.pop();
258
+ }
259
+ } else {
260
+ // longpress followed by anything other than release should become a press and wait
261
+ if (actions[0] === 'longPress') {
262
+ actions = ['press', 'wait', ...actions.slice(1)];
208
263
 
209
- // the `longPress` and `tap` methods release on their own
210
- if ((_.head(actions) === 'tap' || _.head(actions) === 'longPress') && _.last(actions) === 'release') {
211
- gestures.pop();
212
- actions.pop();
264
+ let press = /** @type {NonReleaseTouchAction} */ (gestures.shift());
265
+ press.action = 'press';
266
+ /** @type {NonReleaseTouchAction} */
267
+ let wait = {
268
+ action: 'wait',
269
+ options: {ms: press.options.duration || 1000},
270
+ };
271
+ delete press.options.duration;
272
+ gestures = [press, wait, ...gestures];
273
+ }
213
274
  }
214
- } else {
215
- // longpress followed by anything other than release should become a press and wait
216
- if (actions[0] === 'longPress') {
217
- actions = ['press', 'wait', ...actions.slice(1)];
218
275
 
219
- let press = gestures.shift();
220
- press.action = 'press';
221
- let wait = {
222
- action: 'wait',
223
- options: {ms: press.options.duration || 1000}
224
- };
225
- delete press.options.duration;
226
- gestures = [press, wait, ...gestures];
276
+ let fixedGestures = await this.parseTouch(gestures, false);
277
+ // fix release action then perform all actions
278
+ if (actions[actions.length - 1] === 'release') {
279
+ actions[actions.length - 1] = /** @type {TouchAction} */ (await this.fixRelease(gestures));
280
+ }
281
+ for (let g of fixedGestures) {
282
+ await this.performGesture(g);
227
283
  }
228
284
  }
285
+ },
229
286
 
230
- let fixedGestures = await this.parseTouch(gestures, false);
231
- // fix release action then perform all actions
232
- if (actions[actions.length - 1] === 'release') {
233
- actions[actions.length - 1] = await this.fixRelease(gestures);
234
- }
235
- for (let g of fixedGestures) {
236
- await this.performGesture(g);
287
+ async parseTouch(gestures, multi) {
288
+ // because multi-touch releases at the end by default
289
+ if (multi && /** @type {TouchAction} */ (_.last(gestures)).action === 'release') {
290
+ gestures.pop();
237
291
  }
238
- }
239
- };
240
292
 
241
- helpers.parseTouch = async function parseTouch (gestures, multi) {
242
- // because multi-touch releases at the end by default
243
- if (multi && _.last(gestures).action === 'release') {
244
- gestures.pop();
245
- }
293
+ let touchStateObjects = await asyncmap(
294
+ gestures,
295
+ async (gesture) => {
296
+ let options = gesture.options || {};
297
+ if (_.includes(['press', 'moveTo', 'tap', 'longPress'], gesture.action)) {
298
+ options.offset = false;
299
+ let elementId = gesture.options.element;
300
+ if (elementId) {
301
+ let pos = await this.getLocationInView(elementId);
302
+ if (gesture.options.x || gesture.options.y) {
303
+ options.x = pos.x + (gesture.options.x || 0);
304
+ options.y = pos.y + (gesture.options.y || 0);
305
+ } else {
306
+ const {width, height} = await this.getSize(elementId);
307
+ options.x = pos.x + width / 2;
308
+ options.y = pos.y + height / 2;
309
+ }
310
+ let touchStateObject = {
311
+ action: gesture.action,
312
+ options,
313
+ timeOffset: 0.005,
314
+ };
315
+ return touchStateObject;
316
+ } else {
317
+ options.x = gesture.options.x || 0;
318
+ options.y = gesture.options.y || 0;
246
319
 
247
- let touchStateObjects = await asyncmap(gestures, async (gesture) => {
248
- let options = gesture.options || {};
249
- if (_.includes(['press', 'moveTo', 'tap', 'longPress'], gesture.action)) {
250
- options.offset = false;
251
- let elementId = gesture.options.element;
252
- if (elementId) {
253
- let pos = await this.getLocationInView(elementId);
254
- if (gesture.options.x || gesture.options.y) {
255
- options.x = pos.x + (gesture.options.x || 0);
256
- options.y = pos.y + (gesture.options.y || 0);
320
+ let touchStateObject = {
321
+ action: gesture.action,
322
+ options,
323
+ timeOffset: 0.005,
324
+ };
325
+ return touchStateObject;
326
+ }
257
327
  } else {
258
- const {width, height} = await this.getSize(elementId);
259
- options.x = pos.x + (width / 2);
260
- options.y = pos.y + (height / 2);
328
+ let offset = 0.005;
329
+ if (gesture.action === 'wait') {
330
+ options = gesture.options;
331
+ offset = parseInt(gesture.options.ms, 10) / 1000;
332
+ }
333
+ let touchStateObject = {
334
+ action: gesture.action,
335
+ options,
336
+ timeOffset: offset,
337
+ };
338
+ return touchStateObject;
261
339
  }
262
- let touchStateObject = {
263
- action: gesture.action,
264
- options,
265
- timeOffset: 0.005,
266
- };
267
- return touchStateObject;
268
- } else {
269
- options.x = (gesture.options.x || 0);
270
- options.y = (gesture.options.y || 0);
271
-
272
- let touchStateObject = {
273
- action: gesture.action,
274
- options,
275
- timeOffset: 0.005,
276
- };
277
- return touchStateObject;
340
+ },
341
+ false
342
+ );
343
+ // we need to change the time (which is now an offset)
344
+ // and the position (which may be an offset)
345
+ let prevPos = null,
346
+ time = 0;
347
+ for (let state of touchStateObjects) {
348
+ if (_.isUndefined(state.options.x) && _.isUndefined(state.options.y) && prevPos !== null) {
349
+ // this happens with wait
350
+ state.options.x = prevPos.x;
351
+ state.options.y = prevPos.y;
278
352
  }
279
- } else {
280
- let offset = 0.005;
281
- if (gesture.action === 'wait') {
282
- options = gesture.options;
283
- offset = (parseInt(gesture.options.ms, 10) / 1000);
353
+ if (state.options.offset && prevPos) {
354
+ // the current position is an offset
355
+ state.options.x += prevPos.x;
356
+ state.options.y += prevPos.y;
357
+ }
358
+ delete state.options.offset;
359
+ if (!_.isUndefined(state.options.x) && !_.isUndefined(state.options.y)) {
360
+ prevPos = state.options;
284
361
  }
285
- let touchStateObject = {
286
- action: gesture.action,
287
- options,
288
- timeOffset: offset,
289
- };
290
- return touchStateObject;
291
- }
292
- }, false);
293
- // we need to change the time (which is now an offset)
294
- // and the position (which may be an offset)
295
- let prevPos = null,
296
- time = 0;
297
- for (let state of touchStateObjects) {
298
- if (_.isUndefined(state.options.x) && _.isUndefined(state.options.y) && prevPos !== null) {
299
- // this happens with wait
300
- state.options.x = prevPos.x;
301
- state.options.y = prevPos.y;
302
- }
303
- if (state.options.offset && prevPos) {
304
- // the current position is an offset
305
- state.options.x += prevPos.x;
306
- state.options.y += prevPos.y;
307
- }
308
- delete state.options.offset;
309
- if (!_.isUndefined(state.options.x) && !_.isUndefined(state.options.y)) {
310
- prevPos = state.options;
311
- }
312
362
 
313
- if (multi) {
314
- let timeOffset = state.timeOffset;
315
- time += timeOffset;
316
- state.time = androidHelpers.truncateDecimals(time, 3);
363
+ if (multi) {
364
+ let timeOffset = state.timeOffset;
365
+ time += timeOffset;
366
+ state.time = androidHelpers.truncateDecimals(time, 3);
317
367
 
318
- // multi gestures require 'touch' rather than 'options'
319
- if (!_.isUndefined(state.options.x) && !_.isUndefined(state.options.y)) {
320
- state.touch = {
321
- x: state.options.x,
322
- y: state.options.y
323
- };
368
+ // multi gestures require 'touch' rather than 'options'
369
+ if (!_.isUndefined(state.options.x) && !_.isUndefined(state.options.y)) {
370
+ state.touch = {
371
+ x: state.options.x,
372
+ y: state.options.y,
373
+ };
374
+ }
375
+ delete state.options;
324
376
  }
325
- delete state.options;
377
+ delete state.timeOffset;
326
378
  }
327
- delete state.timeOffset;
328
- }
329
- return touchStateObjects;
330
- };
379
+ return touchStateObjects;
380
+ },
331
381
 
382
+ async performMultiAction(actions, elementId) {
383
+ // Android needs at least two actions to be able to perform a multi pointer gesture
384
+ if (actions.length === 1) {
385
+ throw new Error(
386
+ 'Multi Pointer Gestures need at least two actions. ' +
387
+ 'Use Touch Actions for a single action.'
388
+ );
389
+ }
332
390
 
333
- commands.performMultiAction = async function performMultiAction (actions, elementId) {
334
- // Android needs at least two actions to be able to perform a multi pointer gesture
335
- if (actions.length === 1) {
336
- throw new Error('Multi Pointer Gestures need at least two actions. ' +
337
- 'Use Touch Actions for a single action.');
338
- }
391
+ const states = await asyncmap(
392
+ actions,
393
+ async (action) => await this.parseTouch(action, true),
394
+ false
395
+ );
339
396
 
340
- const states = await asyncmap(actions, async (action) => await this.parseTouch(action, true), false);
397
+ return await this.doPerformMultiAction(elementId, states);
398
+ },
341
399
 
342
- return await this.doPerformMultiAction(elementId, states);
400
+ async doPerformMultiAction(elementId, states) {
401
+ let opts;
402
+ if (elementId) {
403
+ opts = {
404
+ elementId,
405
+ actions: states,
406
+ };
407
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction(
408
+ 'element:performMultiPointerGesture',
409
+ opts
410
+ );
411
+ } else {
412
+ opts = {
413
+ actions: states,
414
+ };
415
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction(
416
+ 'performMultiPointerGesture',
417
+ opts
418
+ );
419
+ }
420
+ },
343
421
  };
344
422
 
423
+ mixin(TouchMixin);
424
+
425
+ export default TouchMixin;
426
+
345
427
  /**
346
- * Reason for isolating doPerformMultiAction from performMultiAction is for reusing performMultiAction
347
- * across android-drivers (like appium-uiautomator2-driver) and to avoid code duplication.
348
- * Other android-drivers (like appium-uiautomator2-driver) need to override doPerformMultiAction
349
- * to facilitate performMultiAction.
428
+ * @typedef {import('appium-adb').ADB} ADB
429
+ * @typedef {import('./types').TouchAction} TouchAction
430
+ * @typedef {import('./types').NonReleaseTouchAction} NonReleaseTouchAction
431
+ * @typedef {import('../bootstrap').AndroidBootstrap} AndroidBootstrap
350
432
  */
351
- commands.doPerformMultiAction = async function doPerformMultiAction (elementId, states) {
352
- let opts;
353
- if (elementId) {
354
- opts = {
355
- elementId,
356
- actions: states
357
- };
358
- return await this.bootstrap.sendAction('element:performMultiPointerGesture', opts);
359
- } else {
360
- opts = {
361
- actions: states
362
- };
363
- return await this.bootstrap.sendAction('performMultiPointerGesture', opts);
364
- }
365
- };
366
-
367
- Object.assign(extensions, commands, helpers);
368
- export { commands, helpers };
369
- export default extensions;