selenium-webdriver 4.0.0-alpha.7 → 4.0.0-beta.3
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/CHANGES.md +56 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +2 -0
- package/chrome.js +69 -79
- package/chromium.js +231 -164
- package/{lib/atoms/make-atoms-module.js → devtools/CDPConnection.js} +15 -9
- package/edge.js +104 -146
- package/example/chrome_android.js +45 -0
- package/example/chrome_mobile_emulation.js +46 -0
- package/example/firefox_channels.js +84 -0
- package/example/google_search.js +50 -0
- package/example/google_search_test.js +70 -0
- package/example/headless.js +63 -0
- package/example/logging.js +64 -0
- package/firefox.js +199 -200
- package/http/index.js +128 -116
- package/http/util.js +56 -64
- package/ie.js +180 -104
- package/index.js +246 -199
- package/io/exec.js +35 -40
- package/io/index.js +146 -157
- package/io/zip.js +75 -66
- package/lib/atoms/find-elements.js +123 -0
- package/lib/atoms/get-attribute.js +75 -59
- package/lib/atoms/is-displayed.js +72 -72
- package/lib/atoms/mutation-listener.js +55 -0
- package/lib/by.js +192 -65
- package/lib/capabilities.js +76 -86
- package/lib/command.js +19 -35
- package/lib/error.js +134 -168
- package/lib/http.js +308 -232
- package/lib/input.js +192 -453
- package/lib/logging.js +126 -137
- package/lib/promise.js +53 -71
- package/lib/proxy.js +31 -41
- package/lib/session.js +12 -15
- package/lib/symbols.js +3 -4
- package/lib/until.js +162 -160
- package/lib/webdriver.js +1014 -549
- package/net/index.js +30 -36
- package/net/portprober.js +30 -143
- package/opera.js +406 -0
- package/package.json +15 -7
- package/proxy.js +2 -2
- package/remote/index.js +189 -187
- package/safari.js +40 -49
- package/testing/index.js +504 -0
- package/lib/README +0 -5
- package/lib/actions.js +0 -609
package/lib/input.js
CHANGED
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
// specific language governing permissions and limitations
|
|
16
16
|
// under the License.
|
|
17
17
|
|
|
18
|
-
'use strict'
|
|
18
|
+
'use strict'
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @fileoverview Defines types related to user input with the WebDriver API.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const promise = require('./promise')
|
|
25
|
-
const {Command,
|
|
24
|
+
const promise = require('./promise')
|
|
25
|
+
const { Command, Name } = require('./command')
|
|
26
26
|
const {
|
|
27
27
|
InvalidArgumentError,
|
|
28
28
|
UnknownCommandError,
|
|
29
|
-
UnsupportedOperationError
|
|
30
|
-
} = require('./error')
|
|
29
|
+
UnsupportedOperationError,
|
|
30
|
+
} = require('./error')
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Enumeration of the buttons used in the advanced interactions API.
|
|
@@ -36,10 +36,8 @@ const {
|
|
|
36
36
|
const Button = {
|
|
37
37
|
LEFT: 0,
|
|
38
38
|
MIDDLE: 1,
|
|
39
|
-
RIGHT: 2
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
RIGHT: 2,
|
|
40
|
+
}
|
|
43
41
|
|
|
44
42
|
/**
|
|
45
43
|
* Representations of pressable keys that aren't text. These are stored in
|
|
@@ -50,69 +48,69 @@ const Button = {
|
|
|
50
48
|
* @see <https://www.w3.org/TR/webdriver/#keyboard-actions>
|
|
51
49
|
*/
|
|
52
50
|
const Key = {
|
|
53
|
-
NULL:
|
|
54
|
-
CANCEL:
|
|
55
|
-
HELP:
|
|
56
|
-
BACK_SPACE:
|
|
57
|
-
TAB:
|
|
58
|
-
CLEAR:
|
|
59
|
-
RETURN:
|
|
60
|
-
ENTER:
|
|
61
|
-
SHIFT:
|
|
62
|
-
CONTROL:
|
|
63
|
-
ALT:
|
|
64
|
-
PAUSE:
|
|
65
|
-
ESCAPE:
|
|
66
|
-
SPACE:
|
|
67
|
-
PAGE_UP:
|
|
68
|
-
PAGE_DOWN:
|
|
69
|
-
END:
|
|
70
|
-
HOME:
|
|
71
|
-
ARROW_LEFT:
|
|
72
|
-
LEFT:
|
|
73
|
-
ARROW_UP:
|
|
74
|
-
UP:
|
|
75
|
-
ARROW_RIGHT:
|
|
76
|
-
RIGHT:
|
|
77
|
-
ARROW_DOWN:
|
|
78
|
-
DOWN:
|
|
79
|
-
INSERT:
|
|
80
|
-
DELETE:
|
|
81
|
-
SEMICOLON:
|
|
82
|
-
EQUALS:
|
|
83
|
-
|
|
84
|
-
NUMPAD0:
|
|
85
|
-
NUMPAD1:
|
|
86
|
-
NUMPAD2:
|
|
87
|
-
NUMPAD3:
|
|
88
|
-
NUMPAD4:
|
|
89
|
-
NUMPAD5:
|
|
90
|
-
NUMPAD6:
|
|
91
|
-
NUMPAD7:
|
|
92
|
-
NUMPAD8:
|
|
93
|
-
NUMPAD9:
|
|
94
|
-
MULTIPLY:
|
|
95
|
-
ADD:
|
|
96
|
-
SEPARATOR:
|
|
97
|
-
SUBTRACT:
|
|
98
|
-
DECIMAL:
|
|
99
|
-
DIVIDE:
|
|
100
|
-
|
|
101
|
-
F1:
|
|
102
|
-
F2:
|
|
103
|
-
F3:
|
|
104
|
-
F4:
|
|
105
|
-
F5:
|
|
106
|
-
F6:
|
|
107
|
-
F7:
|
|
108
|
-
F8:
|
|
109
|
-
F9:
|
|
110
|
-
F10:
|
|
111
|
-
F11:
|
|
112
|
-
F12:
|
|
113
|
-
|
|
114
|
-
COMMAND:
|
|
115
|
-
META:
|
|
51
|
+
NULL: '\uE000',
|
|
52
|
+
CANCEL: '\uE001', // ^break
|
|
53
|
+
HELP: '\uE002',
|
|
54
|
+
BACK_SPACE: '\uE003',
|
|
55
|
+
TAB: '\uE004',
|
|
56
|
+
CLEAR: '\uE005',
|
|
57
|
+
RETURN: '\uE006',
|
|
58
|
+
ENTER: '\uE007',
|
|
59
|
+
SHIFT: '\uE008',
|
|
60
|
+
CONTROL: '\uE009',
|
|
61
|
+
ALT: '\uE00A',
|
|
62
|
+
PAUSE: '\uE00B',
|
|
63
|
+
ESCAPE: '\uE00C',
|
|
64
|
+
SPACE: '\uE00D',
|
|
65
|
+
PAGE_UP: '\uE00E',
|
|
66
|
+
PAGE_DOWN: '\uE00F',
|
|
67
|
+
END: '\uE010',
|
|
68
|
+
HOME: '\uE011',
|
|
69
|
+
ARROW_LEFT: '\uE012',
|
|
70
|
+
LEFT: '\uE012',
|
|
71
|
+
ARROW_UP: '\uE013',
|
|
72
|
+
UP: '\uE013',
|
|
73
|
+
ARROW_RIGHT: '\uE014',
|
|
74
|
+
RIGHT: '\uE014',
|
|
75
|
+
ARROW_DOWN: '\uE015',
|
|
76
|
+
DOWN: '\uE015',
|
|
77
|
+
INSERT: '\uE016',
|
|
78
|
+
DELETE: '\uE017',
|
|
79
|
+
SEMICOLON: '\uE018',
|
|
80
|
+
EQUALS: '\uE019',
|
|
81
|
+
|
|
82
|
+
NUMPAD0: '\uE01A', // number pad keys
|
|
83
|
+
NUMPAD1: '\uE01B',
|
|
84
|
+
NUMPAD2: '\uE01C',
|
|
85
|
+
NUMPAD3: '\uE01D',
|
|
86
|
+
NUMPAD4: '\uE01E',
|
|
87
|
+
NUMPAD5: '\uE01F',
|
|
88
|
+
NUMPAD6: '\uE020',
|
|
89
|
+
NUMPAD7: '\uE021',
|
|
90
|
+
NUMPAD8: '\uE022',
|
|
91
|
+
NUMPAD9: '\uE023',
|
|
92
|
+
MULTIPLY: '\uE024',
|
|
93
|
+
ADD: '\uE025',
|
|
94
|
+
SEPARATOR: '\uE026',
|
|
95
|
+
SUBTRACT: '\uE027',
|
|
96
|
+
DECIMAL: '\uE028',
|
|
97
|
+
DIVIDE: '\uE029',
|
|
98
|
+
|
|
99
|
+
F1: '\uE031', // function keys
|
|
100
|
+
F2: '\uE032',
|
|
101
|
+
F3: '\uE033',
|
|
102
|
+
F4: '\uE034',
|
|
103
|
+
F5: '\uE035',
|
|
104
|
+
F6: '\uE036',
|
|
105
|
+
F7: '\uE037',
|
|
106
|
+
F8: '\uE038',
|
|
107
|
+
F9: '\uE039',
|
|
108
|
+
F10: '\uE03A',
|
|
109
|
+
F11: '\uE03B',
|
|
110
|
+
F12: '\uE03C',
|
|
111
|
+
|
|
112
|
+
COMMAND: '\uE03D', // Apple command key
|
|
113
|
+
META: '\uE03D', // alias for Windows key
|
|
116
114
|
|
|
117
115
|
/**
|
|
118
116
|
* Japanese modifier key for switching between full- and half-width
|
|
@@ -120,8 +118,7 @@ const Key = {
|
|
|
120
118
|
* @see <https://en.wikipedia.org/wiki/Language_input_keys>
|
|
121
119
|
*/
|
|
122
120
|
ZENKAKU_HANKAKU: '\uE040',
|
|
123
|
-
}
|
|
124
|
-
|
|
121
|
+
}
|
|
125
122
|
|
|
126
123
|
/**
|
|
127
124
|
* Simulate pressing many keys at once in a "chord". Takes a sequence of
|
|
@@ -135,10 +132,9 @@ const Key = {
|
|
|
135
132
|
* @param {...string} keys The key sequence to concatenate.
|
|
136
133
|
* @return {string} The null-terminated key sequence.
|
|
137
134
|
*/
|
|
138
|
-
Key.chord = function(...keys) {
|
|
139
|
-
return keys.join('') + Key.NULL
|
|
140
|
-
}
|
|
141
|
-
|
|
135
|
+
Key.chord = function (...keys) {
|
|
136
|
+
return keys.join('') + Key.NULL
|
|
137
|
+
}
|
|
142
138
|
|
|
143
139
|
/**
|
|
144
140
|
* Used with {@link ./webelement.WebElement#sendKeys WebElement#sendKeys} on
|
|
@@ -155,7 +151,6 @@ Key.chord = function(...keys) {
|
|
|
155
151
|
* [Selenium Server](https://selenium.dev/downloads/).
|
|
156
152
|
*/
|
|
157
153
|
class FileDetector {
|
|
158
|
-
|
|
159
154
|
/**
|
|
160
155
|
* Handles the file specified by the given path, preparing it for use with
|
|
161
156
|
* the current browser. If the path does not refer to a valid file, it will
|
|
@@ -170,12 +165,11 @@ class FileDetector {
|
|
|
170
165
|
* @return {!Promise<string>} A promise for the processed file path.
|
|
171
166
|
* @package
|
|
172
167
|
*/
|
|
173
|
-
handleFile(driver, path) {
|
|
174
|
-
return Promise.resolve(path)
|
|
168
|
+
handleFile(driver, path) { // eslint-disable-line
|
|
169
|
+
return Promise.resolve(path)
|
|
175
170
|
}
|
|
176
171
|
}
|
|
177
172
|
|
|
178
|
-
|
|
179
173
|
/**
|
|
180
174
|
* Generic description of a single action to send to the remote end.
|
|
181
175
|
*
|
|
@@ -185,21 +179,20 @@ class FileDetector {
|
|
|
185
179
|
class Action {
|
|
186
180
|
constructor() {
|
|
187
181
|
/** @type {!Action.Type} */
|
|
188
|
-
this.type
|
|
182
|
+
this.type
|
|
189
183
|
/** @type {(number|undefined)} */
|
|
190
|
-
this.duration
|
|
184
|
+
this.duration
|
|
191
185
|
/** @type {(string|undefined)} */
|
|
192
|
-
this.value
|
|
186
|
+
this.value
|
|
193
187
|
/** @type {(Button|undefined)} */
|
|
194
|
-
this.button
|
|
188
|
+
this.button
|
|
195
189
|
/** @type {(number|undefined)} */
|
|
196
|
-
this.x
|
|
190
|
+
this.x
|
|
197
191
|
/** @type {(number|undefined)} */
|
|
198
|
-
this.y
|
|
192
|
+
this.y
|
|
199
193
|
}
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
|
|
203
196
|
/**
|
|
204
197
|
* @enum {string}
|
|
205
198
|
* @package
|
|
@@ -212,9 +205,8 @@ Action.Type = {
|
|
|
212
205
|
POINTER_DOWN: 'pointerDown',
|
|
213
206
|
POINTER_UP: 'pointerUp',
|
|
214
207
|
POINTER_MOVE: 'pointerMove',
|
|
215
|
-
POINTER_CANCEL: 'pointerCancel'
|
|
216
|
-
}
|
|
217
|
-
|
|
208
|
+
POINTER_CANCEL: 'pointerCancel',
|
|
209
|
+
}
|
|
218
210
|
|
|
219
211
|
/**
|
|
220
212
|
* Represents a user input device.
|
|
@@ -227,17 +219,16 @@ class Device {
|
|
|
227
219
|
* @param {string} id a unique ID for this device.
|
|
228
220
|
*/
|
|
229
221
|
constructor(type, id) {
|
|
230
|
-
/** @private @const */ this.type_ = type
|
|
231
|
-
/** @private @const */ this.id_ = id
|
|
222
|
+
/** @private @const */ this.type_ = type
|
|
223
|
+
/** @private @const */ this.id_ = id
|
|
232
224
|
}
|
|
233
225
|
|
|
234
226
|
/** @return {!Object} the JSON encoding for this device. */
|
|
235
227
|
toJSON() {
|
|
236
|
-
return {
|
|
228
|
+
return { type: this.type_, id: this.id_ }
|
|
237
229
|
}
|
|
238
230
|
}
|
|
239
231
|
|
|
240
|
-
|
|
241
232
|
/**
|
|
242
233
|
* Device types supported by the WebDriver protocol.
|
|
243
234
|
*
|
|
@@ -247,9 +238,8 @@ class Device {
|
|
|
247
238
|
Device.Type = {
|
|
248
239
|
KEY: 'key',
|
|
249
240
|
NONE: 'none',
|
|
250
|
-
POINTER: 'pointer'
|
|
251
|
-
}
|
|
252
|
-
|
|
241
|
+
POINTER: 'pointer',
|
|
242
|
+
}
|
|
253
243
|
|
|
254
244
|
/**
|
|
255
245
|
* @param {(string|Key|number)} key
|
|
@@ -258,22 +248,22 @@ Device.Type = {
|
|
|
258
248
|
*/
|
|
259
249
|
function checkCodePoint(key) {
|
|
260
250
|
if (typeof key === 'number') {
|
|
261
|
-
return String.fromCodePoint(key)
|
|
251
|
+
return String.fromCodePoint(key)
|
|
262
252
|
}
|
|
263
253
|
|
|
264
254
|
if (typeof key !== 'string') {
|
|
265
|
-
throw new InvalidArgumentError(`key is not a string: ${key}`)
|
|
255
|
+
throw new InvalidArgumentError(`key is not a string: ${key}`)
|
|
266
256
|
}
|
|
267
257
|
|
|
268
|
-
key = key.normalize()
|
|
258
|
+
key = key.normalize()
|
|
269
259
|
if (Array.from(key).length != 1) {
|
|
270
260
|
throw new InvalidArgumentError(
|
|
271
|
-
|
|
261
|
+
`key input is not a single code point: ${key}`
|
|
262
|
+
)
|
|
272
263
|
}
|
|
273
|
-
return key
|
|
264
|
+
return key
|
|
274
265
|
}
|
|
275
266
|
|
|
276
|
-
|
|
277
267
|
/**
|
|
278
268
|
* Keyboard input device.
|
|
279
269
|
*
|
|
@@ -283,7 +273,7 @@ function checkCodePoint(key) {
|
|
|
283
273
|
class Keyboard extends Device {
|
|
284
274
|
/** @param {string} id the device ID. */
|
|
285
275
|
constructor(id) {
|
|
286
|
-
super(Device.Type.KEY, id)
|
|
276
|
+
super(Device.Type.KEY, id)
|
|
287
277
|
}
|
|
288
278
|
|
|
289
279
|
/**
|
|
@@ -296,7 +286,7 @@ class Keyboard extends Device {
|
|
|
296
286
|
* @package
|
|
297
287
|
*/
|
|
298
288
|
keyDown(key) {
|
|
299
|
-
return {type: Action.Type.KEY_DOWN, value: checkCodePoint(key)}
|
|
289
|
+
return { type: Action.Type.KEY_DOWN, value: checkCodePoint(key) }
|
|
300
290
|
}
|
|
301
291
|
|
|
302
292
|
/**
|
|
@@ -309,11 +299,10 @@ class Keyboard extends Device {
|
|
|
309
299
|
* @package
|
|
310
300
|
*/
|
|
311
301
|
keyUp(key) {
|
|
312
|
-
return {type: Action.Type.KEY_UP, value: checkCodePoint(key)}
|
|
302
|
+
return { type: Action.Type.KEY_UP, value: checkCodePoint(key) }
|
|
313
303
|
}
|
|
314
304
|
}
|
|
315
305
|
|
|
316
|
-
|
|
317
306
|
/**
|
|
318
307
|
* Defines the reference point from which to compute offsets for
|
|
319
308
|
* {@linkplain ./input.Pointer#move pointer move} actions.
|
|
@@ -324,9 +313,8 @@ const Origin = {
|
|
|
324
313
|
/** Compute offsets relative to the pointer's current position. */
|
|
325
314
|
POINTER: 'pointer',
|
|
326
315
|
/** Compute offsets relative to the viewport. */
|
|
327
|
-
VIEWPORT: 'viewport'
|
|
328
|
-
}
|
|
329
|
-
|
|
316
|
+
VIEWPORT: 'viewport',
|
|
317
|
+
}
|
|
330
318
|
|
|
331
319
|
/**
|
|
332
320
|
* Pointer input device.
|
|
@@ -340,15 +328,16 @@ class Pointer extends Device {
|
|
|
340
328
|
* @param {Pointer.Type} type the pointer type.
|
|
341
329
|
*/
|
|
342
330
|
constructor(id, type) {
|
|
343
|
-
super(Device.Type.POINTER, id)
|
|
344
|
-
/** @private @const */ this.pointerType_ = type
|
|
331
|
+
super(Device.Type.POINTER, id)
|
|
332
|
+
/** @private @const */ this.pointerType_ = type
|
|
345
333
|
}
|
|
346
334
|
|
|
347
335
|
/** @override */
|
|
348
336
|
toJSON() {
|
|
349
337
|
return Object.assign(
|
|
350
|
-
|
|
351
|
-
|
|
338
|
+
{ parameters: { pointerType: this.pointerType_ } },
|
|
339
|
+
super.toJSON()
|
|
340
|
+
)
|
|
352
341
|
}
|
|
353
342
|
|
|
354
343
|
/**
|
|
@@ -356,7 +345,7 @@ class Pointer extends Device {
|
|
|
356
345
|
* @package
|
|
357
346
|
*/
|
|
358
347
|
cancel() {
|
|
359
|
-
return {type: Action.Type.POINTER_CANCEL}
|
|
348
|
+
return { type: Action.Type.POINTER_CANCEL }
|
|
360
349
|
}
|
|
361
350
|
|
|
362
351
|
/**
|
|
@@ -365,7 +354,7 @@ class Pointer extends Device {
|
|
|
365
354
|
* @package
|
|
366
355
|
*/
|
|
367
356
|
press(button = Button.LEFT) {
|
|
368
|
-
return {type: Action.Type.POINTER_DOWN, button}
|
|
357
|
+
return { type: Action.Type.POINTER_DOWN, button }
|
|
369
358
|
}
|
|
370
359
|
|
|
371
360
|
/**
|
|
@@ -375,7 +364,7 @@ class Pointer extends Device {
|
|
|
375
364
|
* @package
|
|
376
365
|
*/
|
|
377
366
|
release(button = Button.LEFT) {
|
|
378
|
-
return {type: Action.Type.POINTER_UP, button}
|
|
367
|
+
return { type: Action.Type.POINTER_UP, button }
|
|
379
368
|
}
|
|
380
369
|
|
|
381
370
|
/**
|
|
@@ -394,12 +383,11 @@ class Pointer extends Device {
|
|
|
394
383
|
* @return {!Action} The new action.
|
|
395
384
|
* @package
|
|
396
385
|
*/
|
|
397
|
-
move({x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT}) {
|
|
398
|
-
return {type: Action.Type.POINTER_MOVE, origin, duration, x, y}
|
|
386
|
+
move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT }) {
|
|
387
|
+
return { type: Action.Type.POINTER_MOVE, origin, duration, x, y }
|
|
399
388
|
}
|
|
400
389
|
}
|
|
401
390
|
|
|
402
|
-
|
|
403
391
|
/**
|
|
404
392
|
* The supported types of pointers.
|
|
405
393
|
* @enum {string}
|
|
@@ -407,9 +395,8 @@ class Pointer extends Device {
|
|
|
407
395
|
Pointer.Type = {
|
|
408
396
|
MOUSE: 'mouse',
|
|
409
397
|
PEN: 'pen',
|
|
410
|
-
TOUCH: 'touch'
|
|
411
|
-
}
|
|
412
|
-
|
|
398
|
+
TOUCH: 'touch',
|
|
399
|
+
}
|
|
413
400
|
|
|
414
401
|
/**
|
|
415
402
|
* User facing API for generating complex user gestures. This class should not
|
|
@@ -502,67 +489,6 @@ Pointer.Type = {
|
|
|
502
489
|
* | Keyboard | pause(100) | keyDown(SHIFT) |
|
|
503
490
|
* | Mouse | move({duration: 300}) | |
|
|
504
491
|
*
|
|
505
|
-
*
|
|
506
|
-
* #### Bridge Mode
|
|
507
|
-
*
|
|
508
|
-
* As of January 2018, only Firefox natively supports this API. For other
|
|
509
|
-
* browsers, you may either use the
|
|
510
|
-
* {@link ./actions.LegacyActionSequence LegacyActionSequence} class, _or_ you
|
|
511
|
-
* can put the Actions class into bridge mode by passing `{bridge: true}` on
|
|
512
|
-
* creation:
|
|
513
|
-
*
|
|
514
|
-
* const actions = driver.actions({bridge: true});
|
|
515
|
-
* await actions.click(element).sendKeys('abc').perform();
|
|
516
|
-
*
|
|
517
|
-
* In bridge mode, {@link #perform perform()} will first attempt to execute the
|
|
518
|
-
* configured action sequence using the W3C action protocol. If this is rejected
|
|
519
|
-
* by the remote end, the sequence will be translated to and executed against
|
|
520
|
-
* the legacy protocol.
|
|
521
|
-
*
|
|
522
|
-
* Bridge mode __is not enabled by default__ as there are several notable
|
|
523
|
-
* differences between W3C-specified and legacy protocols. Care must be
|
|
524
|
-
* taken to configure your action sequences to account for these differences:
|
|
525
|
-
*
|
|
526
|
-
* 1. For W3C actions, the entire action sequence is executed in a single
|
|
527
|
-
* call to the remote end. For legacy sequences, multiple calls must be
|
|
528
|
-
* made for each step in the sequence. This introduces additional latency
|
|
529
|
-
* which may impact how the browser responds to the emulated user actions.
|
|
530
|
-
*
|
|
531
|
-
* 2. For the legacy actions, {@linkplain #pause pauses} are handled _locally_.
|
|
532
|
-
*
|
|
533
|
-
* 3. For legacy actions, a {@linkplain #keyDown keyDown()} for a
|
|
534
|
-
* _non-modifier key_ **must** be followed by a {@linkplain #keyUP keyUp()}
|
|
535
|
-
* for the same key. This will be handled for you if you use the
|
|
536
|
-
* {@linkplain #sendKeys sendKeys()} method.
|
|
537
|
-
*
|
|
538
|
-
* 4. Mouse movements may not be specified relative to
|
|
539
|
-
* {@linkplain ./input.Origin.VIEWPORT Origin.VIEWPORT}.
|
|
540
|
-
* All movements must be relative to an element or the mouse's current
|
|
541
|
-
* position ({@linkplain ./input.Origin.POINTER Origin.POINTER}).
|
|
542
|
-
* The {@linkplain #move move()} method defaults to viewport relative
|
|
543
|
-
* offsets, so you must always specify an appropriate origin in bridge mode:
|
|
544
|
-
*
|
|
545
|
-
* driver.actions({bridge: true})
|
|
546
|
-
* .move({x: 0, y: 0, origin: Origin.POINTER})
|
|
547
|
-
* .perform();
|
|
548
|
-
* driver.actions({bridge: true})
|
|
549
|
-
* .move({x: 0, y: 0, origin: someWebElement})
|
|
550
|
-
* .perform();
|
|
551
|
-
*
|
|
552
|
-
* 5. The legacy protocol does not support specifying the duration of a
|
|
553
|
-
* {@linkplain #move mouse movement}; any specified duration _is ignored_
|
|
554
|
-
* when translating actions to the legacy protocol.
|
|
555
|
-
*
|
|
556
|
-
* 6. For W3C actions, move offsets relative to a
|
|
557
|
-
* {@linkplain ./webdriver.WebElement WebElement} are interpreted relative
|
|
558
|
-
* to the center of an element's _first_ [client rect] in the viewport. For
|
|
559
|
-
* legacy actions, element offsets are relative to the top-left corner of
|
|
560
|
-
* the element's [bounding client rect]. When translating actions to the
|
|
561
|
-
* legacy protocol in bridge mode, an extra command must be inserted to
|
|
562
|
-
* translate move offsets from one frame of reference to the other. This
|
|
563
|
-
* extra command contributes to the overall latency issue outlined in
|
|
564
|
-
* point 1.
|
|
565
|
-
*
|
|
566
492
|
* [client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects
|
|
567
493
|
* [bounding client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
|
|
568
494
|
*
|
|
@@ -573,47 +499,38 @@ class Actions {
|
|
|
573
499
|
/**
|
|
574
500
|
* @param {!Executor} executor The object to execute the configured
|
|
575
501
|
* actions with.
|
|
576
|
-
* @param {{async: (boolean|undefined)
|
|
577
|
-
* bridge: (boolean|undefined)}} options Options for this action
|
|
502
|
+
* @param {{async: (boolean|undefined)}} options Options for this action
|
|
578
503
|
* sequence (see class description for details).
|
|
579
504
|
*/
|
|
580
|
-
constructor(executor, {async = false
|
|
581
|
-
if (async && bridge) {
|
|
582
|
-
throw new InvalidArgumentError(
|
|
583
|
-
'Async sequences not supported with bridge mode');
|
|
584
|
-
}
|
|
505
|
+
constructor(executor, { async = false } = {}) {
|
|
585
506
|
|
|
586
507
|
/** @private @const */
|
|
587
|
-
this.executor_ = executor
|
|
508
|
+
this.executor_ = executor
|
|
588
509
|
|
|
589
510
|
/** @private @const */
|
|
590
|
-
this.sync_ = !async
|
|
511
|
+
this.sync_ = !async
|
|
591
512
|
|
|
592
513
|
/** @private @const */
|
|
593
|
-
this.
|
|
514
|
+
this.keyboard_ = new Keyboard('default keyboard')
|
|
594
515
|
|
|
595
516
|
/** @private @const */
|
|
596
|
-
this.
|
|
597
|
-
|
|
598
|
-
/** @private @const */
|
|
599
|
-
this.mouse_ = new Pointer('default mouse', Pointer.Type.MOUSE);
|
|
517
|
+
this.mouse_ = new Pointer('default mouse', Pointer.Type.MOUSE)
|
|
600
518
|
|
|
601
519
|
/** @private @const {!Map<!Device, !Array<!Action>>} */
|
|
602
520
|
this.sequences_ = new Map([
|
|
603
521
|
[this.keyboard_, []],
|
|
604
522
|
[this.mouse_, []],
|
|
605
|
-
])
|
|
523
|
+
])
|
|
606
524
|
}
|
|
607
525
|
|
|
608
|
-
|
|
609
526
|
/** @return {!Keyboard} the keyboard device handle. */
|
|
610
527
|
keyboard() {
|
|
611
|
-
return this.keyboard_
|
|
528
|
+
return this.keyboard_
|
|
612
529
|
}
|
|
613
530
|
|
|
614
531
|
/** @return {!Pointer} the mouse pointer device handle. */
|
|
615
532
|
mouse() {
|
|
616
|
-
return this.mouse_
|
|
533
|
+
return this.mouse_
|
|
617
534
|
}
|
|
618
535
|
|
|
619
536
|
/**
|
|
@@ -622,12 +539,12 @@ class Actions {
|
|
|
622
539
|
* @private
|
|
623
540
|
*/
|
|
624
541
|
sequence_(device) {
|
|
625
|
-
let sequence = this.sequences_.get(device)
|
|
542
|
+
let sequence = this.sequences_.get(device)
|
|
626
543
|
if (!sequence) {
|
|
627
|
-
sequence = []
|
|
628
|
-
this.sequences_.set(device, sequence)
|
|
544
|
+
sequence = []
|
|
545
|
+
this.sequences_.set(device, sequence)
|
|
629
546
|
}
|
|
630
|
-
return sequence
|
|
547
|
+
return sequence
|
|
631
548
|
}
|
|
632
549
|
|
|
633
550
|
/**
|
|
@@ -641,8 +558,8 @@ class Actions {
|
|
|
641
558
|
* @return {!Actions} a self reference.
|
|
642
559
|
*/
|
|
643
560
|
insert(device, ...actions) {
|
|
644
|
-
this.sequence_(device).push(...actions)
|
|
645
|
-
return this.sync_ ? this.synchronize() : this
|
|
561
|
+
this.sequence_(device).push(...actions)
|
|
562
|
+
return this.sync_ ? this.synchronize() : this
|
|
646
563
|
}
|
|
647
564
|
|
|
648
565
|
/**
|
|
@@ -657,30 +574,30 @@ class Actions {
|
|
|
657
574
|
* @return {!Actions} a self reference.
|
|
658
575
|
*/
|
|
659
576
|
synchronize(...devices) {
|
|
660
|
-
let sequences
|
|
661
|
-
let max = 0
|
|
577
|
+
let sequences
|
|
578
|
+
let max = 0
|
|
662
579
|
if (devices.length === 0) {
|
|
663
580
|
for (const s of this.sequences_.values()) {
|
|
664
|
-
max = Math.max(max, s.length)
|
|
581
|
+
max = Math.max(max, s.length)
|
|
665
582
|
}
|
|
666
|
-
sequences = this.sequences_.values()
|
|
583
|
+
sequences = this.sequences_.values()
|
|
667
584
|
} else {
|
|
668
|
-
sequences = []
|
|
585
|
+
sequences = []
|
|
669
586
|
for (const device of devices) {
|
|
670
|
-
const seq = this.sequence_(device)
|
|
671
|
-
max = Math.max(max, seq.length)
|
|
672
|
-
sequences.push(seq)
|
|
587
|
+
const seq = this.sequence_(device)
|
|
588
|
+
max = Math.max(max, seq.length)
|
|
589
|
+
sequences.push(seq)
|
|
673
590
|
}
|
|
674
591
|
}
|
|
675
592
|
|
|
676
|
-
const pause = {type: Action.Type.PAUSE, duration: 0}
|
|
593
|
+
const pause = { type: Action.Type.PAUSE, duration: 0 }
|
|
677
594
|
for (const seq of sequences) {
|
|
678
595
|
while (seq.length < max) {
|
|
679
|
-
seq.push(pause)
|
|
596
|
+
seq.push(pause)
|
|
680
597
|
}
|
|
681
598
|
}
|
|
682
599
|
|
|
683
|
-
return this
|
|
600
|
+
return this
|
|
684
601
|
}
|
|
685
602
|
|
|
686
603
|
/**
|
|
@@ -735,21 +652,21 @@ class Actions {
|
|
|
735
652
|
*/
|
|
736
653
|
pause(duration, ...devices) {
|
|
737
654
|
if (duration instanceof Device) {
|
|
738
|
-
devices.push(duration)
|
|
739
|
-
duration = 0
|
|
655
|
+
devices.push(duration)
|
|
656
|
+
duration = 0
|
|
740
657
|
} else if (!duration) {
|
|
741
|
-
duration = 0
|
|
658
|
+
duration = 0
|
|
742
659
|
}
|
|
743
660
|
|
|
744
|
-
const action = {type: Action.Type.PAUSE, duration}
|
|
661
|
+
const action = { type: Action.Type.PAUSE, duration }
|
|
745
662
|
|
|
746
663
|
// NB: need a properly typed variable for type checking.
|
|
747
664
|
/** @type {!Iterable<!Device>} */
|
|
748
|
-
const iterable = devices.length === 0 ? this.sequences_.keys() : devices
|
|
665
|
+
const iterable = devices.length === 0 ? this.sequences_.keys() : devices
|
|
749
666
|
for (const device of iterable) {
|
|
750
|
-
this.sequence_(device).push(action)
|
|
667
|
+
this.sequence_(device).push(action)
|
|
751
668
|
}
|
|
752
|
-
return this.sync_ ? this.synchronize() : this
|
|
669
|
+
return this.sync_ ? this.synchronize() : this
|
|
753
670
|
}
|
|
754
671
|
|
|
755
672
|
/**
|
|
@@ -761,7 +678,7 @@ class Actions {
|
|
|
761
678
|
* @return {!Actions} a self reference.
|
|
762
679
|
*/
|
|
763
680
|
keyDown(key) {
|
|
764
|
-
return this.insert(this.keyboard_, this.keyboard_.keyDown(key))
|
|
681
|
+
return this.insert(this.keyboard_, this.keyboard_.keyDown(key))
|
|
765
682
|
}
|
|
766
683
|
|
|
767
684
|
/**
|
|
@@ -773,7 +690,7 @@ class Actions {
|
|
|
773
690
|
* @return {!Actions} a self reference.
|
|
774
691
|
*/
|
|
775
692
|
keyUp(key) {
|
|
776
|
-
return this.insert(this.keyboard_, this.keyboard_.keyUp(key))
|
|
693
|
+
return this.insert(this.keyboard_, this.keyboard_.keyUp(key))
|
|
777
694
|
}
|
|
778
695
|
|
|
779
696
|
/**
|
|
@@ -788,21 +705,20 @@ class Actions {
|
|
|
788
705
|
* @return {!Actions} a self reference.
|
|
789
706
|
*/
|
|
790
707
|
sendKeys(...keys) {
|
|
791
|
-
const actions = []
|
|
708
|
+
const actions = []
|
|
792
709
|
for (const key of keys) {
|
|
793
710
|
if (typeof key === 'string') {
|
|
794
711
|
for (const symbol of key) {
|
|
795
712
|
actions.push(
|
|
796
|
-
|
|
797
|
-
|
|
713
|
+
this.keyboard_.keyDown(symbol),
|
|
714
|
+
this.keyboard_.keyUp(symbol)
|
|
715
|
+
)
|
|
798
716
|
}
|
|
799
717
|
} else {
|
|
800
|
-
actions.push(
|
|
801
|
-
this.keyboard_.keyDown(key),
|
|
802
|
-
this.keyboard_.keyUp(key));
|
|
718
|
+
actions.push(this.keyboard_.keyDown(key), this.keyboard_.keyUp(key))
|
|
803
719
|
}
|
|
804
720
|
}
|
|
805
|
-
return this.insert(this.keyboard_, ...actions)
|
|
721
|
+
return this.insert(this.keyboard_, ...actions)
|
|
806
722
|
}
|
|
807
723
|
|
|
808
724
|
/**
|
|
@@ -812,7 +728,7 @@ class Actions {
|
|
|
812
728
|
* @return {!Actions} a self reference.
|
|
813
729
|
*/
|
|
814
730
|
press(button = Button.LEFT) {
|
|
815
|
-
return this.insert(this.mouse_, this.mouse_.press(button))
|
|
731
|
+
return this.insert(this.mouse_, this.mouse_.press(button))
|
|
816
732
|
}
|
|
817
733
|
|
|
818
734
|
/**
|
|
@@ -823,7 +739,7 @@ class Actions {
|
|
|
823
739
|
* @return {!Actions} a self reference.
|
|
824
740
|
*/
|
|
825
741
|
release(button = Button.LEFT) {
|
|
826
|
-
return this.insert(this.mouse_, this.mouse_.release(button))
|
|
742
|
+
return this.insert(this.mouse_, this.mouse_.release(button))
|
|
827
743
|
}
|
|
828
744
|
|
|
829
745
|
/**
|
|
@@ -847,9 +763,11 @@ class Actions {
|
|
|
847
763
|
* corner of the viewport over 100ms.
|
|
848
764
|
* @return {!Actions} a self reference.
|
|
849
765
|
*/
|
|
850
|
-
move({x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT} = {}) {
|
|
766
|
+
move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT } = {}) {
|
|
851
767
|
return this.insert(
|
|
852
|
-
|
|
768
|
+
this.mouse_,
|
|
769
|
+
this.mouse_.move({ x, y, duration, origin })
|
|
770
|
+
)
|
|
853
771
|
}
|
|
854
772
|
|
|
855
773
|
/**
|
|
@@ -862,9 +780,9 @@ class Actions {
|
|
|
862
780
|
*/
|
|
863
781
|
click(element) {
|
|
864
782
|
if (element) {
|
|
865
|
-
this.move({origin: element})
|
|
783
|
+
this.move({ origin: element })
|
|
866
784
|
}
|
|
867
|
-
return this.press().release()
|
|
785
|
+
return this.press().release()
|
|
868
786
|
}
|
|
869
787
|
|
|
870
788
|
/**
|
|
@@ -877,9 +795,9 @@ class Actions {
|
|
|
877
795
|
*/
|
|
878
796
|
contextClick(element) {
|
|
879
797
|
if (element) {
|
|
880
|
-
this.move({origin: element})
|
|
798
|
+
this.move({ origin: element })
|
|
881
799
|
}
|
|
882
|
-
return this.press(Button.RIGHT).release(Button.RIGHT)
|
|
800
|
+
return this.press(Button.RIGHT).release(Button.RIGHT)
|
|
883
801
|
}
|
|
884
802
|
|
|
885
803
|
/**
|
|
@@ -891,7 +809,7 @@ class Actions {
|
|
|
891
809
|
* @return {!Actions} a self reference.
|
|
892
810
|
*/
|
|
893
811
|
doubleClick(element) {
|
|
894
|
-
return this.click(element).press().release()
|
|
812
|
+
return this.click(element).press().release()
|
|
895
813
|
}
|
|
896
814
|
|
|
897
815
|
/**
|
|
@@ -913,20 +831,23 @@ class Actions {
|
|
|
913
831
|
*/
|
|
914
832
|
dragAndDrop(from, to) {
|
|
915
833
|
// Do not require up top to avoid a cycle that breaks static analysis.
|
|
916
|
-
const {WebElement} = require('./webdriver')
|
|
917
|
-
if (
|
|
918
|
-
|
|
834
|
+
const { WebElement } = require('./webdriver')
|
|
835
|
+
if (
|
|
836
|
+
!(to instanceof WebElement) &&
|
|
837
|
+
(!to || typeof to.x !== 'number' || typeof to.y !== 'number')
|
|
838
|
+
) {
|
|
919
839
|
throw new InvalidArgumentError(
|
|
920
|
-
|
|
840
|
+
'Invalid drag target; must specify a WebElement or {x, y} offset'
|
|
841
|
+
)
|
|
921
842
|
}
|
|
922
843
|
|
|
923
|
-
this.move({origin: from}).press()
|
|
844
|
+
this.move({ origin: from }).press()
|
|
924
845
|
if (to instanceof WebElement) {
|
|
925
|
-
this.move({origin: to})
|
|
846
|
+
this.move({ origin: to })
|
|
926
847
|
} else {
|
|
927
|
-
this.move({x: to.x, y: to.y, origin: Origin.POINTER})
|
|
848
|
+
this.move({ x: to.x, y: to.y, origin: Origin.POINTER })
|
|
928
849
|
}
|
|
929
|
-
return this.release()
|
|
850
|
+
return this.release()
|
|
930
851
|
}
|
|
931
852
|
|
|
932
853
|
/**
|
|
@@ -937,9 +858,9 @@ class Actions {
|
|
|
937
858
|
*/
|
|
938
859
|
clear() {
|
|
939
860
|
for (const s of this.sequences_.values()) {
|
|
940
|
-
s.length = 0
|
|
861
|
+
s.length = 0
|
|
941
862
|
}
|
|
942
|
-
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
|
|
863
|
+
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
|
|
943
864
|
}
|
|
944
865
|
|
|
945
866
|
/**
|
|
@@ -949,219 +870,39 @@ class Actions {
|
|
|
949
870
|
* been completed.
|
|
950
871
|
*/
|
|
951
872
|
async perform() {
|
|
952
|
-
const _actions = []
|
|
873
|
+
const _actions = []
|
|
953
874
|
this.sequences_.forEach((actions, device) => {
|
|
954
875
|
if (!isIdle(actions)) {
|
|
955
|
-
actions = actions.concat()
|
|
956
|
-
_actions.push(Object.assign({actions}, device.toJSON()))
|
|
876
|
+
actions = actions.concat() // Defensive copy.
|
|
877
|
+
_actions.push(Object.assign({ actions }, device.toJSON()))
|
|
957
878
|
}
|
|
958
|
-
})
|
|
879
|
+
})
|
|
959
880
|
|
|
960
881
|
if (_actions.length === 0) {
|
|
961
|
-
return Promise.resolve()
|
|
882
|
+
return Promise.resolve()
|
|
962
883
|
}
|
|
963
884
|
|
|
964
885
|
try {
|
|
965
886
|
await this.executor_.execute(
|
|
966
|
-
|
|
887
|
+
new Command(Name.ACTIONS).setParameter('actions', _actions)
|
|
888
|
+
)
|
|
967
889
|
} catch (ex) {
|
|
968
|
-
|
|
969
|
-
&& (ex instanceof UnknownCommandError
|
|
970
|
-
|| ex instanceof UnsupportedOperationError)) {
|
|
971
|
-
return executeLegacy(this.executor_, this.sequences_);
|
|
972
|
-
}
|
|
973
|
-
throw ex;
|
|
890
|
+
throw ex
|
|
974
891
|
}
|
|
975
892
|
}
|
|
976
893
|
}
|
|
977
894
|
|
|
978
|
-
|
|
979
895
|
/**
|
|
980
896
|
* @param {!Array<!Action>} actions
|
|
981
897
|
* @return {boolean}
|
|
982
898
|
*/
|
|
983
899
|
function isIdle(actions) {
|
|
984
|
-
return
|
|
985
|
-
|
|
900
|
+
return (
|
|
901
|
+
actions.length === 0 ||
|
|
902
|
+
actions.every((a) => a.type === Action.Type.PAUSE && !a.duration)
|
|
903
|
+
)
|
|
986
904
|
}
|
|
987
905
|
|
|
988
|
-
|
|
989
|
-
const MODIFIER_KEYS = new Set([Key.ALT, Key.CONTROL, Key.SHIFT, Key.COMMAND]);
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
/**
|
|
993
|
-
* @param {!Executor} executor
|
|
994
|
-
* @param {!Map<!Device, !Array<!Action>>} sequences
|
|
995
|
-
* @return {!Promise<void>}
|
|
996
|
-
* @suppress {deprecated} Ignore warnings about using LegacyActionSequence.
|
|
997
|
-
*/
|
|
998
|
-
async function executeLegacy(executor, sequences) {
|
|
999
|
-
let maxLength = 0;
|
|
1000
|
-
sequences.forEach(seq => maxLength = Math.max(maxLength, seq.length));
|
|
1001
|
-
|
|
1002
|
-
const actions = [];
|
|
1003
|
-
for (let i = 0; i < maxLength; i++) {
|
|
1004
|
-
let next;
|
|
1005
|
-
for (const device of sequences.keys()) {
|
|
1006
|
-
const seq = sequences.get(device);
|
|
1007
|
-
|
|
1008
|
-
if (device instanceof Pointer
|
|
1009
|
-
&& device.pointerType_ !== Pointer.Type.MOUSE) {
|
|
1010
|
-
throw new UnsupportedOperationError(
|
|
1011
|
-
`${device.pointerType_} pointer not supported in bridge mode`);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
const action = seq[i];
|
|
1015
|
-
if (!action || (action.type === Action.Type.PAUSE && !action.duration)) {
|
|
1016
|
-
continue;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
// - If we've already found an action for this tick:
|
|
1020
|
-
// - If we have two pauses, use the one with a longer duration
|
|
1021
|
-
// - If one is a pause and the other isn't, use the non-pause
|
|
1022
|
-
// - Otherwise, two non-pauses is an error.
|
|
1023
|
-
// - Otherwise, we haven't selected an action yet.
|
|
1024
|
-
if (next) {
|
|
1025
|
-
if (next.type === Action.Type.PAUSE) {
|
|
1026
|
-
if (action.type === Action.Type.PAUSE) {
|
|
1027
|
-
next = next.duration > action.duration ? next : action;
|
|
1028
|
-
} else {
|
|
1029
|
-
next = action;
|
|
1030
|
-
}
|
|
1031
|
-
} else if (action.type !== Action.Type.PAUSE) {
|
|
1032
|
-
throw new UnsupportedOperationError(
|
|
1033
|
-
'parallel actions not supported in bridge mode');
|
|
1034
|
-
}
|
|
1035
|
-
} else {
|
|
1036
|
-
next = action;
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
if (action.type === Action.Type.KEY_DOWN) {
|
|
1040
|
-
// If this action is a keydown for a non-modifier key, the next action
|
|
1041
|
-
// must be a keyup for the same key, otherwise it cannot be translated
|
|
1042
|
-
// to the legacy action API.
|
|
1043
|
-
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1044
|
-
const nextAction = seq[i + 1];
|
|
1045
|
-
if (!nextAction
|
|
1046
|
-
|| nextAction.type !== Action.Type.KEY_UP
|
|
1047
|
-
|| nextAction.value !== action.value) {
|
|
1048
|
-
throw new UnsupportedOperationError(
|
|
1049
|
-
`in bridge mode, keydown for <${action.value}> must be followed`
|
|
1050
|
-
+ ' by a keyup for the same key');
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
} else if (action.type === Action.Type.KEY_UP
|
|
1054
|
-
&& !MODIFIER_KEYS.has(action.value)) {
|
|
1055
|
-
next = null;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
if (next) {
|
|
1060
|
-
actions.push(next);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
for (let i = 0; i < actions.length; i++) {
|
|
1065
|
-
const action = actions[i];
|
|
1066
|
-
switch (action.type) {
|
|
1067
|
-
case Action.Type.PAUSE:
|
|
1068
|
-
await promise.delayed(action.duration || 0);
|
|
1069
|
-
break;
|
|
1070
|
-
case Action.Type.KEY_DOWN:
|
|
1071
|
-
const keys = [action.value];
|
|
1072
|
-
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1073
|
-
function nextIsKeyDown() {
|
|
1074
|
-
const next = actions[i + 1];
|
|
1075
|
-
return !!next
|
|
1076
|
-
&& next.type === Action.Type.KEY_DOWN
|
|
1077
|
-
&& !MODIFIER_KEYS.has(next.value);
|
|
1078
|
-
}
|
|
1079
|
-
for (; nextIsKeyDown(); i++) {
|
|
1080
|
-
keys.push(actions[i + 1].value);
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
await executor.execute(
|
|
1084
|
-
new Command(Name.LEGACY_ACTION_SEND_KEYS)
|
|
1085
|
-
.setParameter('value', keys));
|
|
1086
|
-
break;
|
|
1087
|
-
case Action.Type.KEY_UP:
|
|
1088
|
-
await executor.execute(
|
|
1089
|
-
new Command(Name.LEGACY_ACTION_SEND_KEYS)
|
|
1090
|
-
.setParameter('value', [action.value]));
|
|
1091
|
-
break;
|
|
1092
|
-
case Action.Type.POINTER_DOWN: {
|
|
1093
|
-
function isClick(startAt) {
|
|
1094
|
-
const first = actions[startAt];
|
|
1095
|
-
const second = actions[startAt + 1];
|
|
1096
|
-
return !!first && !!second
|
|
1097
|
-
&& first.type === Action.Type.POINTER_DOWN
|
|
1098
|
-
&& second.type === Action.Type.POINTER_UP
|
|
1099
|
-
&& first.button === second.button;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
function isDoubleClick(startAt) {
|
|
1103
|
-
return isClick(startAt)
|
|
1104
|
-
&& isClick(startAt + 2)
|
|
1105
|
-
&& actions[startAt].button === actions[startAt = 2].button;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
let cmd;
|
|
1109
|
-
if (isDoubleClick(i)) {
|
|
1110
|
-
i += 3; // Consume the pointer up/down/up.
|
|
1111
|
-
cmd = Name.LEGACY_ACTION_DOUBLE_CLICK;
|
|
1112
|
-
} else if (isClick(i)) {
|
|
1113
|
-
i++; // Consume the pointer-up.
|
|
1114
|
-
cmd = Name.LEGACY_ACTION_CLICK;
|
|
1115
|
-
} else {
|
|
1116
|
-
cmd = Name.LEGACY_ACTION_MOUSE_DOWN;
|
|
1117
|
-
}
|
|
1118
|
-
await executor.execute(
|
|
1119
|
-
new Command(cmd).setParameter('button', action.button));
|
|
1120
|
-
break;
|
|
1121
|
-
}
|
|
1122
|
-
case Action.Type.POINTER_UP:
|
|
1123
|
-
await executor.execute(
|
|
1124
|
-
new Command(Name.LEGACY_ACTION_MOUSE_UP)
|
|
1125
|
-
.setParameter('button', action.button));
|
|
1126
|
-
break;
|
|
1127
|
-
case Action.Type.POINTER_MOVE:
|
|
1128
|
-
if (action.origin === Origin.VIEWPORT) {
|
|
1129
|
-
throw new UnsupportedOperationError(
|
|
1130
|
-
`pointer movements relative to ${Origin.VIEWPORT} are not`
|
|
1131
|
-
+ ' supported in bridge mode');
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
let x = action.x;
|
|
1136
|
-
let y = action.y;
|
|
1137
|
-
const cmd = new Command(Name.LEGACY_ACTION_MOUSE_MOVE);
|
|
1138
|
-
if (action.origin && action.origin !== Origin.POINTER) {
|
|
1139
|
-
const el = /** @type {!./webdriver.WebElement} */(action.origin);
|
|
1140
|
-
|
|
1141
|
-
// Need to translate frame of reference from center of element's first
|
|
1142
|
-
// client rect to the top-left of its bounding client rect. See:
|
|
1143
|
-
// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-center-point
|
|
1144
|
-
let diff = await executor.execute(
|
|
1145
|
-
new Command(Name.EXECUTE_SCRIPT)
|
|
1146
|
-
.setParameter('script', INTERNAL_COMPUTE_OFFSET_SCRIPT)
|
|
1147
|
-
.setParameter('args', [el]));
|
|
1148
|
-
x += diff[0];
|
|
1149
|
-
y += diff[1];
|
|
1150
|
-
|
|
1151
|
-
const id = await el.getId();
|
|
1152
|
-
cmd.setParameter('element', id);
|
|
1153
|
-
}
|
|
1154
|
-
cmd.setParameter('xoffset', x).setParameter('yoffset', y);
|
|
1155
|
-
await executor.execute(cmd);
|
|
1156
|
-
break;
|
|
1157
|
-
default:
|
|
1158
|
-
throw new UnsupportedOperationError(
|
|
1159
|
-
`${action.type} actions not supported in bridge mode`);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
906
|
/**
|
|
1166
907
|
* Script used to compute the offset from the center of a DOM element's first
|
|
1167
908
|
* client rect from the top-left corner of the element's bounding client rect.
|
|
@@ -1187,14 +928,12 @@ function computeOffset(el) {
|
|
|
1187
928
|
var bbox = el.getBoundingClientRect();
|
|
1188
929
|
return [x - bbox.left, y - bbox.top];
|
|
1189
930
|
}
|
|
1190
|
-
return computeOffset(arguments[0])
|
|
1191
|
-
|
|
931
|
+
return computeOffset(arguments[0]);`
|
|
1192
932
|
|
|
1193
933
|
// PUBLIC API
|
|
1194
934
|
|
|
1195
|
-
|
|
1196
935
|
module.exports = {
|
|
1197
|
-
Action,
|
|
936
|
+
Action, // For documentation only.
|
|
1198
937
|
Actions,
|
|
1199
938
|
Button,
|
|
1200
939
|
Device,
|
|
@@ -1204,4 +943,4 @@ module.exports = {
|
|
|
1204
943
|
Origin,
|
|
1205
944
|
Pointer,
|
|
1206
945
|
INTERNAL_COMPUTE_OFFSET_SCRIPT,
|
|
1207
|
-
}
|
|
946
|
+
}
|