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/webdriver.js
CHANGED
|
@@ -35,6 +35,8 @@ const WebSocket = require('ws')
|
|
|
35
35
|
const http = require('../http/index')
|
|
36
36
|
const fs = require('fs')
|
|
37
37
|
const { Capabilities } = require('./capabilities')
|
|
38
|
+
const path = require('path')
|
|
39
|
+
const { NoSuchElementError } = require('./error')
|
|
38
40
|
|
|
39
41
|
// Capability names that are defined in the W3C spec.
|
|
40
42
|
const W3C_CAPABILITY_NAMES = new Set([
|
|
@@ -970,16 +972,46 @@ class WebDriver {
|
|
|
970
972
|
/** @override */
|
|
971
973
|
findElement(locator) {
|
|
972
974
|
let id
|
|
973
|
-
|
|
975
|
+
let cmd = null
|
|
976
|
+
|
|
977
|
+
if (locator instanceof RelativeBy) {
|
|
978
|
+
cmd = new command.Command(
|
|
979
|
+
command.Name.FIND_ELEMENTS_RELATIVE
|
|
980
|
+
).setParameter('args', locator.marshall())
|
|
981
|
+
} else {
|
|
982
|
+
locator = by.checkedLocator(locator)
|
|
983
|
+
}
|
|
984
|
+
|
|
974
985
|
if (typeof locator === 'function') {
|
|
975
986
|
id = this.findElementInternal_(locator, this)
|
|
976
|
-
|
|
977
|
-
|
|
987
|
+
return new WebElementPromise(this, id)
|
|
988
|
+
} else if (cmd === null) {
|
|
989
|
+
cmd = new command.Command(command.Name.FIND_ELEMENT)
|
|
978
990
|
.setParameter('using', locator.using)
|
|
979
991
|
.setParameter('value', locator.value)
|
|
980
|
-
id = this.execute(cmd)
|
|
981
992
|
}
|
|
982
|
-
|
|
993
|
+
|
|
994
|
+
id = this.execute(cmd)
|
|
995
|
+
if (locator instanceof RelativeBy) {
|
|
996
|
+
return this.normalize_(id)
|
|
997
|
+
} else {
|
|
998
|
+
return new WebElementPromise(this, id)
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* @param {!Function} webElementPromise The webElement in unresolved state
|
|
1004
|
+
* @return {!Promise<!WebElement>} First single WebElement from array of resolved promises
|
|
1005
|
+
*/
|
|
1006
|
+
async normalize_(webElementPromise) {
|
|
1007
|
+
let result = await webElementPromise
|
|
1008
|
+
if (result.length === 0) {
|
|
1009
|
+
throw new NoSuchElementError(
|
|
1010
|
+
'Cannot locate an element with provided parameters'
|
|
1011
|
+
)
|
|
1012
|
+
} else {
|
|
1013
|
+
return result[0]
|
|
1014
|
+
}
|
|
983
1015
|
}
|
|
984
1016
|
|
|
985
1017
|
/**
|
|
@@ -1159,12 +1191,13 @@ class WebDriver {
|
|
|
1159
1191
|
*/
|
|
1160
1192
|
async createCDPConnection(target) {
|
|
1161
1193
|
const caps = await this.getCapabilities()
|
|
1162
|
-
const
|
|
1194
|
+
const seCdp = caps['map_'].get('se:cdp')
|
|
1163
1195
|
const vendorInfo =
|
|
1164
1196
|
caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
|
|
1197
|
+
caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
|
|
1165
1198
|
caps['map_'].get('moz:debuggerAddress') ||
|
|
1166
1199
|
new Map()
|
|
1167
|
-
const debuggerUrl =
|
|
1200
|
+
const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
|
|
1168
1201
|
this._wsUrl = await this.getWsUrl(debuggerUrl, target)
|
|
1169
1202
|
|
|
1170
1203
|
return new Promise((resolve, reject) => {
|
|
@@ -1196,12 +1229,21 @@ class WebDriver {
|
|
|
1196
1229
|
if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
|
|
1197
1230
|
throw new error.InvalidArgumentError('invalid target value')
|
|
1198
1231
|
}
|
|
1199
|
-
let path
|
|
1200
|
-
|
|
1232
|
+
let path
|
|
1233
|
+
if (target === 'page') {
|
|
1234
|
+
path = '/json'
|
|
1235
|
+
} else {
|
|
1236
|
+
path = '/json/version'
|
|
1237
|
+
}
|
|
1201
1238
|
let request = new http.Request('GET', path)
|
|
1202
1239
|
let client = new http.HttpClient('http://' + debuggerAddress)
|
|
1203
1240
|
let response = await client.send(request)
|
|
1204
|
-
let url
|
|
1241
|
+
let url
|
|
1242
|
+
if (target.toLowerCase() === 'page') {
|
|
1243
|
+
url = JSON.parse(response.body)[0]['webSocketDebuggerUrl']
|
|
1244
|
+
} else {
|
|
1245
|
+
url = JSON.parse(response.body)['webSocketDebuggerUrl']
|
|
1246
|
+
}
|
|
1205
1247
|
|
|
1206
1248
|
return url
|
|
1207
1249
|
}
|
|
@@ -1362,7 +1404,10 @@ class WebDriver {
|
|
|
1362
1404
|
.toString()
|
|
1363
1405
|
} catch {
|
|
1364
1406
|
mutationListener = fs
|
|
1365
|
-
.readFileSync(
|
|
1407
|
+
.readFileSync(
|
|
1408
|
+
path.resolve(__dirname, './atoms/mutation-listener.js'),
|
|
1409
|
+
'utf-8'
|
|
1410
|
+
)
|
|
1366
1411
|
.toString()
|
|
1367
1412
|
}
|
|
1368
1413
|
|
|
@@ -2680,18 +2725,12 @@ class WebElement {
|
|
|
2680
2725
|
* Take a screenshot of the visible region encompassed by this element's
|
|
2681
2726
|
* bounding rectangle.
|
|
2682
2727
|
*
|
|
2683
|
-
* @param {boolean=} scroll Optional argument that indicates whether the
|
|
2684
|
-
* element should be scrolled into view before taking a screenshot.
|
|
2685
|
-
* Defaults to false.
|
|
2686
2728
|
* @return {!Promise<string>} A promise that will be
|
|
2687
2729
|
* resolved to the screenshot as a base-64 encoded PNG.
|
|
2688
2730
|
*/
|
|
2689
|
-
takeScreenshot(
|
|
2731
|
+
takeScreenshot() {
|
|
2690
2732
|
return this.execute_(
|
|
2691
|
-
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2692
|
-
'scroll',
|
|
2693
|
-
scroll
|
|
2694
|
-
)
|
|
2733
|
+
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2695
2734
|
)
|
|
2696
2735
|
}
|
|
2697
2736
|
}
|
package/opera.js
CHANGED
|
@@ -383,9 +383,9 @@ class Driver extends webdriver.WebDriver {
|
|
|
383
383
|
caps = options.toCapabilities(caps)
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
return /** @type {!Driver} */ (
|
|
387
|
-
service.kill()
|
|
388
|
-
)
|
|
386
|
+
return /** @type {!Driver} */ (
|
|
387
|
+
super.createSession(executor, caps, () => service.kill())
|
|
388
|
+
)
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selenium-webdriver",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-rc-1",
|
|
4
4
|
"description": "The official WebDriver JavaScript bindings from the Selenium project",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -23,24 +23,24 @@
|
|
|
23
23
|
"node": ">= 10.15.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"jszip": "^3.
|
|
27
|
-
"rimraf": "^
|
|
26
|
+
"jszip": "^3.6.0",
|
|
27
|
+
"rimraf": "^3.0.2",
|
|
28
28
|
"tmp": "^0.2.1",
|
|
29
|
-
"ws": "
|
|
29
|
+
"ws": ">=7.4.6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"eslint": "^7.
|
|
33
|
-
"eslint-config-prettier": "^
|
|
34
|
-
"eslint-plugin-no-only-tests": "^2.
|
|
32
|
+
"eslint": "^7.26.0",
|
|
33
|
+
"eslint-config-prettier": "^8.3.0",
|
|
34
|
+
"eslint-plugin-no-only-tests": "^2.6.0",
|
|
35
35
|
"eslint-plugin-node": "^11.1.0",
|
|
36
|
-
"eslint-plugin-prettier": "^3.
|
|
36
|
+
"eslint-plugin-prettier": "^3.4.0",
|
|
37
37
|
"express": "^4.17.1",
|
|
38
|
-
"jasmine": "^3.
|
|
38
|
+
"jasmine": "^3.8.0",
|
|
39
39
|
"mocha": "^8.1.0",
|
|
40
40
|
"multer": "^1.4.2",
|
|
41
|
-
"prettier": "^2.
|
|
41
|
+
"prettier": "^2.3.0",
|
|
42
42
|
"serve-index": "^1.9.1",
|
|
43
|
-
"sinon": "^
|
|
43
|
+
"sinon": "^10.0.0"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --ext js lib/http.js \"**/*.js\"",
|
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
|
|
|
@@ -449,7 +448,7 @@ DriverService.Builder = class {
|
|
|
449
448
|
|
|
450
449
|
/**
|
|
451
450
|
* Manages the life and death of the
|
|
452
|
-
* <a href="
|
|
451
|
+
* <a href="https://www.selenium.dev/downloads/">
|
|
453
452
|
* standalone Selenium server</a>.
|
|
454
453
|
*/
|
|
455
454
|
class SeleniumServer extends DriverService {
|
|
@@ -563,7 +562,7 @@ SeleniumServer.Options = class {
|
|
|
563
562
|
/**
|
|
564
563
|
* A {@link webdriver.FileDetector} that may be used when running
|
|
565
564
|
* against a remote
|
|
566
|
-
* [Selenium server](
|
|
565
|
+
* [Selenium server](https://www.selenium.dev/downloads/).
|
|
567
566
|
*
|
|
568
567
|
* When a file path on the local machine running this script is entered with
|
|
569
568
|
* {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
|
package/safari.js
CHANGED
|
@@ -154,9 +154,9 @@ class Driver extends webdriver.WebDriver {
|
|
|
154
154
|
service.start().then((url) => new http.HttpClient(url))
|
|
155
155
|
)
|
|
156
156
|
|
|
157
|
-
return /** @type {!Driver} */ (
|
|
158
|
-
service.kill()
|
|
159
|
-
)
|
|
157
|
+
return /** @type {!Driver} */ (
|
|
158
|
+
super.createSession(executor, caps, () => service.kill())
|
|
159
|
+
)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
package/testing/index.js
CHANGED
|
@@ -47,7 +47,7 @@ const { Builder } = require('../index')
|
|
|
47
47
|
* Describes a browser targeted by a {@linkplain suite test suite}.
|
|
48
48
|
* @record
|
|
49
49
|
*/
|
|
50
|
-
function TargetBrowser() {
|
|
50
|
+
function TargetBrowser() {}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* The {@linkplain Browser name} of the targeted browser.
|
|
@@ -100,7 +100,7 @@ function getBrowsersToTestFromEnv() {
|
|
|
100
100
|
const parts = spec.split(/:/, 3)
|
|
101
101
|
let name = parts[0]
|
|
102
102
|
if (name === 'ie') {
|
|
103
|
-
name = Browser.
|
|
103
|
+
name = Browser.INTERNET_EXPLORER
|
|
104
104
|
} else if (name === 'edge') {
|
|
105
105
|
name = Browser.EDGE
|
|
106
106
|
}
|
|
@@ -120,13 +120,8 @@ function getAvailableBrowsers() {
|
|
|
120
120
|
let targets = [
|
|
121
121
|
[chrome.locateSynchronously, Browser.CHROME],
|
|
122
122
|
[edge.locateSynchronously, Browser.EDGE],
|
|
123
|
-
[
|
|
124
|
-
() => edge.locateSynchronously('msedge'),
|
|
125
|
-
Browser.EDGE,
|
|
126
|
-
{ 'ms:edgeChromium': true },
|
|
127
|
-
],
|
|
128
123
|
[firefox.locateSynchronously, Browser.FIREFOX],
|
|
129
|
-
[ie.locateSynchronously, Browser.
|
|
124
|
+
[ie.locateSynchronously, Browser.INTERNET_EXPLORER],
|
|
130
125
|
[safari.locateSynchronously, Browser.SAFARI],
|
|
131
126
|
[opera.locateSynchronously, Browser.OPERA],
|
|
132
127
|
]
|
|
@@ -215,7 +210,7 @@ function init(force = false) {
|
|
|
215
210
|
if (seleniumJar && seleniumUrl) {
|
|
216
211
|
throw Error(
|
|
217
212
|
'Ambiguous test configuration: both SELENIUM_REMOTE_URL' +
|
|
218
|
-
|
|
213
|
+
' && SELENIUM_SERVER_JAR environment variables are set'
|
|
219
214
|
)
|
|
220
215
|
}
|
|
221
216
|
|
|
@@ -223,8 +218,8 @@ function init(force = false) {
|
|
|
223
218
|
if ((seleniumJar || seleniumUrl) && envBrowsers.length === 0) {
|
|
224
219
|
throw Error(
|
|
225
220
|
'Ambiguous test configuration: when either the SELENIUM_REMOTE_URL or' +
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
' SELENIUM_SERVER_JAR environment variable is set, the' +
|
|
222
|
+
' SELENIUM_BROWSER variable must also be set.'
|
|
228
223
|
)
|
|
229
224
|
}
|
|
230
225
|
|
|
@@ -241,7 +236,8 @@ function init(force = false) {
|
|
|
241
236
|
}
|
|
242
237
|
|
|
243
238
|
const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
|
|
244
|
-
const URL_MAP =
|
|
239
|
+
const URL_MAP =
|
|
240
|
+
/** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
|
|
245
241
|
|
|
246
242
|
/**
|
|
247
243
|
* Defines the environment a {@linkplain suite test suite} is running against.
|
|
@@ -254,9 +250,9 @@ class Environment {
|
|
|
254
250
|
* Selenium server to test against.
|
|
255
251
|
*/
|
|
256
252
|
constructor(browser, url = undefined) {
|
|
257
|
-
browser = /** @type {!TargetBrowser} */ (
|
|
258
|
-
Object.assign({}, browser)
|
|
259
|
-
)
|
|
253
|
+
browser = /** @type {!TargetBrowser} */ (
|
|
254
|
+
Object.seal(Object.assign({}, browser))
|
|
255
|
+
)
|
|
260
256
|
|
|
261
257
|
TARGET_MAP.set(this, browser)
|
|
262
258
|
URL_MAP.set(this, url || null)
|
|
@@ -293,7 +289,6 @@ class Environment {
|
|
|
293
289
|
|
|
294
290
|
const realBuild = builder.build
|
|
295
291
|
builder.build = function () {
|
|
296
|
-
|
|
297
292
|
builder.forBrowser(browser.name, browser.version, browser.platform)
|
|
298
293
|
|
|
299
294
|
if (browser.capabilities) {
|
|
@@ -316,7 +311,7 @@ class Environment {
|
|
|
316
311
|
* Configuration options for a {@linkplain ./index.suite test suite}.
|
|
317
312
|
* @record
|
|
318
313
|
*/
|
|
319
|
-
function SuiteOptions() {
|
|
314
|
+
function SuiteOptions() {}
|
|
320
315
|
|
|
321
316
|
/**
|
|
322
317
|
* The browsers to run the test suite against.
|
|
@@ -491,8 +486,8 @@ function getTestHook(name) {
|
|
|
491
486
|
if (type !== 'function') {
|
|
492
487
|
throw TypeError(
|
|
493
488
|
`Expected global.${name} to be a function, but is ${type}.` +
|
|
494
|
-
|
|
495
|
-
|
|
489
|
+
' This can happen if you try using this module when running with' +
|
|
490
|
+
' node directly instead of using jasmine or mocha'
|
|
496
491
|
)
|
|
497
492
|
}
|
|
498
493
|
return fn
|