appium-android-driver 5.14.7 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/build/lib/commands/actions.d.ts +6 -224
- package/build/lib/commands/actions.d.ts.map +1 -1
- package/build/lib/commands/actions.js +306 -405
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/alert.d.ts +7 -9
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +24 -18
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +7 -313
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +135 -293
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/context.d.ts +8 -92
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +381 -439
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/element.d.ts +8 -35
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +153 -136
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/emu-console.d.ts +6 -48
- package/build/lib/commands/emu-console.d.ts.map +1 -1
- package/build/lib/commands/emu-console.js +19 -34
- package/build/lib/commands/emu-console.js.map +1 -1
- package/build/lib/commands/execute.d.ts +6 -5
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +77 -66
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts +7 -128
- package/build/lib/commands/file-actions.d.ts.map +1 -1
- package/build/lib/commands/file-actions.js +183 -219
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +8 -12
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +19 -23
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +9 -132
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +281 -312
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/ime.d.ts +7 -10
- package/build/lib/commands/ime.d.ts.map +1 -1
- package/build/lib/commands/ime.js +47 -35
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/index.d.ts +27 -2
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/commands/index.js +41 -19
- package/build/lib/commands/index.js.map +1 -1
- package/build/lib/commands/intent.d.ts +7 -417
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +104 -216
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +6 -5
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +16 -8
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/log.d.ts +7 -44
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +146 -108
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +7 -143
- package/build/lib/commands/media-projection.d.ts.map +1 -1
- package/build/lib/commands/media-projection.js +113 -140
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/mixins.d.ts +740 -0
- package/build/lib/commands/mixins.d.ts.map +1 -0
- package/build/lib/commands/mixins.js +19 -0
- package/build/lib/commands/mixins.js.map +1 -0
- package/build/lib/commands/network.d.ts +7 -138
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +212 -254
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/performance.d.ts +24 -70
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +144 -100
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +8 -92
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +75 -87
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +7 -193
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +151 -182
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/shell.d.ts +7 -7
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +40 -33
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +9 -103
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +261 -218
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts +22 -90
- package/build/lib/commands/system-bars.d.ts.map +1 -1
- package/build/lib/commands/system-bars.js +76 -74
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/touch.d.ts +10 -29
- package/build/lib/commands/touch.d.ts.map +1 -1
- package/build/lib/commands/touch.js +301 -285
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/commands/types.d.ts +978 -0
- package/build/lib/commands/types.d.ts.map +1 -0
- package/build/lib/commands/types.js +3 -0
- package/build/lib/commands/types.js.map +1 -0
- package/build/lib/constraints.d.ts +291 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/{desired-caps.js → constraints.js} +103 -102
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/driver.d.ts +68 -37
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +123 -80
- package/build/lib/driver.js.map +1 -1
- package/build/lib/helpers/android.d.ts +164 -0
- package/build/lib/helpers/android.d.ts.map +1 -0
- package/build/lib/helpers/android.js +819 -0
- package/build/lib/helpers/android.js.map +1 -0
- package/build/lib/helpers/index.d.ts +7 -0
- package/build/lib/helpers/index.d.ts.map +1 -0
- package/build/lib/helpers/index.js +29 -0
- package/build/lib/helpers/index.js.map +1 -0
- package/build/lib/helpers/types.d.ts +121 -0
- package/build/lib/helpers/types.d.ts.map +1 -0
- package/build/lib/helpers/types.js +3 -0
- package/build/lib/helpers/types.js.map +1 -0
- package/build/lib/helpers/unlock.d.ts +32 -0
- package/build/lib/helpers/unlock.d.ts.map +1 -0
- package/build/lib/helpers/unlock.js +273 -0
- package/build/lib/helpers/unlock.js.map +1 -0
- package/build/lib/helpers/webview.d.ts +74 -0
- package/build/lib/helpers/webview.d.ts.map +1 -0
- package/build/lib/helpers/webview.js +421 -0
- package/build/lib/helpers/webview.js.map +1 -0
- package/build/lib/index.d.ts +9 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +37 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/method-map.d.ts +0 -8
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +63 -74
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/stubs.d.ts +0 -1
- package/build/lib/stubs.d.ts.map +1 -1
- package/build/lib/stubs.js +1 -0
- package/build/lib/stubs.js.map +1 -1
- package/build/lib/utils.d.ts +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/lib/commands/actions.js +351 -464
- package/lib/commands/alert.js +27 -17
- package/lib/commands/app-management.js +156 -314
- package/lib/commands/context.js +457 -441
- package/lib/commands/element.js +201 -157
- package/lib/commands/emu-console.js +25 -45
- package/lib/commands/execute.js +106 -90
- package/lib/commands/file-actions.js +222 -240
- package/lib/commands/find.ts +103 -0
- package/lib/commands/general.js +327 -339
- package/lib/commands/ime.js +50 -34
- package/lib/commands/{index.js → index.ts} +20 -24
- package/lib/commands/intent.js +108 -249
- package/lib/commands/keyboard.js +20 -8
- package/lib/commands/log.js +172 -116
- package/lib/commands/media-projection.js +134 -161
- package/lib/commands/mixins.ts +966 -0
- package/lib/commands/network.js +252 -281
- package/lib/commands/performance.js +203 -132
- package/lib/commands/permissions.js +108 -109
- package/lib/commands/recordscreen.js +212 -209
- package/lib/commands/shell.js +51 -40
- package/lib/commands/streamscreen.js +355 -289
- package/lib/commands/system-bars.js +92 -83
- package/lib/commands/touch.js +357 -294
- package/lib/commands/types.ts +1097 -0
- package/lib/{desired-caps.js → constraints.ts} +106 -103
- package/lib/{driver.js → driver.ts} +278 -132
- package/lib/helpers/android.ts +1143 -0
- package/lib/helpers/index.ts +6 -0
- package/lib/helpers/types.ts +134 -0
- package/lib/helpers/unlock.ts +329 -0
- package/lib/helpers/webview.ts +582 -0
- package/lib/index.ts +18 -0
- package/lib/method-map.js +87 -98
- package/lib/stubs.ts +0 -1
- package/package.json +26 -19
- package/build/index.js +0 -51
- package/build/lib/android-helpers.d.ts +0 -136
- package/build/lib/android-helpers.d.ts.map +0 -1
- package/build/lib/android-helpers.js +0 -855
- package/build/lib/android-helpers.js.map +0 -1
- package/build/lib/commands/coverage.d.ts +0 -5
- package/build/lib/commands/coverage.d.ts.map +0 -1
- package/build/lib/commands/coverage.js +0 -19
- package/build/lib/commands/coverage.js.map +0 -1
- package/build/lib/desired-caps.d.ts +0 -353
- package/build/lib/desired-caps.d.ts.map +0 -1
- package/build/lib/desired-caps.js.map +0 -1
- package/build/lib/unlock-helpers.d.ts +0 -38
- package/build/lib/unlock-helpers.d.ts.map +0 -1
- package/build/lib/unlock-helpers.js +0 -266
- package/build/lib/unlock-helpers.js.map +0 -1
- package/build/lib/webview-helpers.d.ts +0 -224
- package/build/lib/webview-helpers.d.ts.map +0 -1
- package/build/lib/webview-helpers.js +0 -528
- package/build/lib/webview-helpers.js.map +0 -1
- package/index.js +0 -24
- package/lib/android-helpers.js +0 -983
- package/lib/commands/coverage.js +0 -18
- package/lib/commands/find.js +0 -82
- package/lib/unlock-helpers.js +0 -278
- package/lib/webview-helpers.js +0 -602
package/lib/commands/touch.js
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
};
|
|
165
|
+
return release;
|
|
166
|
+
},
|
|
138
167
|
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
152
|
-
}
|
|
153
|
-
};
|
|
184
|
+
},
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
210
|
+
// clients are responsible to use these options correctly
|
|
211
|
+
return {startX, startY, endX, endY, duration, touchCount, element};
|
|
212
|
+
},
|
|
182
213
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
231
|
-
//
|
|
232
|
-
if (
|
|
233
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
options
|
|
283
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
363
|
+
if (multi) {
|
|
364
|
+
let timeOffset = state.timeOffset;
|
|
365
|
+
time += timeOffset;
|
|
366
|
+
state.time = androidHelpers.truncateDecimals(time, 3);
|
|
317
367
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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.
|
|
377
|
+
delete state.timeOffset;
|
|
326
378
|
}
|
|
327
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
391
|
+
const states = await asyncmap(
|
|
392
|
+
actions,
|
|
393
|
+
async (action) => await this.parseTouch(action, true),
|
|
394
|
+
false
|
|
395
|
+
);
|
|
339
396
|
|
|
340
|
-
|
|
397
|
+
return await this.doPerformMultiAction(elementId, states);
|
|
398
|
+
},
|
|
341
399
|
|
|
342
|
-
|
|
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
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
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;
|