selenium-webdriver 4.0.0-beta.2 → 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 +10 -0
- package/io/exec.js +3 -3
- package/io/index.js +8 -9
- package/io/zip.js +5 -6
- package/lib/command.js +0 -15
- package/lib/http.js +0 -29
- package/lib/input.js +2 -286
- package/lib/promise.js +4 -16
- package/package.json +1 -1
- package/remote/index.js +6 -7
- package/lib/actions.js +0 -627
package/CHANGES.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## v4.0.0-beta.3
|
|
2
|
+
|
|
3
|
+
* Removed support for legacy actions.
|
|
4
|
+
This will also remove "Bride Mode" support, which was introduced to translate of action class to legacy API.
|
|
5
|
+
* Remove devtools support for v86,v87 and adding support for v89, v90, v91
|
|
6
|
+
* make isPromise() return boolean value for null and undefined cases (#6785)
|
|
7
|
+
* code cleanup (#9158)
|
|
8
|
+
* replacing asserts equal with strictEqual and deepEqual with deepStrictEqual
|
|
9
|
+
|
|
10
|
+
|
|
1
11
|
## v4.0.0-beta.2
|
|
2
12
|
|
|
3
13
|
* Removed support for Edge legacy.
|
package/io/exec.js
CHANGED
|
@@ -121,9 +121,9 @@ class Command {
|
|
|
121
121
|
* @return {!Command} The launched command.
|
|
122
122
|
*/
|
|
123
123
|
module.exports = function exec(command, opt_options) {
|
|
124
|
-
|
|
124
|
+
const options = opt_options || {}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
let proc = childProcess.spawn(command, options.args || [], {
|
|
127
127
|
env: options.env || process.env,
|
|
128
128
|
stdio: options.stdio || 'ignore',
|
|
129
129
|
})
|
|
@@ -133,7 +133,7 @@ module.exports = function exec(command, opt_options) {
|
|
|
133
133
|
proc.unref()
|
|
134
134
|
process.once('exit', onProcessExit)
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
const result = new Promise((resolve) => {
|
|
137
137
|
proc.once('exit', (code, signal) => {
|
|
138
138
|
proc = null
|
|
139
139
|
process.removeListener('exit', onProcessExit)
|
package/io/index.js
CHANGED
|
@@ -204,13 +204,12 @@ exports.tmpDir = function () {
|
|
|
204
204
|
*/
|
|
205
205
|
exports.tmpFile = function (opt_options) {
|
|
206
206
|
return checkedCall((callback) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
207
|
+
/** check fixed in v > 0.2.1 if
|
|
208
|
+
* (typeof options === 'function') {
|
|
209
|
+
* return [{}, options];
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
tmp.file(opt_options, callback)
|
|
214
213
|
})
|
|
215
214
|
}
|
|
216
215
|
|
|
@@ -224,7 +223,7 @@ exports.tmpFile = function (opt_options) {
|
|
|
224
223
|
* not be found.
|
|
225
224
|
*/
|
|
226
225
|
exports.findInPath = function (file, opt_checkCwd) {
|
|
227
|
-
|
|
226
|
+
const dirs = []
|
|
228
227
|
if (opt_checkCwd) {
|
|
229
228
|
dirs.push(process.cwd())
|
|
230
229
|
}
|
|
@@ -329,7 +328,7 @@ exports.mkdirp = function mkdirp(dir) {
|
|
|
329
328
|
* will be relative to `rootPath`.
|
|
330
329
|
*/
|
|
331
330
|
exports.walkDir = function (rootPath) {
|
|
332
|
-
|
|
331
|
+
const seen = []
|
|
333
332
|
return (function walk(dir) {
|
|
334
333
|
return checkedCall((callback) => fs.readdir(dir, callback)).then((files) =>
|
|
335
334
|
Promise.all(
|
package/io/zip.js
CHANGED
|
@@ -180,8 +180,8 @@ function load(path) {
|
|
|
180
180
|
*/
|
|
181
181
|
function unzip(src, dst) {
|
|
182
182
|
return load(src).then((zip) => {
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
const promisedDirs = new Map()
|
|
184
|
+
const promises = []
|
|
185
185
|
|
|
186
186
|
zip.z_.forEach((relPath, file) => {
|
|
187
187
|
let p
|
|
@@ -218,7 +218,6 @@ function unzip(src, dst) {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
// PUBLIC API
|
|
221
|
-
|
|
222
|
-
exports.
|
|
223
|
-
exports.
|
|
224
|
-
exports.unzip = unzip
|
|
221
|
+
module.exports.Zip = Zip
|
|
222
|
+
module.exports.load = load
|
|
223
|
+
module.exports.unzip = unzip
|
package/lib/command.js
CHANGED
|
@@ -177,21 +177,6 @@ const Name = {
|
|
|
177
177
|
|
|
178
178
|
ACTIONS: 'actions',
|
|
179
179
|
CLEAR_ACTIONS: 'clearActions',
|
|
180
|
-
|
|
181
|
-
LEGACY_ACTION_CLICK: 'legacyAction:click',
|
|
182
|
-
LEGACY_ACTION_DOUBLE_CLICK: 'legacyAction:doubleclick',
|
|
183
|
-
LEGACY_ACTION_MOUSE_DOWN: 'legacyAction:mouseDown',
|
|
184
|
-
LEGACY_ACTION_MOUSE_UP: 'legacyAction:mouseUp',
|
|
185
|
-
LEGACY_ACTION_MOUSE_MOVE: 'legacyAction:mouseMove',
|
|
186
|
-
LEGACY_ACTION_SEND_KEYS: 'legacyAction:sendKeys',
|
|
187
|
-
LEGACY_ACTION_TOUCH_DOWN: 'legacyAction:touchDown',
|
|
188
|
-
LEGACY_ACTION_TOUCH_UP: 'legacyAction:touchUp',
|
|
189
|
-
LEGACY_ACTION_TOUCH_MOVE: 'legacyAction:touchMove',
|
|
190
|
-
LEGACY_ACTION_TOUCH_SCROLL: 'legacyAction:touchScroll',
|
|
191
|
-
LEGACY_ACTION_TOUCH_LONG_PRESS: 'legacyAction:touchLongPress',
|
|
192
|
-
LEGACY_ACTION_TOUCH_FLICK: 'legacyAction:touchFlick',
|
|
193
|
-
LEGACY_ACTION_TOUCH_SINGLE_TAP: 'legacyAction:singleTap',
|
|
194
|
-
LEGACY_ACTION_TOUCH_DOUBLE_TAP: 'legacyAction:doubleTap',
|
|
195
180
|
}
|
|
196
181
|
|
|
197
182
|
/**
|
package/lib/http.js
CHANGED
|
@@ -305,35 +305,6 @@ const COMMAND_MAP = new Map([
|
|
|
305
305
|
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
|
|
306
306
|
[cmd.Name.GET_SESSION_LOGS, post('/logs')],
|
|
307
307
|
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
|
|
308
|
-
[cmd.Name.LEGACY_ACTION_CLICK, post('/session/:sessionId/click')],
|
|
309
|
-
[
|
|
310
|
-
cmd.Name.LEGACY_ACTION_DOUBLE_CLICK,
|
|
311
|
-
post('/session/:sessionId/doubleclick'),
|
|
312
|
-
],
|
|
313
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_DOWN, post('/session/:sessionId/buttondown')],
|
|
314
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_UP, post('/session/:sessionId/buttonup')],
|
|
315
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_MOVE, post('/session/:sessionId/moveto')],
|
|
316
|
-
[cmd.Name.LEGACY_ACTION_SEND_KEYS, post('/session/:sessionId/keys')],
|
|
317
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_DOWN, post('/session/:sessionId/touch/down')],
|
|
318
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_UP, post('/session/:sessionId/touch/up')],
|
|
319
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_MOVE, post('/session/:sessionId/touch/move')],
|
|
320
|
-
[
|
|
321
|
-
cmd.Name.LEGACY_ACTION_TOUCH_SCROLL,
|
|
322
|
-
post('/session/:sessionId/touch/scroll'),
|
|
323
|
-
],
|
|
324
|
-
[
|
|
325
|
-
cmd.Name.LEGACY_ACTION_TOUCH_LONG_PRESS,
|
|
326
|
-
post('/session/:sessionId/touch/longclick'),
|
|
327
|
-
],
|
|
328
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
|
|
329
|
-
[
|
|
330
|
-
cmd.Name.LEGACY_ACTION_TOUCH_SINGLE_TAP,
|
|
331
|
-
post('/session/:sessionId/touch/click'),
|
|
332
|
-
],
|
|
333
|
-
[
|
|
334
|
-
cmd.Name.LEGACY_ACTION_TOUCH_DOUBLE_TAP,
|
|
335
|
-
post('/session/:sessionId/touch/doubleclick'),
|
|
336
|
-
],
|
|
337
308
|
])
|
|
338
309
|
|
|
339
310
|
/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
|
package/lib/input.js
CHANGED
|
@@ -489,67 +489,6 @@ Pointer.Type = {
|
|
|
489
489
|
* | Keyboard | pause(100) | keyDown(SHIFT) |
|
|
490
490
|
* | Mouse | move({duration: 300}) | |
|
|
491
491
|
*
|
|
492
|
-
*
|
|
493
|
-
* #### Bridge Mode
|
|
494
|
-
*
|
|
495
|
-
* As of January 2018, only Firefox natively supports this API. For other
|
|
496
|
-
* browsers, you may either use the
|
|
497
|
-
* {@link ./actions.LegacyActionSequence LegacyActionSequence} class, _or_ you
|
|
498
|
-
* can put the Actions class into bridge mode by passing `{bridge: true}` on
|
|
499
|
-
* creation:
|
|
500
|
-
*
|
|
501
|
-
* const actions = driver.actions({bridge: true});
|
|
502
|
-
* await actions.click(element).sendKeys('abc').perform();
|
|
503
|
-
*
|
|
504
|
-
* In bridge mode, {@link #perform perform()} will first attempt to execute the
|
|
505
|
-
* configured action sequence using the W3C action protocol. If this is rejected
|
|
506
|
-
* by the remote end, the sequence will be translated to and executed against
|
|
507
|
-
* the legacy protocol.
|
|
508
|
-
*
|
|
509
|
-
* Bridge mode __is not enabled by default__ as there are several notable
|
|
510
|
-
* differences between W3C-specified and legacy protocols. Care must be
|
|
511
|
-
* taken to configure your action sequences to account for these differences:
|
|
512
|
-
*
|
|
513
|
-
* 1. For W3C actions, the entire action sequence is executed in a single
|
|
514
|
-
* call to the remote end. For legacy sequences, multiple calls must be
|
|
515
|
-
* made for each step in the sequence. This introduces additional latency
|
|
516
|
-
* which may impact how the browser responds to the emulated user actions.
|
|
517
|
-
*
|
|
518
|
-
* 2. For the legacy actions, {@linkplain #pause pauses} are handled _locally_.
|
|
519
|
-
*
|
|
520
|
-
* 3. For legacy actions, a {@linkplain #keyDown keyDown()} for a
|
|
521
|
-
* _non-modifier key_ **must** be followed by a {@linkplain #keyUP keyUp()}
|
|
522
|
-
* for the same key. This will be handled for you if you use the
|
|
523
|
-
* {@linkplain #sendKeys sendKeys()} method.
|
|
524
|
-
*
|
|
525
|
-
* 4. Mouse movements may not be specified relative to
|
|
526
|
-
* {@linkplain ./input.Origin.VIEWPORT Origin.VIEWPORT}.
|
|
527
|
-
* All movements must be relative to an element or the mouse's current
|
|
528
|
-
* position ({@linkplain ./input.Origin.POINTER Origin.POINTER}).
|
|
529
|
-
* The {@linkplain #move move()} method defaults to viewport relative
|
|
530
|
-
* offsets, so you must always specify an appropriate origin in bridge mode:
|
|
531
|
-
*
|
|
532
|
-
* driver.actions({bridge: true})
|
|
533
|
-
* .move({x: 0, y: 0, origin: Origin.POINTER})
|
|
534
|
-
* .perform();
|
|
535
|
-
* driver.actions({bridge: true})
|
|
536
|
-
* .move({x: 0, y: 0, origin: someWebElement})
|
|
537
|
-
* .perform();
|
|
538
|
-
*
|
|
539
|
-
* 5. The legacy protocol does not support specifying the duration of a
|
|
540
|
-
* {@linkplain #move mouse movement}; any specified duration _is ignored_
|
|
541
|
-
* when translating actions to the legacy protocol.
|
|
542
|
-
*
|
|
543
|
-
* 6. For W3C actions, move offsets relative to a
|
|
544
|
-
* {@linkplain ./webdriver.WebElement WebElement} are interpreted relative
|
|
545
|
-
* to the center of an element's _first_ [client rect] in the viewport. For
|
|
546
|
-
* legacy actions, element offsets are relative to the top-left corner of
|
|
547
|
-
* the element's [bounding client rect]. When translating actions to the
|
|
548
|
-
* legacy protocol in bridge mode, an extra command must be inserted to
|
|
549
|
-
* translate move offsets from one frame of reference to the other. This
|
|
550
|
-
* extra command contributes to the overall latency issue outlined in
|
|
551
|
-
* point 1.
|
|
552
|
-
*
|
|
553
492
|
* [client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects
|
|
554
493
|
* [bounding client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
|
|
555
494
|
*
|
|
@@ -560,16 +499,10 @@ class Actions {
|
|
|
560
499
|
/**
|
|
561
500
|
* @param {!Executor} executor The object to execute the configured
|
|
562
501
|
* actions with.
|
|
563
|
-
* @param {{async: (boolean|undefined)
|
|
564
|
-
* bridge: (boolean|undefined)}} options Options for this action
|
|
502
|
+
* @param {{async: (boolean|undefined)}} options Options for this action
|
|
565
503
|
* sequence (see class description for details).
|
|
566
504
|
*/
|
|
567
|
-
constructor(executor, { async = false
|
|
568
|
-
if (async && bridge) {
|
|
569
|
-
throw new InvalidArgumentError(
|
|
570
|
-
'Async sequences not supported with bridge mode'
|
|
571
|
-
)
|
|
572
|
-
}
|
|
505
|
+
constructor(executor, { async = false } = {}) {
|
|
573
506
|
|
|
574
507
|
/** @private @const */
|
|
575
508
|
this.executor_ = executor
|
|
@@ -577,9 +510,6 @@ class Actions {
|
|
|
577
510
|
/** @private @const */
|
|
578
511
|
this.sync_ = !async
|
|
579
512
|
|
|
580
|
-
/** @private @const */
|
|
581
|
-
this.bridge_ = !!bridge
|
|
582
|
-
|
|
583
513
|
/** @private @const */
|
|
584
514
|
this.keyboard_ = new Keyboard('default keyboard')
|
|
585
515
|
|
|
@@ -957,13 +887,6 @@ class Actions {
|
|
|
957
887
|
new Command(Name.ACTIONS).setParameter('actions', _actions)
|
|
958
888
|
)
|
|
959
889
|
} catch (ex) {
|
|
960
|
-
if (
|
|
961
|
-
this.bridge_ &&
|
|
962
|
-
(ex instanceof UnknownCommandError ||
|
|
963
|
-
ex instanceof UnsupportedOperationError)
|
|
964
|
-
) {
|
|
965
|
-
return executeLegacy(this.executor_, this.sequences_)
|
|
966
|
-
}
|
|
967
890
|
throw ex
|
|
968
891
|
}
|
|
969
892
|
}
|
|
@@ -980,213 +903,6 @@ function isIdle(actions) {
|
|
|
980
903
|
)
|
|
981
904
|
}
|
|
982
905
|
|
|
983
|
-
const MODIFIER_KEYS = new Set([Key.ALT, Key.CONTROL, Key.SHIFT, Key.COMMAND])
|
|
984
|
-
|
|
985
|
-
/**
|
|
986
|
-
* @param {!Executor} executor
|
|
987
|
-
* @param {!Map<!Device, !Array<!Action>>} sequences
|
|
988
|
-
* @return {!Promise<void>}
|
|
989
|
-
* @suppress {deprecated} Ignore warnings about using LegacyActionSequence.
|
|
990
|
-
*/
|
|
991
|
-
async function executeLegacy(executor, sequences) {
|
|
992
|
-
let maxLength = 0
|
|
993
|
-
sequences.forEach((seq) => (maxLength = Math.max(maxLength, seq.length)))
|
|
994
|
-
|
|
995
|
-
const actions = []
|
|
996
|
-
for (let i = 0; i < maxLength; i++) {
|
|
997
|
-
let next
|
|
998
|
-
for (const device of sequences.keys()) {
|
|
999
|
-
const seq = sequences.get(device)
|
|
1000
|
-
|
|
1001
|
-
if (
|
|
1002
|
-
device instanceof Pointer &&
|
|
1003
|
-
device.pointerType_ !== Pointer.Type.MOUSE
|
|
1004
|
-
) {
|
|
1005
|
-
throw new UnsupportedOperationError(
|
|
1006
|
-
`${device.pointerType_} pointer not supported in bridge mode`
|
|
1007
|
-
)
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
const action = seq[i]
|
|
1011
|
-
if (!action || (action.type === Action.Type.PAUSE && !action.duration)) {
|
|
1012
|
-
continue
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// - If we've already found an action for this tick:
|
|
1016
|
-
// - If we have two pauses, use the one with a longer duration
|
|
1017
|
-
// - If one is a pause and the other isn't, use the non-pause
|
|
1018
|
-
// - Otherwise, two non-pauses is an error.
|
|
1019
|
-
// - Otherwise, we haven't selected an action yet.
|
|
1020
|
-
if (next) {
|
|
1021
|
-
if (next.type === Action.Type.PAUSE) {
|
|
1022
|
-
if (action.type === Action.Type.PAUSE) {
|
|
1023
|
-
next = next.duration > action.duration ? next : action
|
|
1024
|
-
} else {
|
|
1025
|
-
next = action
|
|
1026
|
-
}
|
|
1027
|
-
} else if (action.type !== Action.Type.PAUSE) {
|
|
1028
|
-
throw new UnsupportedOperationError(
|
|
1029
|
-
'parallel actions not supported in bridge mode'
|
|
1030
|
-
)
|
|
1031
|
-
}
|
|
1032
|
-
} else {
|
|
1033
|
-
next = action
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
if (action.type === Action.Type.KEY_DOWN) {
|
|
1037
|
-
// If this action is a keydown for a non-modifier key, the next action
|
|
1038
|
-
// must be a keyup for the same key, otherwise it cannot be translated
|
|
1039
|
-
// to the legacy action API.
|
|
1040
|
-
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1041
|
-
const nextAction = seq[i + 1]
|
|
1042
|
-
if (
|
|
1043
|
-
!nextAction ||
|
|
1044
|
-
nextAction.type !== Action.Type.KEY_UP ||
|
|
1045
|
-
nextAction.value !== action.value
|
|
1046
|
-
) {
|
|
1047
|
-
throw new UnsupportedOperationError(
|
|
1048
|
-
`in bridge mode, keydown for <${action.value}> must be followed` +
|
|
1049
|
-
' by a keyup for the same key'
|
|
1050
|
-
)
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
} else if (
|
|
1054
|
-
action.type === Action.Type.KEY_UP &&
|
|
1055
|
-
!MODIFIER_KEYS.has(action.value)
|
|
1056
|
-
) {
|
|
1057
|
-
next = null
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
if (next) {
|
|
1062
|
-
actions.push(next)
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
for (let i = 0; i < actions.length; i++) {
|
|
1067
|
-
const action = actions[i]
|
|
1068
|
-
switch (action.type) {
|
|
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]
|
|
1075
|
-
if (!MODIFIER_KEYS.has(action.value)) {
|
|
1076
|
-
// eslint-disable-next-line no-inner-declarations
|
|
1077
|
-
function nextIsKeyDown() {
|
|
1078
|
-
const next = actions[i + 1]
|
|
1079
|
-
return (
|
|
1080
|
-
!!next &&
|
|
1081
|
-
next.type === Action.Type.KEY_DOWN &&
|
|
1082
|
-
!MODIFIER_KEYS.has(next.value)
|
|
1083
|
-
)
|
|
1084
|
-
}
|
|
1085
|
-
for (; nextIsKeyDown(); i++) {
|
|
1086
|
-
keys.push(actions[i + 1].value)
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
await executor.execute(
|
|
1090
|
-
new Command(Name.LEGACY_ACTION_SEND_KEYS).setParameter('value', keys)
|
|
1091
|
-
)
|
|
1092
|
-
break
|
|
1093
|
-
}
|
|
1094
|
-
case Action.Type.KEY_UP: {
|
|
1095
|
-
await executor.execute(
|
|
1096
|
-
new Command(Name.LEGACY_ACTION_SEND_KEYS).setParameter('value', [
|
|
1097
|
-
action.value,
|
|
1098
|
-
])
|
|
1099
|
-
)
|
|
1100
|
-
break
|
|
1101
|
-
}
|
|
1102
|
-
case Action.Type.POINTER_DOWN: {
|
|
1103
|
-
// eslint-disable-next-line no-inner-declarations
|
|
1104
|
-
function isClick(startAt) {
|
|
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
|
-
)
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// eslint-disable-next-line no-inner-declarations
|
|
1117
|
-
function isDoubleClick(startAt) {
|
|
1118
|
-
return (
|
|
1119
|
-
isClick(startAt) &&
|
|
1120
|
-
isClick(startAt + 2) &&
|
|
1121
|
-
actions[startAt].button === actions[(startAt = 2)].button
|
|
1122
|
-
)
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
let cmd
|
|
1126
|
-
if (isDoubleClick(i)) {
|
|
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
|
|
1132
|
-
} else {
|
|
1133
|
-
cmd = Name.LEGACY_ACTION_MOUSE_DOWN
|
|
1134
|
-
}
|
|
1135
|
-
await executor.execute(
|
|
1136
|
-
new Command(cmd).setParameter('button', action.button)
|
|
1137
|
-
)
|
|
1138
|
-
break
|
|
1139
|
-
}
|
|
1140
|
-
case Action.Type.POINTER_UP: {
|
|
1141
|
-
await executor.execute(
|
|
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: {
|
|
1150
|
-
if (action.origin === Origin.VIEWPORT) {
|
|
1151
|
-
throw new UnsupportedOperationError(
|
|
1152
|
-
`pointer movements relative to ${Origin.VIEWPORT} are not` +
|
|
1153
|
-
' supported in bridge mode'
|
|
1154
|
-
)
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
let x = action.x
|
|
1158
|
-
let y = action.y
|
|
1159
|
-
const cmd = new Command(Name.LEGACY_ACTION_MOUSE_MOVE)
|
|
1160
|
-
if (action.origin && action.origin !== Origin.POINTER) {
|
|
1161
|
-
const el = /** @type {!./webdriver.WebElement} */ (action.origin)
|
|
1162
|
-
|
|
1163
|
-
// Need to translate frame of reference from center of element's first
|
|
1164
|
-
// client rect to the top-left of its bounding client rect. See:
|
|
1165
|
-
// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-center-point
|
|
1166
|
-
let diff = await executor.execute(
|
|
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)
|
|
1176
|
-
}
|
|
1177
|
-
cmd.setParameter('xoffset', x).setParameter('yoffset', y)
|
|
1178
|
-
await executor.execute(cmd)
|
|
1179
|
-
break
|
|
1180
|
-
}
|
|
1181
|
-
default: {
|
|
1182
|
-
throw new UnsupportedOperationError(
|
|
1183
|
-
`${action.type} actions not supported in bridge mode`
|
|
1184
|
-
)
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
906
|
/**
|
|
1191
907
|
* Script used to compute the offset from the center of a DOM element's first
|
|
1192
908
|
* client rect from the top-left corner of the element's bounding client rect.
|
package/lib/promise.js
CHANGED
|
@@ -30,17 +30,7 @@
|
|
|
30
30
|
* @return {boolean} Whether the value is a promise.
|
|
31
31
|
*/
|
|
32
32
|
function isPromise(value) {
|
|
33
|
-
|
|
34
|
-
// Use array notation so the Closure compiler does not obfuscate away our
|
|
35
|
-
// contract.
|
|
36
|
-
return (
|
|
37
|
-
value &&
|
|
38
|
-
(typeof value === 'object' || typeof value === 'function') &&
|
|
39
|
-
typeof value['then'] === 'function'
|
|
40
|
-
)
|
|
41
|
-
} catch (ex) {
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
33
|
+
return Object.prototype.toString.call(value) === '[object Promise]'
|
|
44
34
|
}
|
|
45
35
|
|
|
46
36
|
/**
|
|
@@ -50,9 +40,7 @@ function isPromise(value) {
|
|
|
50
40
|
* @return {!Promise<void>} The promise.
|
|
51
41
|
*/
|
|
52
42
|
function delayed(ms) {
|
|
53
|
-
return new Promise((resolve) =>
|
|
54
|
-
setTimeout(resolve, ms)
|
|
55
|
-
})
|
|
43
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
56
44
|
}
|
|
57
45
|
|
|
58
46
|
/**
|
|
@@ -199,8 +187,8 @@ async function filter(array, fn, self = undefined) {
|
|
|
199
187
|
|
|
200
188
|
for (let i = 0; i < n; i++) {
|
|
201
189
|
if (i in arr) {
|
|
202
|
-
|
|
203
|
-
|
|
190
|
+
const value = arr[i]
|
|
191
|
+
const include = await fn.call(self, value, i, arr)
|
|
204
192
|
if (include) {
|
|
205
193
|
values[valuesLength++] = value
|
|
206
194
|
}
|
package/package.json
CHANGED
package/remote/index.js
CHANGED
|
@@ -210,7 +210,7 @@ class DriverService {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
return resolveCommandLineFlags(this.args_).then((args) => {
|
|
213
|
-
|
|
213
|
+
const command = exec(self.executable_, {
|
|
214
214
|
args: args,
|
|
215
215
|
env: self.env_,
|
|
216
216
|
stdio: self.stdio_,
|
|
@@ -218,8 +218,8 @@ class DriverService {
|
|
|
218
218
|
|
|
219
219
|
resolveCommand(command)
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
const earlyTermination = command.result().then(function (result) {
|
|
222
|
+
const error =
|
|
223
223
|
result.code == null
|
|
224
224
|
? Error('Server was killed with ' + result.signal)
|
|
225
225
|
: Error('Server terminated early with status ' + result.code)
|
|
@@ -229,7 +229,7 @@ class DriverService {
|
|
|
229
229
|
throw error
|
|
230
230
|
})
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
let hostname = self.hostname_
|
|
233
233
|
if (!hostname) {
|
|
234
234
|
hostname =
|
|
235
235
|
(!self.loopbackOnly_ && net.getAddress()) ||
|
|
@@ -338,9 +338,8 @@ DriverService.Builder = class {
|
|
|
338
338
|
* @this {THIS}
|
|
339
339
|
* @template THIS
|
|
340
340
|
*/
|
|
341
|
-
addArguments(
|
|
342
|
-
|
|
343
|
-
this.options_.args = this.options_.args.concat(args)
|
|
341
|
+
addArguments(...arguments_) {
|
|
342
|
+
this.options_.args = this.options_.args.concat(arguments_)
|
|
344
343
|
return this
|
|
345
344
|
}
|
|
346
345
|
|
package/lib/actions.js
DELETED
|
@@ -1,627 +0,0 @@
|
|
|
1
|
-
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
2
|
-
// or more contributor license agreements. See the NOTICE file
|
|
3
|
-
// distributed with this work for additional information
|
|
4
|
-
// regarding copyright ownership. The SFC licenses this file
|
|
5
|
-
// to you under the Apache License, Version 2.0 (the
|
|
6
|
-
// "License"); you may not use this file except in compliance
|
|
7
|
-
// with the License. You may obtain a copy of the License at
|
|
8
|
-
//
|
|
9
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
//
|
|
11
|
-
// Unless required by applicable law or agreed to in writing,
|
|
12
|
-
// software distributed under the License is distributed on an
|
|
13
|
-
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
-
// KIND, either express or implied. See the License for the
|
|
15
|
-
// specific language governing permissions and limitations
|
|
16
|
-
// under the License.
|
|
17
|
-
|
|
18
|
-
'use strict'
|
|
19
|
-
|
|
20
|
-
const command = require('./command')
|
|
21
|
-
const error = require('./error')
|
|
22
|
-
const input = require('./input')
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param {!IArrayLike} args .
|
|
26
|
-
* @return {!Array} .
|
|
27
|
-
*/
|
|
28
|
-
function flatten(args) {
|
|
29
|
-
const result = []
|
|
30
|
-
for (let i = 0; i < args.length; i++) {
|
|
31
|
-
const element = args[i]
|
|
32
|
-
if (Array.isArray(element)) {
|
|
33
|
-
result.push.apply(result, flatten(element))
|
|
34
|
-
} else {
|
|
35
|
-
result.push(element)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return result
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const MODIFIER_KEYS = new Set([
|
|
42
|
-
input.Key.ALT,
|
|
43
|
-
input.Key.CONTROL,
|
|
44
|
-
input.Key.SHIFT,
|
|
45
|
-
input.Key.COMMAND,
|
|
46
|
-
])
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Checks that a key is a modifier key.
|
|
50
|
-
* @param {!input.Key} key The key to check.
|
|
51
|
-
* @throws {error.InvalidArgumentError} If the key is not a modifier key.
|
|
52
|
-
* @private
|
|
53
|
-
*/
|
|
54
|
-
function checkModifierKey(key) {
|
|
55
|
-
if (!MODIFIER_KEYS.has(key)) {
|
|
56
|
-
throw new error.InvalidArgumentError('Not a modifier key')
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Class for defining sequences of complex user interactions. Each sequence
|
|
62
|
-
* will not be executed until {@link #perform} is called.
|
|
63
|
-
*
|
|
64
|
-
* This class should not be instantiated directly. Instead, obtain an instance
|
|
65
|
-
* using {@link ./webdriver.WebDriver#actions() WebDriver.actions()}.
|
|
66
|
-
*
|
|
67
|
-
* Sample usage:
|
|
68
|
-
*
|
|
69
|
-
* new LegacyActions(driver).
|
|
70
|
-
* keyDown(Key.SHIFT).
|
|
71
|
-
* click(element1).
|
|
72
|
-
* click(element2).
|
|
73
|
-
* dragAndDrop(element3, element4).
|
|
74
|
-
* keyUp(Key.SHIFT).
|
|
75
|
-
* perform();
|
|
76
|
-
*
|
|
77
|
-
* @deprecated This class is strongly deprecated and will be removed once
|
|
78
|
-
* [Google's Chrome][Chrome] and [Microsoft's Edge][Edge] browsers
|
|
79
|
-
* support the new action sequence API.
|
|
80
|
-
*
|
|
81
|
-
* [Chrome]: https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md
|
|
82
|
-
* [Edge]: https://docs.microsoft.com/en-us/microsoft-edge/webdriver
|
|
83
|
-
*/
|
|
84
|
-
class LegacyActionSequence {
|
|
85
|
-
/**
|
|
86
|
-
* @param {!./webdriver.WebDriver} driver The driver that should be used to
|
|
87
|
-
* perform this action sequence.
|
|
88
|
-
*/
|
|
89
|
-
constructor(driver) {
|
|
90
|
-
/** @private {!./webdriver.WebDriver} */
|
|
91
|
-
this.driver_ = driver
|
|
92
|
-
|
|
93
|
-
/** @private {!Array<{description: string, command: !command.Command}>} */
|
|
94
|
-
this.actions_ = []
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Schedules an action to be executed each time {@link #perform} is called on
|
|
99
|
-
* this instance.
|
|
100
|
-
*
|
|
101
|
-
* @param {string} description A description of the command.
|
|
102
|
-
* @param {!command.Command} command The command.
|
|
103
|
-
* @private
|
|
104
|
-
*/
|
|
105
|
-
schedule_(description, command) {
|
|
106
|
-
this.actions_.push({
|
|
107
|
-
description: description,
|
|
108
|
-
command: command,
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Executes this action sequence.
|
|
114
|
-
*
|
|
115
|
-
* @return {!Promise} A promise that will be resolved once this sequence has
|
|
116
|
-
* completed.
|
|
117
|
-
*/
|
|
118
|
-
async perform() {
|
|
119
|
-
// Make a protected copy of the scheduled actions. This will protect against
|
|
120
|
-
// users defining additional commands before this sequence is actually
|
|
121
|
-
// executed.
|
|
122
|
-
const actions = this.actions_.concat()
|
|
123
|
-
for (const action of actions) {
|
|
124
|
-
await this.driver_.execute(action.command)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Moves the mouse. The location to move to may be specified in terms of the
|
|
130
|
-
* mouse's current location, an offset relative to the top-left corner of an
|
|
131
|
-
* element, or an element (in which case the middle of the element is used).
|
|
132
|
-
*
|
|
133
|
-
* @param {(!./webdriver.WebElement|{x: number, y: number})} location The
|
|
134
|
-
* location to drag to, as either another WebElement or an offset in
|
|
135
|
-
* pixels.
|
|
136
|
-
* @param {{x: number, y: number}=} opt_offset If the target {@code location}
|
|
137
|
-
* is defined as a {@link ./webdriver.WebElement}, this parameter defines
|
|
138
|
-
* an offset within that element. The offset should be specified in pixels
|
|
139
|
-
* relative to the top-left corner of the element's bounding box. If
|
|
140
|
-
* omitted, the element's center will be used as the target offset.
|
|
141
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
142
|
-
*/
|
|
143
|
-
mouseMove(location, opt_offset) {
|
|
144
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_MOUSE_MOVE)
|
|
145
|
-
|
|
146
|
-
if (typeof location.x === 'number') {
|
|
147
|
-
setOffset(/** @type {{x: number, y: number}} */ (location))
|
|
148
|
-
} else {
|
|
149
|
-
cmd.setParameter('element', location.getId())
|
|
150
|
-
if (opt_offset) {
|
|
151
|
-
setOffset(opt_offset)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
this.schedule_('mouseMove', cmd)
|
|
156
|
-
return this
|
|
157
|
-
|
|
158
|
-
/** @param {{x: number, y: number}} offset The offset to use. */
|
|
159
|
-
function setOffset(offset) {
|
|
160
|
-
cmd.setParameter('xoffset', offset.x || 0)
|
|
161
|
-
cmd.setParameter('yoffset', offset.y || 0)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Schedules a mouse action.
|
|
167
|
-
* @param {string} description A simple descriptive label for the scheduled
|
|
168
|
-
* action.
|
|
169
|
-
* @param {!command.Name} commandName The name of the command.
|
|
170
|
-
* @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
|
|
171
|
-
* the element to interact with or the button to click with.
|
|
172
|
-
* Defaults to {@link input.Button.LEFT} if neither an element nor
|
|
173
|
-
* button is specified.
|
|
174
|
-
* @param {input.Button=} opt_button The button to use. Defaults to
|
|
175
|
-
* {@link input.Button.LEFT}. Ignored if the previous argument is
|
|
176
|
-
* provided as a button.
|
|
177
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
178
|
-
* @private
|
|
179
|
-
*/
|
|
180
|
-
scheduleMouseAction_(
|
|
181
|
-
description,
|
|
182
|
-
commandName,
|
|
183
|
-
opt_elementOrButton,
|
|
184
|
-
opt_button
|
|
185
|
-
) {
|
|
186
|
-
let button
|
|
187
|
-
if (typeof opt_elementOrButton === 'number') {
|
|
188
|
-
button = opt_elementOrButton
|
|
189
|
-
} else {
|
|
190
|
-
if (opt_elementOrButton) {
|
|
191
|
-
this.mouseMove(
|
|
192
|
-
/** @type {!./webdriver.WebElement} */ (opt_elementOrButton)
|
|
193
|
-
)
|
|
194
|
-
}
|
|
195
|
-
button = opt_button !== void 0 ? opt_button : input.Button.LEFT
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
let cmd = new command.Command(commandName).setParameter('button', button)
|
|
199
|
-
this.schedule_(description, cmd)
|
|
200
|
-
return this
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Presses a mouse button. The mouse button will not be released until
|
|
205
|
-
* {@link #mouseUp} is called, regardless of whether that call is made in this
|
|
206
|
-
* sequence or another. The behavior for out-of-order events (e.g. mouseDown,
|
|
207
|
-
* click) is undefined.
|
|
208
|
-
*
|
|
209
|
-
* If an element is provided, the mouse will first be moved to the center
|
|
210
|
-
* of that element. This is equivalent to:
|
|
211
|
-
*
|
|
212
|
-
* sequence.mouseMove(element).mouseDown()
|
|
213
|
-
*
|
|
214
|
-
* Warning: this method currently only supports the left mouse button. See
|
|
215
|
-
* [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
|
|
216
|
-
*
|
|
217
|
-
* @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
|
|
218
|
-
* the element to interact with or the button to click with.
|
|
219
|
-
* Defaults to {@link input.Button.LEFT} if neither an element nor
|
|
220
|
-
* button is specified.
|
|
221
|
-
* @param {input.Button=} opt_button The button to use. Defaults to
|
|
222
|
-
* {@link input.Button.LEFT}. Ignored if a button is provided as the
|
|
223
|
-
* first argument.
|
|
224
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
225
|
-
*/
|
|
226
|
-
mouseDown(opt_elementOrButton, opt_button) {
|
|
227
|
-
return this.scheduleMouseAction_(
|
|
228
|
-
'mouseDown',
|
|
229
|
-
command.Name.LEGACY_ACTION_MOUSE_DOWN,
|
|
230
|
-
opt_elementOrButton,
|
|
231
|
-
opt_button
|
|
232
|
-
)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Releases a mouse button. Behavior is undefined for calling this function
|
|
237
|
-
* without a previous call to {@link #mouseDown}.
|
|
238
|
-
*
|
|
239
|
-
* If an element is provided, the mouse will first be moved to the center
|
|
240
|
-
* of that element. This is equivalent to:
|
|
241
|
-
*
|
|
242
|
-
* sequence.mouseMove(element).mouseUp()
|
|
243
|
-
*
|
|
244
|
-
* Warning: this method currently only supports the left mouse button. See
|
|
245
|
-
* [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
|
|
246
|
-
*
|
|
247
|
-
* @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
|
|
248
|
-
* the element to interact with or the button to click with.
|
|
249
|
-
* Defaults to {@link input.Button.LEFT} if neither an element nor
|
|
250
|
-
* button is specified.
|
|
251
|
-
* @param {input.Button=} opt_button The button to use. Defaults to
|
|
252
|
-
* {@link input.Button.LEFT}. Ignored if a button is provided as the
|
|
253
|
-
* first argument.
|
|
254
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
255
|
-
*/
|
|
256
|
-
mouseUp(opt_elementOrButton, opt_button) {
|
|
257
|
-
return this.scheduleMouseAction_(
|
|
258
|
-
'mouseUp',
|
|
259
|
-
command.Name.LEGACY_ACTION_MOUSE_UP,
|
|
260
|
-
opt_elementOrButton,
|
|
261
|
-
opt_button
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Convenience function for performing a "drag and drop" maneuver. The target
|
|
267
|
-
* element may be moved to the location of another element, or by an offset (in
|
|
268
|
-
* pixels).
|
|
269
|
-
*
|
|
270
|
-
* @param {!./webdriver.WebElement} element The element to drag.
|
|
271
|
-
* @param {(!./webdriver.WebElement|{x: number, y: number})} location The
|
|
272
|
-
* location to drag to, either as another WebElement or an offset in
|
|
273
|
-
* pixels.
|
|
274
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
275
|
-
*/
|
|
276
|
-
dragAndDrop(element, location) {
|
|
277
|
-
return this.mouseDown(element).mouseMove(location).mouseUp()
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Clicks a mouse button.
|
|
282
|
-
*
|
|
283
|
-
* If an element is provided, the mouse will first be moved to the center
|
|
284
|
-
* of that element. This is equivalent to:
|
|
285
|
-
*
|
|
286
|
-
* sequence.mouseMove(element).click()
|
|
287
|
-
*
|
|
288
|
-
* @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
|
|
289
|
-
* the element to interact with or the button to click with.
|
|
290
|
-
* Defaults to {@link input.Button.LEFT} if neither an element nor
|
|
291
|
-
* button is specified.
|
|
292
|
-
* @param {input.Button=} opt_button The button to use. Defaults to
|
|
293
|
-
* {@link input.Button.LEFT}. Ignored if a button is provided as the
|
|
294
|
-
* first argument.
|
|
295
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
296
|
-
*/
|
|
297
|
-
click(opt_elementOrButton, opt_button) {
|
|
298
|
-
return this.scheduleMouseAction_(
|
|
299
|
-
'click',
|
|
300
|
-
command.Name.LEGACY_ACTION_CLICK,
|
|
301
|
-
opt_elementOrButton,
|
|
302
|
-
opt_button
|
|
303
|
-
)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Double-clicks a mouse button.
|
|
308
|
-
*
|
|
309
|
-
* If an element is provided, the mouse will first be moved to the center of
|
|
310
|
-
* that element. This is equivalent to:
|
|
311
|
-
*
|
|
312
|
-
* sequence.mouseMove(element).doubleClick()
|
|
313
|
-
*
|
|
314
|
-
* Warning: this method currently only supports the left mouse button. See
|
|
315
|
-
* [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
|
|
316
|
-
*
|
|
317
|
-
* @param {(./webdriver.WebElement|input.Button)=} opt_elementOrButton Either
|
|
318
|
-
* the element to interact with or the button to click with.
|
|
319
|
-
* Defaults to {@link input.Button.LEFT} if neither an element nor
|
|
320
|
-
* button is specified.
|
|
321
|
-
* @param {input.Button=} opt_button The button to use. Defaults to
|
|
322
|
-
* {@link input.Button.LEFT}. Ignored if a button is provided as the
|
|
323
|
-
* first argument.
|
|
324
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
325
|
-
*/
|
|
326
|
-
doubleClick(opt_elementOrButton, opt_button) {
|
|
327
|
-
return this.scheduleMouseAction_(
|
|
328
|
-
'doubleClick',
|
|
329
|
-
command.Name.LEGACY_ACTION_DOUBLE_CLICK,
|
|
330
|
-
opt_elementOrButton,
|
|
331
|
-
opt_button
|
|
332
|
-
)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Schedules a keyboard action.
|
|
337
|
-
*
|
|
338
|
-
* @param {string} description A simple descriptive label for the scheduled
|
|
339
|
-
* action.
|
|
340
|
-
* @param {!Array<(string|!input.Key)>} keys The keys to send.
|
|
341
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
342
|
-
* @private
|
|
343
|
-
*/
|
|
344
|
-
scheduleKeyboardAction_(description, keys) {
|
|
345
|
-
let cmd = new command.Command(
|
|
346
|
-
command.Name.LEGACY_ACTION_SEND_KEYS
|
|
347
|
-
).setParameter('value', keys)
|
|
348
|
-
this.schedule_(description, cmd)
|
|
349
|
-
return this
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Performs a modifier key press. The modifier key is <em>not released</em>
|
|
354
|
-
* until {@link #keyUp} or {@link #sendKeys} is called. The key press will be
|
|
355
|
-
* targeted at the currently focused element.
|
|
356
|
-
*
|
|
357
|
-
* @param {!input.Key} key The modifier key to push. Must be one of
|
|
358
|
-
* {ALT, CONTROL, SHIFT, COMMAND, META}.
|
|
359
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
360
|
-
* @throws {error.InvalidArgumentError} If the key is not a valid modifier
|
|
361
|
-
* key.
|
|
362
|
-
*/
|
|
363
|
-
keyDown(key) {
|
|
364
|
-
checkModifierKey(key)
|
|
365
|
-
return this.scheduleKeyboardAction_('keyDown', [key])
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Performs a modifier key release. The release is targeted at the currently
|
|
370
|
-
* focused element.
|
|
371
|
-
* @param {!input.Key} key The modifier key to release. Must be one of
|
|
372
|
-
* {ALT, CONTROL, SHIFT, COMMAND, META}.
|
|
373
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
374
|
-
* @throws {error.InvalidArgumentError} If the key is not a valid modifier
|
|
375
|
-
* key.
|
|
376
|
-
*/
|
|
377
|
-
keyUp(key) {
|
|
378
|
-
checkModifierKey(key)
|
|
379
|
-
return this.scheduleKeyboardAction_('keyUp', [key])
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Simulates typing multiple keys. Each modifier key encountered in the
|
|
384
|
-
* sequence will not be released until it is encountered again. All key events
|
|
385
|
-
* will be targeted at the currently focused element.
|
|
386
|
-
*
|
|
387
|
-
* @param {...(string|!input.Key|!Array<(string|!input.Key)>)} var_args
|
|
388
|
-
* The keys to type.
|
|
389
|
-
* @return {!LegacyActionSequence} A self reference.
|
|
390
|
-
* @throws {Error} If the key is not a valid modifier key.
|
|
391
|
-
*/
|
|
392
|
-
sendKeys(_var_args) {
|
|
393
|
-
// eslint-disable-line
|
|
394
|
-
const keys = flatten(arguments)
|
|
395
|
-
return this.scheduleKeyboardAction_('sendKeys', keys)
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Class for defining sequences of user touch interactions. Each sequence
|
|
401
|
-
* will not be executed until {@link #perform} is called.
|
|
402
|
-
*
|
|
403
|
-
* This class should not be instantiated directly. Instead, obtain an instance
|
|
404
|
-
* using {@link ./webdriver.WebDriver#touchActions() WebDriver.touchActions()}.
|
|
405
|
-
*
|
|
406
|
-
* Sample usage:
|
|
407
|
-
*
|
|
408
|
-
* new LegacyActions(driver).
|
|
409
|
-
* tapAndHold({x: 0, y: 0}).
|
|
410
|
-
* move({x: 3, y: 4}).
|
|
411
|
-
* release({x: 10, y: 10}).
|
|
412
|
-
* perform();
|
|
413
|
-
*
|
|
414
|
-
* @deprecated This class is strongly deprecated and will be removed once
|
|
415
|
-
* [Google's Chrome][Chrome] and [Microsoft's Edge][Edge] browsers
|
|
416
|
-
* support the new action sequence API.
|
|
417
|
-
*
|
|
418
|
-
* [Chrome]: https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md
|
|
419
|
-
* [Edge]: https://docs.microsoft.com/en-us/microsoft-edge/webdriver
|
|
420
|
-
*/
|
|
421
|
-
class LegacyTouchSequence {
|
|
422
|
-
/**
|
|
423
|
-
* @param {!./webdriver.WebDriver} driver The driver that should be used to
|
|
424
|
-
* perform this action sequence.
|
|
425
|
-
*/
|
|
426
|
-
constructor(driver) {
|
|
427
|
-
/** @private {!./webdriver.WebDriver} */
|
|
428
|
-
this.driver_ = driver
|
|
429
|
-
|
|
430
|
-
/** @private {!Array<{description: string, command: !command.Command}>} */
|
|
431
|
-
this.actions_ = []
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Schedules an action to be executed each time {@link #perform} is called on
|
|
436
|
-
* this instance.
|
|
437
|
-
* @param {string} description A description of the command.
|
|
438
|
-
* @param {!command.Command} command The command.
|
|
439
|
-
* @private
|
|
440
|
-
*/
|
|
441
|
-
schedule_(description, command) {
|
|
442
|
-
this.actions_.push({
|
|
443
|
-
description: description,
|
|
444
|
-
command: command,
|
|
445
|
-
})
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Executes this action sequence.
|
|
450
|
-
* @return {!Promise} A promise that will be resolved once this sequence has
|
|
451
|
-
* completed.
|
|
452
|
-
*/
|
|
453
|
-
async perform() {
|
|
454
|
-
// Make a protected copy of the scheduled actions. This will protect against
|
|
455
|
-
// users defining additional commands before this sequence is actually
|
|
456
|
-
// executed.
|
|
457
|
-
let actions = this.actions_.concat()
|
|
458
|
-
for (let action of actions) {
|
|
459
|
-
await this.driver_.execute(action.command)
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Taps an element.
|
|
465
|
-
*
|
|
466
|
-
* @param {!./webdriver.WebElement} elem The element to tap.
|
|
467
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
468
|
-
*/
|
|
469
|
-
tap(elem) {
|
|
470
|
-
let cmd = new command.Command(
|
|
471
|
-
command.Name.LEGACY_ACTION_TOUCH_SINGLE_TAP
|
|
472
|
-
).setParameter('element', elem.getId())
|
|
473
|
-
|
|
474
|
-
this.schedule_('tap', cmd)
|
|
475
|
-
return this
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Double taps an element.
|
|
480
|
-
*
|
|
481
|
-
* @param {!./webdriver.WebElement} elem The element to double tap.
|
|
482
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
483
|
-
*/
|
|
484
|
-
doubleTap(elem) {
|
|
485
|
-
const cmd = new command.Command(
|
|
486
|
-
command.Name.LEGACY_ACTION_TOUCH_DOUBLE_TAP
|
|
487
|
-
).setParameter('element', elem.getId())
|
|
488
|
-
|
|
489
|
-
this.schedule_('doubleTap', cmd)
|
|
490
|
-
return this
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Long press on an element.
|
|
495
|
-
*
|
|
496
|
-
* @param {!./webdriver.WebElement} elem The element to long press.
|
|
497
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
498
|
-
*/
|
|
499
|
-
longPress(elem) {
|
|
500
|
-
const cmd = new command.Command(
|
|
501
|
-
command.Name.LEGACY_ACTION_TOUCH_LONG_PRESS
|
|
502
|
-
).setParameter('element', elem.getId())
|
|
503
|
-
|
|
504
|
-
this.schedule_('longPress', cmd)
|
|
505
|
-
return this
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Touch down at the given location.
|
|
510
|
-
*
|
|
511
|
-
* @param {{x: number, y: number}} location The location to touch down at.
|
|
512
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
513
|
-
*/
|
|
514
|
-
tapAndHold(location) {
|
|
515
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_DOWN)
|
|
516
|
-
.setParameter('x', location.x)
|
|
517
|
-
.setParameter('y', location.y)
|
|
518
|
-
|
|
519
|
-
this.schedule_('tapAndHold', cmd)
|
|
520
|
-
return this
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Move a held {@linkplain #tapAndHold touch} to the specified location.
|
|
525
|
-
*
|
|
526
|
-
* @param {{x: number, y: number}} location The location to move to.
|
|
527
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
528
|
-
*/
|
|
529
|
-
move(location) {
|
|
530
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_MOVE)
|
|
531
|
-
.setParameter('x', location.x)
|
|
532
|
-
.setParameter('y', location.y)
|
|
533
|
-
|
|
534
|
-
this.schedule_('move', cmd)
|
|
535
|
-
return this
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Release a held {@linkplain #tapAndHold touch} at the specified location.
|
|
540
|
-
*
|
|
541
|
-
* @param {{x: number, y: number}} location The location to release at.
|
|
542
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
543
|
-
*/
|
|
544
|
-
release(location) {
|
|
545
|
-
let cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_UP)
|
|
546
|
-
.setParameter('x', location.x)
|
|
547
|
-
.setParameter('y', location.y)
|
|
548
|
-
|
|
549
|
-
this.schedule_('release', cmd)
|
|
550
|
-
return this
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Scrolls the touch screen by the given offset.
|
|
555
|
-
*
|
|
556
|
-
* @param {{x: number, y: number}} offset The offset to scroll to.
|
|
557
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
558
|
-
*/
|
|
559
|
-
scroll(offset) {
|
|
560
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_SCROLL)
|
|
561
|
-
.setParameter('xoffset', offset.x)
|
|
562
|
-
.setParameter('yoffset', offset.y)
|
|
563
|
-
|
|
564
|
-
this.schedule_('scroll', cmd)
|
|
565
|
-
return this
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Scrolls the touch screen, starting on `elem` and moving by the specified
|
|
570
|
-
* offset.
|
|
571
|
-
*
|
|
572
|
-
* @param {!./webdriver.WebElement} elem The element where scroll starts.
|
|
573
|
-
* @param {{x: number, y: number}} offset The offset to scroll to.
|
|
574
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
575
|
-
*/
|
|
576
|
-
scrollFromElement(elem, offset) {
|
|
577
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_SCROLL)
|
|
578
|
-
.setParameter('element', elem.getId())
|
|
579
|
-
.setParameter('xoffset', offset.x)
|
|
580
|
-
.setParameter('yoffset', offset.y)
|
|
581
|
-
|
|
582
|
-
this.schedule_('scrollFromElement', cmd)
|
|
583
|
-
return this
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Flick, starting anywhere on the screen, at speed xspeed and yspeed.
|
|
588
|
-
*
|
|
589
|
-
* @param {{xspeed: number, yspeed: number}} speed The speed to flick in each
|
|
590
|
-
direction, in pixels per second.
|
|
591
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
592
|
-
*/
|
|
593
|
-
flick(speed) {
|
|
594
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_FLICK)
|
|
595
|
-
.setParameter('xspeed', speed.xspeed)
|
|
596
|
-
.setParameter('yspeed', speed.yspeed)
|
|
597
|
-
|
|
598
|
-
this.schedule_('flick', cmd)
|
|
599
|
-
return this
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Flick starting at elem and moving by x and y at specified speed.
|
|
604
|
-
*
|
|
605
|
-
* @param {!./webdriver.WebElement} elem The element where flick starts.
|
|
606
|
-
* @param {{x: number, y: number}} offset The offset to flick to.
|
|
607
|
-
* @param {number} speed The speed to flick at in pixels per second.
|
|
608
|
-
* @return {!LegacyTouchSequence} A self reference.
|
|
609
|
-
*/
|
|
610
|
-
flickElement(elem, offset, speed) {
|
|
611
|
-
const cmd = new command.Command(command.Name.LEGACY_ACTION_TOUCH_FLICK)
|
|
612
|
-
.setParameter('element', elem.getId())
|
|
613
|
-
.setParameter('xoffset', offset.x)
|
|
614
|
-
.setParameter('yoffset', offset.y)
|
|
615
|
-
.setParameter('speed', speed)
|
|
616
|
-
|
|
617
|
-
this.schedule_('flickElement', cmd)
|
|
618
|
-
return this
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// PUBLIC API
|
|
623
|
-
|
|
624
|
-
module.exports = {
|
|
625
|
-
LegacyActionSequence,
|
|
626
|
-
LegacyTouchSequence,
|
|
627
|
-
}
|