selenium-webdriver 4.0.0-alpha.5 → 4.0.0-beta.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/CHANGES.md +35 -1
- package/LICENSE +1 -1
- package/NOTICE +1 -1
- package/README.md +4 -4
- package/chrome.js +72 -592
- package/chromium.js +829 -0
- package/devtools/CDPConnection.js +37 -0
- package/edge.js +124 -86
- package/example/chrome_android.js +16 -13
- package/example/chrome_mobile_emulation.js +18 -14
- package/example/firefox_channels.js +35 -28
- package/example/google_search.js +9 -9
- package/example/google_search_test.js +20 -22
- package/example/headless.js +27 -19
- package/example/logging.js +34 -38
- package/firefox.js +200 -200
- package/http/index.js +131 -115
- package/http/util.js +56 -64
- package/ie.js +180 -104
- package/index.js +246 -199
- package/io/exec.js +34 -39
- package/io/index.js +142 -152
- package/io/zip.js +75 -65
- package/lib/actions.js +140 -122
- package/lib/atoms/find-elements.js +124 -0
- package/lib/atoms/get-attribute.js +79 -59
- package/lib/atoms/is-displayed.js +72 -73
- package/lib/atoms/mutation-listener.js +38 -0
- package/lib/by.js +192 -65
- package/lib/capabilities.js +82 -78
- package/lib/command.js +20 -20
- package/lib/error.js +134 -168
- package/lib/http.js +337 -230
- package/lib/input.js +325 -302
- package/lib/logging.js +127 -138
- package/lib/promise.js +60 -66
- 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 +1024 -541
- package/net/index.js +30 -36
- package/net/portprober.js +31 -144
- package/opera.js +406 -0
- package/package.json +19 -12
- package/proxy.js +2 -2
- package/remote/index.js +190 -187
- package/safari.js +40 -49
- package/testing/index.js +166 -155
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
|
|
@@ -152,10 +148,9 @@ Key.chord = function(...keys) {
|
|
|
152
148
|
* elements. Of particular note is the
|
|
153
149
|
* {@link selenium-webdriver/remote.FileDetector}, which should be used when
|
|
154
150
|
* running against a remote
|
|
155
|
-
* [Selenium Server](
|
|
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
|
|
@@ -478,7 +465,7 @@ Pointer.Type = {
|
|
|
478
465
|
*
|
|
479
466
|
* The length of each action tick is however long it takes the remote end to
|
|
480
467
|
* execute the actions for every device in that tick. Most actions are
|
|
481
|
-
* "
|
|
468
|
+
* "instantaneous", however, {@linkplain #pause pause} and
|
|
482
469
|
* {@linkplain #move pointer move} actions allow you to specify a duration for
|
|
483
470
|
* how long that action should take. The remote end will always wait for all
|
|
484
471
|
* actions within a tick to finish before starting the next tick, so a device
|
|
@@ -495,7 +482,7 @@ Pointer.Type = {
|
|
|
495
482
|
*
|
|
496
483
|
* This implicit waiting also applies to pauses. In the table below, even though
|
|
497
484
|
* the keyboard only defines a pause of 100 ms, the remote end will wait an
|
|
498
|
-
* additional 200 ms for the mouse
|
|
485
|
+
* additional 200 ms for the mouse move to finish before moving to Tick 2.
|
|
499
486
|
*
|
|
500
487
|
* | Device | Tick 1 | Tick 2 |
|
|
501
488
|
* | -------- | --------------------- | -------------- |
|
|
@@ -554,13 +541,13 @@ Pointer.Type = {
|
|
|
554
541
|
* when translating actions to the legacy protocol.
|
|
555
542
|
*
|
|
556
543
|
* 6. For W3C actions, move offsets relative to a
|
|
557
|
-
* {@linkplain ./webdriver.WebElement WebElement} are
|
|
544
|
+
* {@linkplain ./webdriver.WebElement WebElement} are interpreted relative
|
|
558
545
|
* to the center of an element's _first_ [client rect] in the viewport. For
|
|
559
546
|
* legacy actions, element offsets are relative to the top-left corner of
|
|
560
547
|
* the element's [bounding client rect]. When translating actions to the
|
|
561
548
|
* legacy protocol in bridge mode, an extra command must be inserted to
|
|
562
549
|
* translate move offsets from one frame of reference to the other. This
|
|
563
|
-
* extra command
|
|
550
|
+
* extra command contributes to the overall latency issue outlined in
|
|
564
551
|
* point 1.
|
|
565
552
|
*
|
|
566
553
|
* [client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects
|
|
@@ -577,43 +564,43 @@ class Actions {
|
|
|
577
564
|
* bridge: (boolean|undefined)}} options Options for this action
|
|
578
565
|
* sequence (see class description for details).
|
|
579
566
|
*/
|
|
580
|
-
constructor(executor, {async = false, bridge = false} = {}) {
|
|
567
|
+
constructor(executor, { async = false, bridge = false } = {}) {
|
|
581
568
|
if (async && bridge) {
|
|
582
569
|
throw new InvalidArgumentError(
|
|
583
|
-
|
|
570
|
+
'Async sequences not supported with bridge mode'
|
|
571
|
+
)
|
|
584
572
|
}
|
|
585
573
|
|
|
586
574
|
/** @private @const */
|
|
587
|
-
this.executor_ = executor
|
|
575
|
+
this.executor_ = executor
|
|
588
576
|
|
|
589
577
|
/** @private @const */
|
|
590
|
-
this.sync_ = !async
|
|
578
|
+
this.sync_ = !async
|
|
591
579
|
|
|
592
580
|
/** @private @const */
|
|
593
|
-
this.bridge_ = !!bridge
|
|
581
|
+
this.bridge_ = !!bridge
|
|
594
582
|
|
|
595
583
|
/** @private @const */
|
|
596
|
-
this.keyboard_ = new Keyboard('default keyboard')
|
|
584
|
+
this.keyboard_ = new Keyboard('default keyboard')
|
|
597
585
|
|
|
598
586
|
/** @private @const */
|
|
599
|
-
this.mouse_ = new Pointer('default mouse', Pointer.Type.MOUSE)
|
|
587
|
+
this.mouse_ = new Pointer('default mouse', Pointer.Type.MOUSE)
|
|
600
588
|
|
|
601
589
|
/** @private @const {!Map<!Device, !Array<!Action>>} */
|
|
602
590
|
this.sequences_ = new Map([
|
|
603
591
|
[this.keyboard_, []],
|
|
604
592
|
[this.mouse_, []],
|
|
605
|
-
])
|
|
593
|
+
])
|
|
606
594
|
}
|
|
607
595
|
|
|
608
|
-
|
|
609
596
|
/** @return {!Keyboard} the keyboard device handle. */
|
|
610
597
|
keyboard() {
|
|
611
|
-
return this.keyboard_
|
|
598
|
+
return this.keyboard_
|
|
612
599
|
}
|
|
613
600
|
|
|
614
601
|
/** @return {!Pointer} the mouse pointer device handle. */
|
|
615
602
|
mouse() {
|
|
616
|
-
return this.mouse_
|
|
603
|
+
return this.mouse_
|
|
617
604
|
}
|
|
618
605
|
|
|
619
606
|
/**
|
|
@@ -622,12 +609,12 @@ class Actions {
|
|
|
622
609
|
* @private
|
|
623
610
|
*/
|
|
624
611
|
sequence_(device) {
|
|
625
|
-
let sequence = this.sequences_.get(device)
|
|
612
|
+
let sequence = this.sequences_.get(device)
|
|
626
613
|
if (!sequence) {
|
|
627
|
-
sequence = []
|
|
628
|
-
this.sequences_.set(device, sequence)
|
|
614
|
+
sequence = []
|
|
615
|
+
this.sequences_.set(device, sequence)
|
|
629
616
|
}
|
|
630
|
-
return sequence
|
|
617
|
+
return sequence
|
|
631
618
|
}
|
|
632
619
|
|
|
633
620
|
/**
|
|
@@ -641,8 +628,8 @@ class Actions {
|
|
|
641
628
|
* @return {!Actions} a self reference.
|
|
642
629
|
*/
|
|
643
630
|
insert(device, ...actions) {
|
|
644
|
-
this.sequence_(device).push(...actions)
|
|
645
|
-
return this.sync_ ? this.synchronize() : this
|
|
631
|
+
this.sequence_(device).push(...actions)
|
|
632
|
+
return this.sync_ ? this.synchronize() : this
|
|
646
633
|
}
|
|
647
634
|
|
|
648
635
|
/**
|
|
@@ -657,30 +644,30 @@ class Actions {
|
|
|
657
644
|
* @return {!Actions} a self reference.
|
|
658
645
|
*/
|
|
659
646
|
synchronize(...devices) {
|
|
660
|
-
let sequences
|
|
661
|
-
let max = 0
|
|
647
|
+
let sequences
|
|
648
|
+
let max = 0
|
|
662
649
|
if (devices.length === 0) {
|
|
663
650
|
for (const s of this.sequences_.values()) {
|
|
664
|
-
max = Math.max(max, s.length)
|
|
651
|
+
max = Math.max(max, s.length)
|
|
665
652
|
}
|
|
666
|
-
sequences = this.sequences_.values()
|
|
653
|
+
sequences = this.sequences_.values()
|
|
667
654
|
} else {
|
|
668
|
-
sequences = []
|
|
655
|
+
sequences = []
|
|
669
656
|
for (const device of devices) {
|
|
670
|
-
const seq = this.sequence_(device)
|
|
671
|
-
max = Math.max(max, seq.length)
|
|
672
|
-
sequences.push(seq)
|
|
657
|
+
const seq = this.sequence_(device)
|
|
658
|
+
max = Math.max(max, seq.length)
|
|
659
|
+
sequences.push(seq)
|
|
673
660
|
}
|
|
674
661
|
}
|
|
675
662
|
|
|
676
|
-
const pause = {type: Action.Type.PAUSE, duration: 0}
|
|
663
|
+
const pause = { type: Action.Type.PAUSE, duration: 0 }
|
|
677
664
|
for (const seq of sequences) {
|
|
678
665
|
while (seq.length < max) {
|
|
679
|
-
seq.push(pause)
|
|
666
|
+
seq.push(pause)
|
|
680
667
|
}
|
|
681
668
|
}
|
|
682
669
|
|
|
683
|
-
return this
|
|
670
|
+
return this
|
|
684
671
|
}
|
|
685
672
|
|
|
686
673
|
/**
|
|
@@ -692,7 +679,7 @@ class Actions {
|
|
|
692
679
|
* If no devices are specified, a pause action will be created (using the same
|
|
693
680
|
* duration) for every device.
|
|
694
681
|
*
|
|
695
|
-
* When device
|
|
682
|
+
* When device synchronization is enabled (the default for new {@link Actions}
|
|
696
683
|
* objects), there is no need to specify devices as pausing one automatically
|
|
697
684
|
* pauses the others for the same duration. In other words, the following are
|
|
698
685
|
* all equivalent:
|
|
@@ -735,21 +722,21 @@ class Actions {
|
|
|
735
722
|
*/
|
|
736
723
|
pause(duration, ...devices) {
|
|
737
724
|
if (duration instanceof Device) {
|
|
738
|
-
devices.push(duration)
|
|
739
|
-
duration = 0
|
|
725
|
+
devices.push(duration)
|
|
726
|
+
duration = 0
|
|
740
727
|
} else if (!duration) {
|
|
741
|
-
duration = 0
|
|
728
|
+
duration = 0
|
|
742
729
|
}
|
|
743
730
|
|
|
744
|
-
const action = {type: Action.Type.PAUSE, duration}
|
|
731
|
+
const action = { type: Action.Type.PAUSE, duration }
|
|
745
732
|
|
|
746
733
|
// NB: need a properly typed variable for type checking.
|
|
747
734
|
/** @type {!Iterable<!Device>} */
|
|
748
|
-
const iterable = devices.length === 0 ? this.sequences_.keys() : devices
|
|
735
|
+
const iterable = devices.length === 0 ? this.sequences_.keys() : devices
|
|
749
736
|
for (const device of iterable) {
|
|
750
|
-
this.sequence_(device).push(action)
|
|
737
|
+
this.sequence_(device).push(action)
|
|
751
738
|
}
|
|
752
|
-
return this.sync_ ? this.synchronize() : this
|
|
739
|
+
return this.sync_ ? this.synchronize() : this
|
|
753
740
|
}
|
|
754
741
|
|
|
755
742
|
/**
|
|
@@ -761,7 +748,7 @@ class Actions {
|
|
|
761
748
|
* @return {!Actions} a self reference.
|
|
762
749
|
*/
|
|
763
750
|
keyDown(key) {
|
|
764
|
-
return this.insert(this.keyboard_, this.keyboard_.keyDown(key))
|
|
751
|
+
return this.insert(this.keyboard_, this.keyboard_.keyDown(key))
|
|
765
752
|
}
|
|
766
753
|
|
|
767
754
|
/**
|
|
@@ -773,7 +760,7 @@ class Actions {
|
|
|
773
760
|
* @return {!Actions} a self reference.
|
|
774
761
|
*/
|
|
775
762
|
keyUp(key) {
|
|
776
|
-
return this.insert(this.keyboard_, this.keyboard_.keyUp(key))
|
|
763
|
+
return this.insert(this.keyboard_, this.keyboard_.keyUp(key))
|
|
777
764
|
}
|
|
778
765
|
|
|
779
766
|
/**
|
|
@@ -788,21 +775,20 @@ class Actions {
|
|
|
788
775
|
* @return {!Actions} a self reference.
|
|
789
776
|
*/
|
|
790
777
|
sendKeys(...keys) {
|
|
791
|
-
const actions = []
|
|
778
|
+
const actions = []
|
|
792
779
|
for (const key of keys) {
|
|
793
780
|
if (typeof key === 'string') {
|
|
794
781
|
for (const symbol of key) {
|
|
795
782
|
actions.push(
|
|
796
|
-
|
|
797
|
-
|
|
783
|
+
this.keyboard_.keyDown(symbol),
|
|
784
|
+
this.keyboard_.keyUp(symbol)
|
|
785
|
+
)
|
|
798
786
|
}
|
|
799
787
|
} else {
|
|
800
|
-
actions.push(
|
|
801
|
-
this.keyboard_.keyDown(key),
|
|
802
|
-
this.keyboard_.keyUp(key));
|
|
788
|
+
actions.push(this.keyboard_.keyDown(key), this.keyboard_.keyUp(key))
|
|
803
789
|
}
|
|
804
790
|
}
|
|
805
|
-
return this.insert(this.keyboard_, ...actions)
|
|
791
|
+
return this.insert(this.keyboard_, ...actions)
|
|
806
792
|
}
|
|
807
793
|
|
|
808
794
|
/**
|
|
@@ -812,7 +798,7 @@ class Actions {
|
|
|
812
798
|
* @return {!Actions} a self reference.
|
|
813
799
|
*/
|
|
814
800
|
press(button = Button.LEFT) {
|
|
815
|
-
return this.insert(this.mouse_, this.mouse_.press(button))
|
|
801
|
+
return this.insert(this.mouse_, this.mouse_.press(button))
|
|
816
802
|
}
|
|
817
803
|
|
|
818
804
|
/**
|
|
@@ -823,7 +809,7 @@ class Actions {
|
|
|
823
809
|
* @return {!Actions} a self reference.
|
|
824
810
|
*/
|
|
825
811
|
release(button = Button.LEFT) {
|
|
826
|
-
return this.insert(this.mouse_, this.mouse_.release(button))
|
|
812
|
+
return this.insert(this.mouse_, this.mouse_.release(button))
|
|
827
813
|
}
|
|
828
814
|
|
|
829
815
|
/**
|
|
@@ -847,9 +833,11 @@ class Actions {
|
|
|
847
833
|
* corner of the viewport over 100ms.
|
|
848
834
|
* @return {!Actions} a self reference.
|
|
849
835
|
*/
|
|
850
|
-
move({x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT} = {}) {
|
|
836
|
+
move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT } = {}) {
|
|
851
837
|
return this.insert(
|
|
852
|
-
|
|
838
|
+
this.mouse_,
|
|
839
|
+
this.mouse_.move({ x, y, duration, origin })
|
|
840
|
+
)
|
|
853
841
|
}
|
|
854
842
|
|
|
855
843
|
/**
|
|
@@ -862,9 +850,9 @@ class Actions {
|
|
|
862
850
|
*/
|
|
863
851
|
click(element) {
|
|
864
852
|
if (element) {
|
|
865
|
-
this.move({origin: element})
|
|
853
|
+
this.move({ origin: element })
|
|
866
854
|
}
|
|
867
|
-
return this.press().release()
|
|
855
|
+
return this.press().release()
|
|
868
856
|
}
|
|
869
857
|
|
|
870
858
|
/**
|
|
@@ -877,9 +865,9 @@ class Actions {
|
|
|
877
865
|
*/
|
|
878
866
|
contextClick(element) {
|
|
879
867
|
if (element) {
|
|
880
|
-
this.move({origin: element})
|
|
868
|
+
this.move({ origin: element })
|
|
881
869
|
}
|
|
882
|
-
return this.press(Button.RIGHT).release(Button.RIGHT)
|
|
870
|
+
return this.press(Button.RIGHT).release(Button.RIGHT)
|
|
883
871
|
}
|
|
884
872
|
|
|
885
873
|
/**
|
|
@@ -891,7 +879,7 @@ class Actions {
|
|
|
891
879
|
* @return {!Actions} a self reference.
|
|
892
880
|
*/
|
|
893
881
|
doubleClick(element) {
|
|
894
|
-
return this.click(element).press().release()
|
|
882
|
+
return this.click(element).press().release()
|
|
895
883
|
}
|
|
896
884
|
|
|
897
885
|
/**
|
|
@@ -913,20 +901,23 @@ class Actions {
|
|
|
913
901
|
*/
|
|
914
902
|
dragAndDrop(from, to) {
|
|
915
903
|
// Do not require up top to avoid a cycle that breaks static analysis.
|
|
916
|
-
const {WebElement} = require('./webdriver')
|
|
917
|
-
if (
|
|
918
|
-
|
|
904
|
+
const { WebElement } = require('./webdriver')
|
|
905
|
+
if (
|
|
906
|
+
!(to instanceof WebElement) &&
|
|
907
|
+
(!to || typeof to.x !== 'number' || typeof to.y !== 'number')
|
|
908
|
+
) {
|
|
919
909
|
throw new InvalidArgumentError(
|
|
920
|
-
|
|
910
|
+
'Invalid drag target; must specify a WebElement or {x, y} offset'
|
|
911
|
+
)
|
|
921
912
|
}
|
|
922
913
|
|
|
923
|
-
this.move({origin: from}).press()
|
|
914
|
+
this.move({ origin: from }).press()
|
|
924
915
|
if (to instanceof WebElement) {
|
|
925
|
-
this.move({origin: to})
|
|
916
|
+
this.move({ origin: to })
|
|
926
917
|
} else {
|
|
927
|
-
this.move({x: to.x, y: to.y, origin: Origin.POINTER})
|
|
918
|
+
this.move({ x: to.x, y: to.y, origin: Origin.POINTER })
|
|
928
919
|
}
|
|
929
|
-
return this.release()
|
|
920
|
+
return this.release()
|
|
930
921
|
}
|
|
931
922
|
|
|
932
923
|
/**
|
|
@@ -937,9 +928,9 @@ class Actions {
|
|
|
937
928
|
*/
|
|
938
929
|
clear() {
|
|
939
930
|
for (const s of this.sequences_.values()) {
|
|
940
|
-
s.length = 0
|
|
931
|
+
s.length = 0
|
|
941
932
|
}
|
|
942
|
-
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
|
|
933
|
+
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
|
|
943
934
|
}
|
|
944
935
|
|
|
945
936
|
/**
|
|
@@ -949,45 +940,47 @@ class Actions {
|
|
|
949
940
|
* been completed.
|
|
950
941
|
*/
|
|
951
942
|
async perform() {
|
|
952
|
-
const _actions = []
|
|
943
|
+
const _actions = []
|
|
953
944
|
this.sequences_.forEach((actions, device) => {
|
|
954
945
|
if (!isIdle(actions)) {
|
|
955
|
-
actions = actions.concat()
|
|
956
|
-
_actions.push(Object.assign({actions}, device.toJSON()))
|
|
946
|
+
actions = actions.concat() // Defensive copy.
|
|
947
|
+
_actions.push(Object.assign({ actions }, device.toJSON()))
|
|
957
948
|
}
|
|
958
|
-
})
|
|
949
|
+
})
|
|
959
950
|
|
|
960
951
|
if (_actions.length === 0) {
|
|
961
|
-
return Promise.resolve()
|
|
952
|
+
return Promise.resolve()
|
|
962
953
|
}
|
|
963
954
|
|
|
964
955
|
try {
|
|
965
956
|
await this.executor_.execute(
|
|
966
|
-
|
|
957
|
+
new Command(Name.ACTIONS).setParameter('actions', _actions)
|
|
958
|
+
)
|
|
967
959
|
} catch (ex) {
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
960
|
+
if (
|
|
961
|
+
this.bridge_ &&
|
|
962
|
+
(ex instanceof UnknownCommandError ||
|
|
963
|
+
ex instanceof UnsupportedOperationError)
|
|
964
|
+
) {
|
|
965
|
+
return executeLegacy(this.executor_, this.sequences_)
|
|
972
966
|
}
|
|
973
|
-
throw ex
|
|
967
|
+
throw ex
|
|
974
968
|
}
|
|
975
969
|
}
|
|
976
970
|
}
|
|
977
971
|
|
|
978
|
-
|
|
979
972
|
/**
|
|
980
973
|
* @param {!Array<!Action>} actions
|
|
981
974
|
* @return {boolean}
|
|
982
975
|
*/
|
|
983
976
|
function isIdle(actions) {
|
|
984
|
-
return
|
|
985
|
-
|
|
977
|
+
return (
|
|
978
|
+
actions.length === 0 ||
|
|
979
|
+
actions.every((a) => a.type === Action.Type.PAUSE && !a.duration)
|
|
980
|
+
)
|
|
986
981
|
}
|
|
987
982
|
|
|
988
|
-
|
|
989
|
-
const MODIFIER_KEYS = new Set([Key.ALT, Key.CONTROL, Key.SHIFT, Key.COMMAND]);
|
|
990
|
-
|
|
983
|
+
const MODIFIER_KEYS = new Set([Key.ALT, Key.CONTROL, Key.SHIFT, Key.COMMAND])
|
|
991
984
|
|
|
992
985
|
/**
|
|
993
986
|
* @param {!Executor} executor
|
|
@@ -996,24 +989,27 @@ const MODIFIER_KEYS = new Set([Key.ALT, Key.CONTROL, Key.SHIFT, Key.COMMAND]);
|
|
|
996
989
|
* @suppress {deprecated} Ignore warnings about using LegacyActionSequence.
|
|
997
990
|
*/
|
|
998
991
|
async function executeLegacy(executor, sequences) {
|
|
999
|
-
let maxLength = 0
|
|
1000
|
-
sequences.forEach(seq => maxLength = Math.max(maxLength, seq.length))
|
|
992
|
+
let maxLength = 0
|
|
993
|
+
sequences.forEach((seq) => (maxLength = Math.max(maxLength, seq.length)))
|
|
1001
994
|
|
|
1002
|
-
const actions = []
|
|
995
|
+
const actions = []
|
|
1003
996
|
for (let i = 0; i < maxLength; i++) {
|
|
1004
|
-
let next
|
|
997
|
+
let next
|
|
1005
998
|
for (const device of sequences.keys()) {
|
|
1006
|
-
const seq = sequences.get(device)
|
|
999
|
+
const seq = sequences.get(device)
|
|
1007
1000
|
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1001
|
+
if (
|
|
1002
|
+
device instanceof Pointer &&
|
|
1003
|
+
device.pointerType_ !== Pointer.Type.MOUSE
|
|
1004
|
+
) {
|
|
1010
1005
|
throw new UnsupportedOperationError(
|
|
1011
|
-
|
|
1006
|
+
`${device.pointerType_} pointer not supported in bridge mode`
|
|
1007
|
+
)
|
|
1012
1008
|
}
|
|
1013
1009
|
|
|
1014
|
-
const action = seq[i]
|
|
1010
|
+
const action = seq[i]
|
|
1015
1011
|
if (!action || (action.type === Action.Type.PAUSE && !action.duration)) {
|
|
1016
|
-
continue
|
|
1012
|
+
continue
|
|
1017
1013
|
}
|
|
1018
1014
|
|
|
1019
1015
|
// - If we've already found an action for this tick:
|
|
@@ -1024,16 +1020,17 @@ async function executeLegacy(executor, sequences) {
|
|
|
1024
1020
|
if (next) {
|
|
1025
1021
|
if (next.type === Action.Type.PAUSE) {
|
|
1026
1022
|
if (action.type === Action.Type.PAUSE) {
|
|
1027
|
-
next = next.duration > action.duration ? next : action
|
|
1023
|
+
next = next.duration > action.duration ? next : action
|
|
1028
1024
|
} else {
|
|
1029
|
-
next = action
|
|
1025
|
+
next = action
|
|
1030
1026
|
}
|
|
1031
1027
|
} else if (action.type !== Action.Type.PAUSE) {
|
|
1032
1028
|
throw new UnsupportedOperationError(
|
|
1033
|
-
|
|
1029
|
+
'parallel actions not supported in bridge mode'
|
|
1030
|
+
)
|
|
1034
1031
|
}
|
|
1035
1032
|
} else {
|
|
1036
|
-
next = action
|
|
1033
|
+
next = action
|
|
1037
1034
|
}
|
|
1038
1035
|
|
|
1039
1036
|
if (action.type === Action.Type.KEY_DOWN) {
|
|
@@ -1041,127 +1038,155 @@ async function executeLegacy(executor, sequences) {
|
|
|
1041
1038
|
// must be a keyup for the same key, otherwise it cannot be translated
|
|
1042
1039
|
// to the legacy action API.
|
|
1043
1040
|
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1044
|
-
const nextAction = seq[i + 1]
|
|
1045
|
-
if (
|
|
1046
|
-
|
|
1047
|
-
|
|
1041
|
+
const nextAction = seq[i + 1]
|
|
1042
|
+
if (
|
|
1043
|
+
!nextAction ||
|
|
1044
|
+
nextAction.type !== Action.Type.KEY_UP ||
|
|
1045
|
+
nextAction.value !== action.value
|
|
1046
|
+
) {
|
|
1048
1047
|
throw new UnsupportedOperationError(
|
|
1049
|
-
|
|
1050
|
-
|
|
1048
|
+
`in bridge mode, keydown for <${action.value}> must be followed` +
|
|
1049
|
+
' by a keyup for the same key'
|
|
1050
|
+
)
|
|
1051
1051
|
}
|
|
1052
1052
|
}
|
|
1053
|
-
} else if (
|
|
1054
|
-
|
|
1055
|
-
|
|
1053
|
+
} else if (
|
|
1054
|
+
action.type === Action.Type.KEY_UP &&
|
|
1055
|
+
!MODIFIER_KEYS.has(action.value)
|
|
1056
|
+
) {
|
|
1057
|
+
next = null
|
|
1056
1058
|
}
|
|
1057
1059
|
}
|
|
1058
1060
|
|
|
1059
1061
|
if (next) {
|
|
1060
|
-
actions.push(next)
|
|
1062
|
+
actions.push(next)
|
|
1061
1063
|
}
|
|
1062
1064
|
}
|
|
1063
1065
|
|
|
1064
1066
|
for (let i = 0; i < actions.length; i++) {
|
|
1065
|
-
const action = actions[i]
|
|
1067
|
+
const action = actions[i]
|
|
1066
1068
|
switch (action.type) {
|
|
1067
|
-
case Action.Type.PAUSE:
|
|
1068
|
-
await promise.delayed(action.duration || 0)
|
|
1069
|
-
break
|
|
1070
|
-
|
|
1071
|
-
|
|
1069
|
+
case Action.Type.PAUSE: {
|
|
1070
|
+
await promise.delayed(action.duration || 0)
|
|
1071
|
+
break
|
|
1072
|
+
}
|
|
1073
|
+
case Action.Type.KEY_DOWN: {
|
|
1074
|
+
const keys = [action.value]
|
|
1072
1075
|
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1076
|
+
// eslint-disable-next-line no-inner-declarations
|
|
1073
1077
|
function nextIsKeyDown() {
|
|
1074
|
-
const next = actions[i + 1]
|
|
1075
|
-
return
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
+
const next = actions[i + 1]
|
|
1079
|
+
return (
|
|
1080
|
+
!!next &&
|
|
1081
|
+
next.type === Action.Type.KEY_DOWN &&
|
|
1082
|
+
!MODIFIER_KEYS.has(next.value)
|
|
1083
|
+
)
|
|
1078
1084
|
}
|
|
1079
1085
|
for (; nextIsKeyDown(); i++) {
|
|
1080
|
-
keys.push(actions[i + 1].value)
|
|
1086
|
+
keys.push(actions[i + 1].value)
|
|
1081
1087
|
}
|
|
1082
1088
|
}
|
|
1083
1089
|
await executor.execute(
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
break
|
|
1087
|
-
|
|
1090
|
+
new Command(Name.LEGACY_ACTION_SEND_KEYS).setParameter('value', keys)
|
|
1091
|
+
)
|
|
1092
|
+
break
|
|
1093
|
+
}
|
|
1094
|
+
case Action.Type.KEY_UP: {
|
|
1088
1095
|
await executor.execute(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1096
|
+
new Command(Name.LEGACY_ACTION_SEND_KEYS).setParameter('value', [
|
|
1097
|
+
action.value,
|
|
1098
|
+
])
|
|
1099
|
+
)
|
|
1100
|
+
break
|
|
1101
|
+
}
|
|
1092
1102
|
case Action.Type.POINTER_DOWN: {
|
|
1103
|
+
// eslint-disable-next-line no-inner-declarations
|
|
1093
1104
|
function isClick(startAt) {
|
|
1094
|
-
const first = actions[startAt]
|
|
1095
|
-
const second = actions[startAt + 1]
|
|
1096
|
-
return
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1105
|
+
const first = actions[startAt]
|
|
1106
|
+
const second = actions[startAt + 1]
|
|
1107
|
+
return (
|
|
1108
|
+
!!first &&
|
|
1109
|
+
!!second &&
|
|
1110
|
+
first.type === Action.Type.POINTER_DOWN &&
|
|
1111
|
+
second.type === Action.Type.POINTER_UP &&
|
|
1112
|
+
first.button === second.button
|
|
1113
|
+
)
|
|
1100
1114
|
}
|
|
1101
1115
|
|
|
1116
|
+
// eslint-disable-next-line no-inner-declarations
|
|
1102
1117
|
function isDoubleClick(startAt) {
|
|
1103
|
-
return
|
|
1104
|
-
|
|
1105
|
-
|
|
1118
|
+
return (
|
|
1119
|
+
isClick(startAt) &&
|
|
1120
|
+
isClick(startAt + 2) &&
|
|
1121
|
+
actions[startAt].button === actions[(startAt = 2)].button
|
|
1122
|
+
)
|
|
1106
1123
|
}
|
|
1107
1124
|
|
|
1108
|
-
let cmd
|
|
1125
|
+
let cmd
|
|
1109
1126
|
if (isDoubleClick(i)) {
|
|
1110
|
-
i += 3
|
|
1111
|
-
cmd = Name.LEGACY_ACTION_DOUBLE_CLICK
|
|
1112
|
-
} else
|
|
1113
|
-
i
|
|
1114
|
-
cmd = Name.LEGACY_ACTION_CLICK
|
|
1127
|
+
i += 3 // Consume the pointer up/down/up.
|
|
1128
|
+
cmd = Name.LEGACY_ACTION_DOUBLE_CLICK
|
|
1129
|
+
} else if (isClick(i)) {
|
|
1130
|
+
i++ // Consume the pointer-up.
|
|
1131
|
+
cmd = Name.LEGACY_ACTION_CLICK
|
|
1115
1132
|
} else {
|
|
1116
|
-
cmd = Name.LEGACY_ACTION_MOUSE_DOWN
|
|
1133
|
+
cmd = Name.LEGACY_ACTION_MOUSE_DOWN
|
|
1117
1134
|
}
|
|
1118
1135
|
await executor.execute(
|
|
1119
|
-
|
|
1120
|
-
|
|
1136
|
+
new Command(cmd).setParameter('button', action.button)
|
|
1137
|
+
)
|
|
1138
|
+
break
|
|
1121
1139
|
}
|
|
1122
|
-
case Action.Type.POINTER_UP:
|
|
1140
|
+
case Action.Type.POINTER_UP: {
|
|
1123
1141
|
await executor.execute(
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1142
|
+
new Command(Name.LEGACY_ACTION_MOUSE_UP).setParameter(
|
|
1143
|
+
'button',
|
|
1144
|
+
action.button
|
|
1145
|
+
)
|
|
1146
|
+
)
|
|
1147
|
+
break
|
|
1148
|
+
}
|
|
1149
|
+
case Action.Type.POINTER_MOVE: {
|
|
1128
1150
|
if (action.origin === Origin.VIEWPORT) {
|
|
1129
1151
|
throw new UnsupportedOperationError(
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1152
|
+
`pointer movements relative to ${Origin.VIEWPORT} are not` +
|
|
1153
|
+
' supported in bridge mode'
|
|
1154
|
+
)
|
|
1133
1155
|
}
|
|
1134
1156
|
|
|
1135
|
-
let x = action.x
|
|
1136
|
-
let y = action.y
|
|
1137
|
-
const cmd = new Command(Name.LEGACY_ACTION_MOUSE_MOVE)
|
|
1157
|
+
let x = action.x
|
|
1158
|
+
let y = action.y
|
|
1159
|
+
const cmd = new Command(Name.LEGACY_ACTION_MOUSE_MOVE)
|
|
1138
1160
|
if (action.origin && action.origin !== Origin.POINTER) {
|
|
1139
|
-
const el = /** @type {!./webdriver.WebElement} */(action.origin)
|
|
1161
|
+
const el = /** @type {!./webdriver.WebElement} */ (action.origin)
|
|
1140
1162
|
|
|
1141
1163
|
// Need to translate frame of reference from center of element's first
|
|
1142
1164
|
// client rect to the top-left of its bounding client rect. See:
|
|
1143
1165
|
// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-center-point
|
|
1144
1166
|
let diff = await executor.execute(
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1167
|
+
new Command(Name.EXECUTE_SCRIPT)
|
|
1168
|
+
.setParameter('script', INTERNAL_COMPUTE_OFFSET_SCRIPT)
|
|
1169
|
+
.setParameter('args', [el])
|
|
1170
|
+
)
|
|
1171
|
+
x += diff[0]
|
|
1172
|
+
y += diff[1]
|
|
1173
|
+
|
|
1174
|
+
const id = await el.getId()
|
|
1175
|
+
cmd.setParameter('element', id)
|
|
1153
1176
|
}
|
|
1154
|
-
cmd.setParameter('xoffset', x).setParameter('yoffset', y)
|
|
1155
|
-
await executor.execute(cmd)
|
|
1156
|
-
break
|
|
1157
|
-
|
|
1177
|
+
cmd.setParameter('xoffset', x).setParameter('yoffset', y)
|
|
1178
|
+
await executor.execute(cmd)
|
|
1179
|
+
break
|
|
1180
|
+
}
|
|
1181
|
+
default: {
|
|
1158
1182
|
throw new UnsupportedOperationError(
|
|
1159
|
-
|
|
1183
|
+
`${action.type} actions not supported in bridge mode`
|
|
1184
|
+
)
|
|
1185
|
+
}
|
|
1160
1186
|
}
|
|
1161
1187
|
}
|
|
1162
1188
|
}
|
|
1163
1189
|
|
|
1164
|
-
|
|
1165
1190
|
/**
|
|
1166
1191
|
* Script used to compute the offset from the center of a DOM element's first
|
|
1167
1192
|
* client rect from the top-left corner of the element's bounding client rect.
|
|
@@ -1187,14 +1212,12 @@ function computeOffset(el) {
|
|
|
1187
1212
|
var bbox = el.getBoundingClientRect();
|
|
1188
1213
|
return [x - bbox.left, y - bbox.top];
|
|
1189
1214
|
}
|
|
1190
|
-
return computeOffset(arguments[0])
|
|
1191
|
-
|
|
1215
|
+
return computeOffset(arguments[0]);`
|
|
1192
1216
|
|
|
1193
1217
|
// PUBLIC API
|
|
1194
1218
|
|
|
1195
|
-
|
|
1196
1219
|
module.exports = {
|
|
1197
|
-
Action,
|
|
1220
|
+
Action, // For documentation only.
|
|
1198
1221
|
Actions,
|
|
1199
1222
|
Button,
|
|
1200
1223
|
Device,
|
|
@@ -1204,4 +1227,4 @@ module.exports = {
|
|
|
1204
1227
|
Origin,
|
|
1205
1228
|
Pointer,
|
|
1206
1229
|
INTERNAL_COMPUTE_OFFSET_SCRIPT,
|
|
1207
|
-
}
|
|
1230
|
+
}
|