selenium-webdriver 4.0.0-beta.1 → 4.0.0-rc-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 +46 -0
- package/README.md +4 -2
- package/chrome.js +3 -4
- package/chromium.js +38 -2
- package/devtools/CDPConnection.js +16 -18
- package/edge.js +84 -131
- package/firefox.js +18 -1
- package/http/index.js +25 -3
- package/ie.js +8 -8
- package/index.js +11 -8
- package/io/exec.js +5 -4
- package/io/index.js +8 -9
- package/io/zip.js +5 -6
- package/lib/atoms/find-elements.js +77 -78
- package/lib/atoms/get-attribute.js +64 -68
- package/lib/atoms/is-displayed.js +27 -27
- package/lib/atoms/mutation-listener.js +17 -0
- package/lib/by.js +39 -20
- package/lib/capabilities.js +10 -2
- package/lib/command.js +0 -15
- package/lib/http.js +2 -30
- package/lib/input.js +6 -301
- package/lib/promise.js +4 -16
- package/lib/until.js +14 -16
- package/lib/webdriver.js +58 -19
- package/opera.js +3 -3
- package/package.json +11 -11
- package/remote/index.js +8 -9
- package/safari.js +3 -3
- package/testing/index.js +14 -19
- package/lib/actions.js +0 -627
package/lib/by.js
CHANGED
|
@@ -258,8 +258,36 @@ class By {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Start Searching for relative objects using the value returned from
|
|
263
|
+
* `By.tagName()`.
|
|
264
|
+
*
|
|
265
|
+
* Note: this method will likely be removed in the future please use
|
|
266
|
+
* `locateWith`.
|
|
267
|
+
* @param {By} The value returned from calling By.tagName()
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
261
270
|
function withTagName(tagName) {
|
|
262
|
-
return new RelativeBy(tagName)
|
|
271
|
+
return new RelativeBy({ 'css selector': tagName })
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Start searching for relative objects using search criteria with By.
|
|
276
|
+
* @param {string} A By map that shows how to find the initial element
|
|
277
|
+
* @returns {RelativeBy}
|
|
278
|
+
*/
|
|
279
|
+
function locateWith(by) {
|
|
280
|
+
return new RelativeBy(getLocator(by));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getLocator(locatorOrElement) {
|
|
284
|
+
let toFind
|
|
285
|
+
if (locatorOrElement instanceof By) {
|
|
286
|
+
toFind = locatorOrElement.toObject()
|
|
287
|
+
} else {
|
|
288
|
+
toFind = locatorOrElement
|
|
289
|
+
}
|
|
290
|
+
return toFind
|
|
263
291
|
}
|
|
264
292
|
|
|
265
293
|
/**
|
|
@@ -269,11 +297,11 @@ function withTagName(tagName) {
|
|
|
269
297
|
*/
|
|
270
298
|
class RelativeBy {
|
|
271
299
|
/**
|
|
272
|
-
* @param {
|
|
300
|
+
* @param {By} findDetails
|
|
273
301
|
* @param {Array<Object>} filters
|
|
274
302
|
*/
|
|
275
|
-
constructor(
|
|
276
|
-
this.root =
|
|
303
|
+
constructor(findDetails, filters = null) {
|
|
304
|
+
this.root = findDetails
|
|
277
305
|
this.filters = filters || []
|
|
278
306
|
}
|
|
279
307
|
|
|
@@ -285,7 +313,7 @@ class RelativeBy {
|
|
|
285
313
|
above(locatorOrElement) {
|
|
286
314
|
this.filters.push({
|
|
287
315
|
kind: 'above',
|
|
288
|
-
args: [
|
|
316
|
+
args: [getLocator(locatorOrElement)],
|
|
289
317
|
})
|
|
290
318
|
return this
|
|
291
319
|
}
|
|
@@ -298,7 +326,7 @@ class RelativeBy {
|
|
|
298
326
|
below(locatorOrElement) {
|
|
299
327
|
this.filters.push({
|
|
300
328
|
kind: 'below',
|
|
301
|
-
args: [
|
|
329
|
+
args: [getLocator(locatorOrElement)],
|
|
302
330
|
})
|
|
303
331
|
return this
|
|
304
332
|
}
|
|
@@ -311,7 +339,7 @@ class RelativeBy {
|
|
|
311
339
|
toLeftOf(locatorOrElement) {
|
|
312
340
|
this.filters.push({
|
|
313
341
|
kind: 'left',
|
|
314
|
-
args: [
|
|
342
|
+
args: [getLocator(locatorOrElement)],
|
|
315
343
|
})
|
|
316
344
|
return this
|
|
317
345
|
}
|
|
@@ -324,7 +352,7 @@ class RelativeBy {
|
|
|
324
352
|
toRightOf(locatorOrElement) {
|
|
325
353
|
this.filters.push({
|
|
326
354
|
kind: 'right',
|
|
327
|
-
args: [
|
|
355
|
+
args: [getLocator(locatorOrElement)],
|
|
328
356
|
})
|
|
329
357
|
return this
|
|
330
358
|
}
|
|
@@ -337,7 +365,7 @@ class RelativeBy {
|
|
|
337
365
|
near(locatorOrElement) {
|
|
338
366
|
this.filters.push({
|
|
339
367
|
kind: 'near',
|
|
340
|
-
args: [
|
|
368
|
+
args: [getLocator(locatorOrElement)],
|
|
341
369
|
})
|
|
342
370
|
return this
|
|
343
371
|
}
|
|
@@ -350,22 +378,12 @@ class RelativeBy {
|
|
|
350
378
|
marshall() {
|
|
351
379
|
return {
|
|
352
380
|
relative: {
|
|
353
|
-
root:
|
|
381
|
+
root: this.root,
|
|
354
382
|
filters: this.filters,
|
|
355
383
|
},
|
|
356
384
|
}
|
|
357
385
|
}
|
|
358
386
|
|
|
359
|
-
getLocator(locatorOrElement) {
|
|
360
|
-
let toFind
|
|
361
|
-
if (locatorOrElement instanceof By) {
|
|
362
|
-
toFind = locatorOrElement.toObject()
|
|
363
|
-
} else {
|
|
364
|
-
toFind = locatorOrElement
|
|
365
|
-
}
|
|
366
|
-
return toFind
|
|
367
|
-
}
|
|
368
|
-
|
|
369
387
|
/** @override */
|
|
370
388
|
toString() {
|
|
371
389
|
// The static By.name() overrides this.constructor.name. Shame...
|
|
@@ -411,5 +429,6 @@ module.exports = {
|
|
|
411
429
|
By: By,
|
|
412
430
|
RelativeBy: RelativeBy,
|
|
413
431
|
withTagName: withTagName,
|
|
432
|
+
locateWith: locateWith,
|
|
414
433
|
checkedLocator: check,
|
|
415
434
|
}
|
package/lib/capabilities.js
CHANGED
|
@@ -32,7 +32,6 @@ const Browser = {
|
|
|
32
32
|
CHROME: 'chrome',
|
|
33
33
|
EDGE: 'MicrosoftEdge',
|
|
34
34
|
FIREFOX: 'firefox',
|
|
35
|
-
IE: 'internet explorer',
|
|
36
35
|
INTERNET_EXPLORER: 'internet explorer',
|
|
37
36
|
SAFARI: 'safari',
|
|
38
37
|
OPERA: 'opera',
|
|
@@ -264,7 +263,9 @@ class Capabilities {
|
|
|
264
263
|
* @return {!Capabilities} A basic set of capabilities for Firefox.
|
|
265
264
|
*/
|
|
266
265
|
static firefox() {
|
|
267
|
-
return new Capabilities()
|
|
266
|
+
return new Capabilities()
|
|
267
|
+
.setBrowserName(Browser.FIREFOX)
|
|
268
|
+
.set('moz:debuggerAddress', true)
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
/**
|
|
@@ -281,6 +282,13 @@ class Capabilities {
|
|
|
281
282
|
return new Capabilities().setBrowserName(Browser.SAFARI)
|
|
282
283
|
}
|
|
283
284
|
|
|
285
|
+
/**
|
|
286
|
+
* @return {!Capabilities} A basic set of capabilities for Opera
|
|
287
|
+
*/
|
|
288
|
+
static opera() {
|
|
289
|
+
return new Capabilities().setBrowserName(Browser.OPERA)
|
|
290
|
+
}
|
|
291
|
+
|
|
284
292
|
/**
|
|
285
293
|
* @return {!Object<string, ?>} The JSON representation of this instance.
|
|
286
294
|
* Note, the returned object may contain nested promised values.
|
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)>} */
|
|
@@ -519,7 +490,8 @@ function buildRequest(customCommands, w3c, command) {
|
|
|
519
490
|
}
|
|
520
491
|
}
|
|
521
492
|
|
|
522
|
-
const CLIENTS =
|
|
493
|
+
const CLIENTS =
|
|
494
|
+
/** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */ new WeakMap()
|
|
523
495
|
|
|
524
496
|
/**
|
|
525
497
|
* A command executor that communicates with the server using JSON over HTTP.
|
package/lib/input.js
CHANGED
|
@@ -20,14 +20,8 @@
|
|
|
20
20
|
/**
|
|
21
21
|
* @fileoverview Defines types related to user input with the WebDriver API.
|
|
22
22
|
*/
|
|
23
|
-
|
|
24
|
-
const promise = require('./promise')
|
|
25
23
|
const { Command, Name } = require('./command')
|
|
26
|
-
const {
|
|
27
|
-
InvalidArgumentError,
|
|
28
|
-
UnknownCommandError,
|
|
29
|
-
UnsupportedOperationError,
|
|
30
|
-
} = require('./error')
|
|
24
|
+
const { InvalidArgumentError } = require('./error')
|
|
31
25
|
|
|
32
26
|
/**
|
|
33
27
|
* Enumeration of the buttons used in the advanced interactions API.
|
|
@@ -489,67 +483,6 @@ Pointer.Type = {
|
|
|
489
483
|
* | Keyboard | pause(100) | keyDown(SHIFT) |
|
|
490
484
|
* | Mouse | move({duration: 300}) | |
|
|
491
485
|
*
|
|
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
486
|
* [client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects
|
|
554
487
|
* [bounding client rect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
|
|
555
488
|
*
|
|
@@ -560,26 +493,16 @@ class Actions {
|
|
|
560
493
|
/**
|
|
561
494
|
* @param {!Executor} executor The object to execute the configured
|
|
562
495
|
* actions with.
|
|
563
|
-
* @param {{async: (boolean|undefined)
|
|
564
|
-
* bridge: (boolean|undefined)}} options Options for this action
|
|
496
|
+
* @param {{async: (boolean|undefined)}} options Options for this action
|
|
565
497
|
* sequence (see class description for details).
|
|
566
498
|
*/
|
|
567
|
-
constructor(executor, { async = false
|
|
568
|
-
if (async && bridge) {
|
|
569
|
-
throw new InvalidArgumentError(
|
|
570
|
-
'Async sequences not supported with bridge mode'
|
|
571
|
-
)
|
|
572
|
-
}
|
|
573
|
-
|
|
499
|
+
constructor(executor, { async = false } = {}) {
|
|
574
500
|
/** @private @const */
|
|
575
501
|
this.executor_ = executor
|
|
576
502
|
|
|
577
503
|
/** @private @const */
|
|
578
504
|
this.sync_ = !async
|
|
579
505
|
|
|
580
|
-
/** @private @const */
|
|
581
|
-
this.bridge_ = !!bridge
|
|
582
|
-
|
|
583
506
|
/** @private @const */
|
|
584
507
|
this.keyboard_ = new Keyboard('default keyboard')
|
|
585
508
|
|
|
@@ -952,20 +875,9 @@ class Actions {
|
|
|
952
875
|
return Promise.resolve()
|
|
953
876
|
}
|
|
954
877
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
)
|
|
959
|
-
} 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
|
-
throw ex
|
|
968
|
-
}
|
|
878
|
+
await this.executor_.execute(
|
|
879
|
+
new Command(Name.ACTIONS).setParameter('actions', _actions)
|
|
880
|
+
)
|
|
969
881
|
}
|
|
970
882
|
}
|
|
971
883
|
|
|
@@ -980,213 +892,6 @@ function isIdle(actions) {
|
|
|
980
892
|
)
|
|
981
893
|
}
|
|
982
894
|
|
|
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
895
|
/**
|
|
1191
896
|
* Script used to compute the offset from the center of a DOM element's first
|
|
1192
897
|
* 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/lib/until.js
CHANGED
|
@@ -137,14 +137,13 @@ exports.alertIsPresent = function alertIsPresent() {
|
|
|
137
137
|
* @return {!Condition<boolean>} The new condition.
|
|
138
138
|
*/
|
|
139
139
|
exports.titleIs = function titleIs(title) {
|
|
140
|
-
return new Condition(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
140
|
+
return new Condition('for title to be ' + JSON.stringify(title), function (
|
|
141
|
+
driver
|
|
142
|
+
) {
|
|
143
|
+
return driver.getTitle().then(function (t) {
|
|
144
|
+
return t === title
|
|
145
|
+
})
|
|
146
|
+
})
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
/**
|
|
@@ -189,14 +188,13 @@ exports.titleMatches = function titleMatches(regex) {
|
|
|
189
188
|
* @return {!Condition<boolean>} The new condition.
|
|
190
189
|
*/
|
|
191
190
|
exports.urlIs = function urlIs(url) {
|
|
192
|
-
return new Condition(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
)
|
|
191
|
+
return new Condition('for URL to be ' + JSON.stringify(url), function (
|
|
192
|
+
driver
|
|
193
|
+
) {
|
|
194
|
+
return driver.getCurrentUrl().then(function (u) {
|
|
195
|
+
return u === url
|
|
196
|
+
})
|
|
197
|
+
})
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
/**
|