selenium-webdriver 4.0.0-alpha.8 → 4.0.0-beta.4
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 +41 -1
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +0 -0
- package/chrome.js +0 -0
- package/chromium.js +23 -202
- package/devtools/CDPConnection.js +16 -18
- package/edge.js +89 -130
- package/example/chrome_android.js +45 -0
- package/{lib/atoms/make-atoms-module.js → example/chrome_mobile_emulation.js} +26 -15
- package/example/firefox_channels.js +84 -0
- package/example/google_search.js +50 -0
- package/example/google_search_test.js +70 -0
- package/example/headless.js +63 -0
- package/example/logging.js +64 -0
- package/firefox.js +10 -9
- package/http/index.js +0 -0
- package/http/util.js +3 -3
- package/ie.js +0 -0
- package/index.js +2 -2
- package/io/exec.js +3 -3
- package/io/index.js +12 -13
- package/io/zip.js +5 -6
- package/lib/atoms/find-elements.js +78 -78
- package/lib/atoms/get-attribute.js +64 -68
- package/lib/atoms/is-displayed.js +27 -27
- package/lib/atoms/mutation-listener.js +55 -0
- package/lib/by.js +1 -1
- package/lib/capabilities.js +2 -3
- package/lib/command.js +2 -15
- package/lib/error.js +0 -0
- package/lib/http.js +17 -30
- package/lib/input.js +2 -286
- package/lib/logging.js +0 -0
- package/lib/promise.js +4 -16
- package/lib/proxy.js +0 -0
- package/lib/session.js +0 -0
- package/lib/symbols.js +0 -0
- package/lib/until.js +17 -15
- package/lib/webdriver.js +336 -16
- package/net/index.js +0 -0
- package/net/portprober.js +8 -8
- package/opera.js +0 -0
- package/package.json +13 -13
- package/proxy.js +0 -0
- package/remote/index.js +6 -7
- package/safari.js +0 -0
- package/testing/index.js +504 -0
- package/.eslintrc.js +0 -27
- package/.prettierrc +0 -4
- package/devtools/generator/protocol-dts-generator.js +0 -429
- package/lib/README +0 -5
- package/lib/actions.js +0 -626
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
(function () {
|
|
19
|
+
const observer = new MutationObserver((mutations) => {
|
|
20
|
+
for (const mutation of mutations) {
|
|
21
|
+
switch (mutation.type) {
|
|
22
|
+
case 'attributes':
|
|
23
|
+
// Don't report our own attribute has changed.
|
|
24
|
+
if (mutation.attributeName === "data-__webdriver_id") {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
const curr = mutation.target.getAttribute(mutation.attributeName);
|
|
28
|
+
var id = mutation.target.dataset.__webdriver_id
|
|
29
|
+
if (!id) {
|
|
30
|
+
id = Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
31
|
+
mutation.target.dataset.__webdriver_id = id;
|
|
32
|
+
}
|
|
33
|
+
const json = JSON.stringify({
|
|
34
|
+
'target': id,
|
|
35
|
+
'name': mutation.attributeName,
|
|
36
|
+
'value': curr,
|
|
37
|
+
'oldValue': mutation.oldValue
|
|
38
|
+
});
|
|
39
|
+
__webdriver_attribute(json);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
observer.observe(document, {
|
|
48
|
+
'attributes': true,
|
|
49
|
+
'attributeOldValue': true,
|
|
50
|
+
'characterData': true,
|
|
51
|
+
'characterDataOldValue': true,
|
|
52
|
+
'childList': true,
|
|
53
|
+
'subtree': true
|
|
54
|
+
});
|
|
55
|
+
})();
|
package/lib/by.js
CHANGED
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',
|
|
@@ -84,7 +83,7 @@ const Platform = {
|
|
|
84
83
|
*
|
|
85
84
|
* @record
|
|
86
85
|
*/
|
|
87
|
-
function Timeouts() {}
|
|
86
|
+
function Timeouts() { }
|
|
88
87
|
|
|
89
88
|
/**
|
|
90
89
|
* Defines when, in milliseconds, to interrupt a script that is being
|
|
@@ -264,7 +263,7 @@ class Capabilities {
|
|
|
264
263
|
* @return {!Capabilities} A basic set of capabilities for Firefox.
|
|
265
264
|
*/
|
|
266
265
|
static firefox() {
|
|
267
|
-
return new Capabilities().setBrowserName(Browser.FIREFOX)
|
|
266
|
+
return new Capabilities().setBrowserName(Browser.FIREFOX).set("moz:debuggerAddress", true)
|
|
268
267
|
}
|
|
269
268
|
|
|
270
269
|
/**
|
package/lib/command.js
CHANGED
|
@@ -140,6 +140,8 @@ const Name = {
|
|
|
140
140
|
EXECUTE_ASYNC_SCRIPT: 'executeAsyncScript',
|
|
141
141
|
|
|
142
142
|
GET_ELEMENT_TEXT: 'getElementText',
|
|
143
|
+
GET_COMPUTED_ROLE: 'getAriaRole',
|
|
144
|
+
GET_COMPUTED_LABEL: 'getAccessibleName',
|
|
143
145
|
GET_ELEMENT_TAG_NAME: 'getElementTagName',
|
|
144
146
|
IS_ELEMENT_SELECTED: 'isElementSelected',
|
|
145
147
|
IS_ELEMENT_ENABLED: 'isElementEnabled',
|
|
@@ -175,21 +177,6 @@ const Name = {
|
|
|
175
177
|
|
|
176
178
|
ACTIONS: 'actions',
|
|
177
179
|
CLEAR_ACTIONS: 'clearActions',
|
|
178
|
-
|
|
179
|
-
LEGACY_ACTION_CLICK: 'legacyAction:click',
|
|
180
|
-
LEGACY_ACTION_DOUBLE_CLICK: 'legacyAction:doubleclick',
|
|
181
|
-
LEGACY_ACTION_MOUSE_DOWN: 'legacyAction:mouseDown',
|
|
182
|
-
LEGACY_ACTION_MOUSE_UP: 'legacyAction:mouseUp',
|
|
183
|
-
LEGACY_ACTION_MOUSE_MOVE: 'legacyAction:mouseMove',
|
|
184
|
-
LEGACY_ACTION_SEND_KEYS: 'legacyAction:sendKeys',
|
|
185
|
-
LEGACY_ACTION_TOUCH_DOWN: 'legacyAction:touchDown',
|
|
186
|
-
LEGACY_ACTION_TOUCH_UP: 'legacyAction:touchUp',
|
|
187
|
-
LEGACY_ACTION_TOUCH_MOVE: 'legacyAction:touchMove',
|
|
188
|
-
LEGACY_ACTION_TOUCH_SCROLL: 'legacyAction:touchScroll',
|
|
189
|
-
LEGACY_ACTION_TOUCH_LONG_PRESS: 'legacyAction:touchLongPress',
|
|
190
|
-
LEGACY_ACTION_TOUCH_FLICK: 'legacyAction:touchFlick',
|
|
191
|
-
LEGACY_ACTION_TOUCH_SINGLE_TAP: 'legacyAction:singleTap',
|
|
192
|
-
LEGACY_ACTION_TOUCH_DOUBLE_TAP: 'legacyAction:doubleTap',
|
|
193
180
|
}
|
|
194
181
|
|
|
195
182
|
/**
|
package/lib/error.js
CHANGED
|
File without changes
|
package/lib/http.js
CHANGED
|
@@ -76,7 +76,7 @@ function requireAtom(module, bazelTarget) {
|
|
|
76
76
|
* @return {string} The headers as a string.
|
|
77
77
|
*/
|
|
78
78
|
function headersToString(headers) {
|
|
79
|
-
|
|
79
|
+
const ret = []
|
|
80
80
|
headers.forEach(function (value, name) {
|
|
81
81
|
ret.push(`${name.toLowerCase()}: ${value}`)
|
|
82
82
|
})
|
|
@@ -234,6 +234,14 @@ const COMMAND_MAP = new Map([
|
|
|
234
234
|
],
|
|
235
235
|
[cmd.Name.SUBMIT_ELEMENT, post('/session/:sessionId/element/:id/submit')],
|
|
236
236
|
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
|
237
|
+
[
|
|
238
|
+
cmd.Name.GET_COMPUTED_ROLE,
|
|
239
|
+
get('/session/:sessionId/element/:id/computedrole'),
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
cmd.Name.GET_COMPUTED_LABEL,
|
|
243
|
+
get('/session/:sessionId/element/:id/computedlabel'),
|
|
244
|
+
],
|
|
237
245
|
[cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
|
|
238
246
|
[
|
|
239
247
|
cmd.Name.IS_ELEMENT_SELECTED,
|
|
@@ -297,35 +305,6 @@ const COMMAND_MAP = new Map([
|
|
|
297
305
|
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
|
|
298
306
|
[cmd.Name.GET_SESSION_LOGS, post('/logs')],
|
|
299
307
|
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
|
|
300
|
-
[cmd.Name.LEGACY_ACTION_CLICK, post('/session/:sessionId/click')],
|
|
301
|
-
[
|
|
302
|
-
cmd.Name.LEGACY_ACTION_DOUBLE_CLICK,
|
|
303
|
-
post('/session/:sessionId/doubleclick'),
|
|
304
|
-
],
|
|
305
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_DOWN, post('/session/:sessionId/buttondown')],
|
|
306
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_UP, post('/session/:sessionId/buttonup')],
|
|
307
|
-
[cmd.Name.LEGACY_ACTION_MOUSE_MOVE, post('/session/:sessionId/moveto')],
|
|
308
|
-
[cmd.Name.LEGACY_ACTION_SEND_KEYS, post('/session/:sessionId/keys')],
|
|
309
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_DOWN, post('/session/:sessionId/touch/down')],
|
|
310
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_UP, post('/session/:sessionId/touch/up')],
|
|
311
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_MOVE, post('/session/:sessionId/touch/move')],
|
|
312
|
-
[
|
|
313
|
-
cmd.Name.LEGACY_ACTION_TOUCH_SCROLL,
|
|
314
|
-
post('/session/:sessionId/touch/scroll'),
|
|
315
|
-
],
|
|
316
|
-
[
|
|
317
|
-
cmd.Name.LEGACY_ACTION_TOUCH_LONG_PRESS,
|
|
318
|
-
post('/session/:sessionId/touch/longclick'),
|
|
319
|
-
],
|
|
320
|
-
[cmd.Name.LEGACY_ACTION_TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
|
|
321
|
-
[
|
|
322
|
-
cmd.Name.LEGACY_ACTION_TOUCH_SINGLE_TAP,
|
|
323
|
-
post('/session/:sessionId/touch/click'),
|
|
324
|
-
],
|
|
325
|
-
[
|
|
326
|
-
cmd.Name.LEGACY_ACTION_TOUCH_DOUBLE_TAP,
|
|
327
|
-
post('/session/:sessionId/touch/doubleclick'),
|
|
328
|
-
],
|
|
329
308
|
])
|
|
330
309
|
|
|
331
310
|
/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
|
|
@@ -402,6 +381,14 @@ const W3C_COMMAND_MAP = new Map([
|
|
|
402
381
|
post('/session/:sessionId/element/:id/value'),
|
|
403
382
|
],
|
|
404
383
|
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
|
384
|
+
[
|
|
385
|
+
cmd.Name.GET_COMPUTED_ROLE,
|
|
386
|
+
get('/session/:sessionId/element/:id/computedrole'),
|
|
387
|
+
],
|
|
388
|
+
[
|
|
389
|
+
cmd.Name.GET_COMPUTED_LABEL,
|
|
390
|
+
get('/session/:sessionId/element/:id/computedlabel'),
|
|
391
|
+
],
|
|
405
392
|
[cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
|
|
406
393
|
[
|
|
407
394
|
cmd.Name.GET_ELEMENT_ATTRIBUTE,
|
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/logging.js
CHANGED
|
File without changes
|
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/proxy.js
CHANGED
|
File without changes
|
package/lib/session.js
CHANGED
|
File without changes
|
package/lib/symbols.js
CHANGED
|
File without changes
|
package/lib/until.js
CHANGED
|
@@ -68,7 +68,7 @@ const webdriver = require('./webdriver'),
|
|
|
68
68
|
* @return {!Condition<boolean>} A new condition.
|
|
69
69
|
*/
|
|
70
70
|
exports.ableToSwitchToFrame = function ableToSwitchToFrame(frame) {
|
|
71
|
-
|
|
71
|
+
let condition
|
|
72
72
|
if (typeof frame === 'number' || frame instanceof webdriver.WebElement) {
|
|
73
73
|
condition = (driver) => attemptToSwitchFrames(driver, frame)
|
|
74
74
|
} else {
|
|
@@ -137,13 +137,14 @@ 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
|
-
|
|
140
|
+
return new Condition(
|
|
141
|
+
'for title to be ' + JSON.stringify(title),
|
|
142
|
+
function (driver) {
|
|
143
|
+
return driver.getTitle().then(function (t) {
|
|
144
|
+
return t === title
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
)
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
/**
|
|
@@ -188,13 +189,14 @@ exports.titleMatches = function titleMatches(regex) {
|
|
|
188
189
|
* @return {!Condition<boolean>} The new condition.
|
|
189
190
|
*/
|
|
190
191
|
exports.urlIs = function urlIs(url) {
|
|
191
|
-
return new Condition(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
return new Condition(
|
|
193
|
+
'for URL to be ' + JSON.stringify(url),
|
|
194
|
+
function (driver) {
|
|
195
|
+
return driver.getCurrentUrl().then(function (u) {
|
|
196
|
+
return u === url
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
)
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
/**
|