selenium-webdriver 4.0.0-beta.3 → 4.0.0
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 +42 -0
- package/README.md +4 -2
- package/chrome.js +3 -4
- package/chromium.js +38 -2
- package/devtools/networkinterceptor.js +89 -0
- package/edge.js +14 -20
- package/firefox.js +25 -1
- package/http/index.js +25 -3
- package/ie.js +8 -8
- package/index.js +24 -8
- package/io/exec.js +2 -1
- package/lib/atoms/get-attribute.js +25 -25
- package/lib/by.js +39 -20
- package/lib/capabilities.js +10 -1
- package/lib/http.js +2 -1
- package/lib/input.js +4 -15
- package/lib/until.js +14 -16
- package/lib/webdriver.js +133 -44
- package/opera.js +3 -3
- package/package.json +11 -11
- package/remote/index.js +2 -2
- package/safari.js +3 -3
- package/testing/index.js +7 -6
package/lib/webdriver.js
CHANGED
|
@@ -29,26 +29,28 @@ const input = require('./input')
|
|
|
29
29
|
const logging = require('./logging')
|
|
30
30
|
const promise = require('./promise')
|
|
31
31
|
const Symbols = require('./symbols')
|
|
32
|
-
const cdpTargets = ['page', 'browser']
|
|
33
32
|
const cdp = require('../devtools/CDPConnection')
|
|
34
33
|
const WebSocket = require('ws')
|
|
35
34
|
const http = require('../http/index')
|
|
36
35
|
const fs = require('fs')
|
|
37
36
|
const { Capabilities } = require('./capabilities')
|
|
38
37
|
const path = require('path')
|
|
38
|
+
const { NoSuchElementError } = require('./error')
|
|
39
|
+
const cdpTargets = ['page', 'browser']
|
|
39
40
|
|
|
40
41
|
// Capability names that are defined in the W3C spec.
|
|
41
42
|
const W3C_CAPABILITY_NAMES = new Set([
|
|
42
43
|
'acceptInsecureCerts',
|
|
43
44
|
'browserName',
|
|
44
45
|
'browserVersion',
|
|
45
|
-
'platformName',
|
|
46
46
|
'pageLoadStrategy',
|
|
47
|
+
'platformName',
|
|
47
48
|
'proxy',
|
|
48
49
|
'setWindowRect',
|
|
49
|
-
'timeouts',
|
|
50
50
|
'strictFileInteractability',
|
|
51
|
+
'timeouts',
|
|
51
52
|
'unhandledPromptBehavior',
|
|
53
|
+
'webSocketUrl'
|
|
52
54
|
])
|
|
53
55
|
|
|
54
56
|
/**
|
|
@@ -649,7 +651,6 @@ function filterNonW3CCaps(capabilities) {
|
|
|
649
651
|
* @implements {IWebDriver}
|
|
650
652
|
*/
|
|
651
653
|
class WebDriver {
|
|
652
|
-
|
|
653
654
|
/**
|
|
654
655
|
* @param {!(./session.Session|IThenable<!./session.Session>)} session Either
|
|
655
656
|
* a known session or a promise that will be resolved to a session.
|
|
@@ -972,16 +973,46 @@ class WebDriver {
|
|
|
972
973
|
/** @override */
|
|
973
974
|
findElement(locator) {
|
|
974
975
|
let id
|
|
975
|
-
|
|
976
|
+
let cmd = null
|
|
977
|
+
|
|
978
|
+
if (locator instanceof RelativeBy) {
|
|
979
|
+
cmd = new command.Command(
|
|
980
|
+
command.Name.FIND_ELEMENTS_RELATIVE
|
|
981
|
+
).setParameter('args', locator.marshall())
|
|
982
|
+
} else {
|
|
983
|
+
locator = by.checkedLocator(locator)
|
|
984
|
+
}
|
|
985
|
+
|
|
976
986
|
if (typeof locator === 'function') {
|
|
977
987
|
id = this.findElementInternal_(locator, this)
|
|
978
|
-
|
|
979
|
-
|
|
988
|
+
return new WebElementPromise(this, id)
|
|
989
|
+
} else if (cmd === null) {
|
|
990
|
+
cmd = new command.Command(command.Name.FIND_ELEMENT)
|
|
980
991
|
.setParameter('using', locator.using)
|
|
981
992
|
.setParameter('value', locator.value)
|
|
982
|
-
id = this.execute(cmd)
|
|
983
993
|
}
|
|
984
|
-
|
|
994
|
+
|
|
995
|
+
id = this.execute(cmd)
|
|
996
|
+
if (locator instanceof RelativeBy) {
|
|
997
|
+
return this.normalize_(id)
|
|
998
|
+
} else {
|
|
999
|
+
return new WebElementPromise(this, id)
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* @param {!Function} webElementPromise The webElement in unresolved state
|
|
1005
|
+
* @return {!Promise<!WebElement>} First single WebElement from array of resolved promises
|
|
1006
|
+
*/
|
|
1007
|
+
async normalize_(webElementPromise) {
|
|
1008
|
+
let result = await webElementPromise
|
|
1009
|
+
if (result.length === 0) {
|
|
1010
|
+
throw new NoSuchElementError(
|
|
1011
|
+
'Cannot locate an element with provided parameters'
|
|
1012
|
+
)
|
|
1013
|
+
} else {
|
|
1014
|
+
return result[0]
|
|
1015
|
+
}
|
|
985
1016
|
}
|
|
986
1017
|
|
|
987
1018
|
/**
|
|
@@ -1166,10 +1197,9 @@ class WebDriver {
|
|
|
1166
1197
|
caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
|
|
1167
1198
|
caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
|
|
1168
1199
|
caps['map_'].get('moz:debuggerAddress') ||
|
|
1169
|
-
new Map()
|
|
1200
|
+
new Map()
|
|
1170
1201
|
const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
|
|
1171
|
-
this._wsUrl = await this.getWsUrl(debuggerUrl, target)
|
|
1172
|
-
|
|
1202
|
+
this._wsUrl = await this.getWsUrl(debuggerUrl, target, caps)
|
|
1173
1203
|
return new Promise((resolve, reject) => {
|
|
1174
1204
|
try {
|
|
1175
1205
|
this._wsConnection = new WebSocket(this._wsUrl)
|
|
@@ -1192,30 +1222,38 @@ class WebDriver {
|
|
|
1192
1222
|
/**
|
|
1193
1223
|
* Retrieves 'webSocketDebuggerUrl' by sending a http request using debugger address
|
|
1194
1224
|
* @param {string} debuggerAddress
|
|
1195
|
-
* @param
|
|
1225
|
+
* @param target
|
|
1226
|
+
* @param caps
|
|
1196
1227
|
* @return {string} Returns parsed webSocketDebuggerUrl obtained from the http request
|
|
1197
1228
|
*/
|
|
1198
|
-
async getWsUrl(debuggerAddress, target) {
|
|
1229
|
+
async getWsUrl(debuggerAddress, target, caps) {
|
|
1199
1230
|
if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
|
|
1200
1231
|
throw new error.InvalidArgumentError('invalid target value')
|
|
1201
1232
|
}
|
|
1233
|
+
|
|
1234
|
+
if (debuggerAddress.match(/\/se\/cdp/)) {
|
|
1235
|
+
return debuggerAddress;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1202
1238
|
let path
|
|
1203
|
-
if (target === 'page') {
|
|
1239
|
+
if (target === 'page' && caps['map_'].get('browserName')!=='firefox' ){
|
|
1204
1240
|
path = '/json'
|
|
1205
|
-
} else {
|
|
1241
|
+
} else if(target === 'page' && caps['map_'].get('browserName')==='firefox'){
|
|
1242
|
+
path = '/json/list'
|
|
1243
|
+
}
|
|
1244
|
+
else {
|
|
1206
1245
|
path = '/json/version'
|
|
1207
1246
|
}
|
|
1247
|
+
|
|
1208
1248
|
let request = new http.Request('GET', path)
|
|
1209
1249
|
let client = new http.HttpClient('http://' + debuggerAddress)
|
|
1210
1250
|
let response = await client.send(request)
|
|
1211
|
-
|
|
1251
|
+
|
|
1212
1252
|
if (target.toLowerCase() === 'page') {
|
|
1213
|
-
|
|
1253
|
+
return JSON.parse(response.body)[0]['webSocketDebuggerUrl']
|
|
1214
1254
|
} else {
|
|
1215
|
-
|
|
1255
|
+
return JSON.parse(response.body)['webSocketDebuggerUrl']
|
|
1216
1256
|
}
|
|
1217
|
-
|
|
1218
|
-
return url
|
|
1219
1257
|
}
|
|
1220
1258
|
|
|
1221
1259
|
/**
|
|
@@ -1227,14 +1265,6 @@ class WebDriver {
|
|
|
1227
1265
|
* @param connection CDP Connection
|
|
1228
1266
|
*/
|
|
1229
1267
|
async register(username, password, connection) {
|
|
1230
|
-
await connection.execute(
|
|
1231
|
-
'Network.setCacheDisabled',
|
|
1232
|
-
this.getRandomNumber(1, 10),
|
|
1233
|
-
{
|
|
1234
|
-
cacheDisabled: true,
|
|
1235
|
-
},
|
|
1236
|
-
null
|
|
1237
|
-
)
|
|
1238
1268
|
|
|
1239
1269
|
this._wsConnection.on('message', (message) => {
|
|
1240
1270
|
const params = JSON.parse(message)
|
|
@@ -1273,8 +1303,69 @@ class WebDriver {
|
|
|
1273
1303
|
},
|
|
1274
1304
|
null
|
|
1275
1305
|
)
|
|
1306
|
+
await connection.execute(
|
|
1307
|
+
'Network.setCacheDisabled',
|
|
1308
|
+
this.getRandomNumber(1, 10),
|
|
1309
|
+
{
|
|
1310
|
+
cacheDisabled: true,
|
|
1311
|
+
},
|
|
1312
|
+
null
|
|
1313
|
+
)
|
|
1276
1314
|
}
|
|
1277
1315
|
|
|
1316
|
+
/**
|
|
1317
|
+
* Handle Network interception requests
|
|
1318
|
+
* @param connection WebSocket connection to the browser
|
|
1319
|
+
* @param httpResponse Object representing what we are intercepting
|
|
1320
|
+
* as well as what should be returned.
|
|
1321
|
+
* @param callback callback called when we intercept requests.
|
|
1322
|
+
*/
|
|
1323
|
+
async onIntercept(connection, httpResponse, callback) {
|
|
1324
|
+
|
|
1325
|
+
this._wsConnection.on('message', (message) => {
|
|
1326
|
+
const params = JSON.parse(message)
|
|
1327
|
+
if (params.method === 'Fetch.requestPaused') {
|
|
1328
|
+
const requestPausedParams = params['params']
|
|
1329
|
+
if (requestPausedParams.request.url == httpResponse.urlToIntercept) {
|
|
1330
|
+
connection.execute(
|
|
1331
|
+
'Fetch.continueRequest',
|
|
1332
|
+
this.getRandomNumber(1, 10),
|
|
1333
|
+
{
|
|
1334
|
+
requestId: requestPausedParams['requestId'],
|
|
1335
|
+
url: httpResponse.urlToIntercept,
|
|
1336
|
+
method: httpResponse.method,
|
|
1337
|
+
headers: httpResponse.headers,
|
|
1338
|
+
postData: httpResponse.body
|
|
1339
|
+
}
|
|
1340
|
+
)
|
|
1341
|
+
callback()
|
|
1342
|
+
} else {
|
|
1343
|
+
connection.execute(
|
|
1344
|
+
'Fetch.continueRequest',
|
|
1345
|
+
this.getRandomNumber(1, 10),
|
|
1346
|
+
{
|
|
1347
|
+
requestId: requestPausedParams['requestId'],
|
|
1348
|
+
}
|
|
1349
|
+
)
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
})
|
|
1353
|
+
|
|
1354
|
+
await connection.execute(
|
|
1355
|
+
'Fetch.enable',
|
|
1356
|
+
1,
|
|
1357
|
+
{},
|
|
1358
|
+
null
|
|
1359
|
+
)
|
|
1360
|
+
await connection.execute(
|
|
1361
|
+
'Network.setCacheDisabled',
|
|
1362
|
+
this.getRandomNumber(1, 10),
|
|
1363
|
+
{
|
|
1364
|
+
cacheDisabled: true,
|
|
1365
|
+
},
|
|
1366
|
+
null
|
|
1367
|
+
)
|
|
1368
|
+
}
|
|
1278
1369
|
/**
|
|
1279
1370
|
*
|
|
1280
1371
|
* @param connection
|
|
@@ -1282,12 +1373,7 @@ class WebDriver {
|
|
|
1282
1373
|
* @returns {Promise<void>}
|
|
1283
1374
|
*/
|
|
1284
1375
|
async onLogEvent(connection, callback) {
|
|
1285
|
-
|
|
1286
|
-
'Runtime.enable',
|
|
1287
|
-
this.getRandomNumber(1, 10),
|
|
1288
|
-
{},
|
|
1289
|
-
null
|
|
1290
|
-
)
|
|
1376
|
+
|
|
1291
1377
|
|
|
1292
1378
|
this._wsConnection.on('message', (message) => {
|
|
1293
1379
|
const params = JSON.parse(message)
|
|
@@ -1303,6 +1389,12 @@ class WebDriver {
|
|
|
1303
1389
|
callback(event)
|
|
1304
1390
|
}
|
|
1305
1391
|
})
|
|
1392
|
+
await connection.execute(
|
|
1393
|
+
'Runtime.enable',
|
|
1394
|
+
this.getRandomNumber(1, 10),
|
|
1395
|
+
{},
|
|
1396
|
+
null
|
|
1397
|
+
)
|
|
1306
1398
|
}
|
|
1307
1399
|
|
|
1308
1400
|
/**
|
|
@@ -1374,7 +1466,10 @@ class WebDriver {
|
|
|
1374
1466
|
.toString()
|
|
1375
1467
|
} catch {
|
|
1376
1468
|
mutationListener = fs
|
|
1377
|
-
.readFileSync(
|
|
1469
|
+
.readFileSync(
|
|
1470
|
+
path.resolve(__dirname, './atoms/mutation-listener.js'),
|
|
1471
|
+
'utf-8'
|
|
1472
|
+
)
|
|
1378
1473
|
.toString()
|
|
1379
1474
|
}
|
|
1380
1475
|
|
|
@@ -2692,18 +2787,12 @@ class WebElement {
|
|
|
2692
2787
|
* Take a screenshot of the visible region encompassed by this element's
|
|
2693
2788
|
* bounding rectangle.
|
|
2694
2789
|
*
|
|
2695
|
-
* @param {boolean=} scroll Optional argument that indicates whether the
|
|
2696
|
-
* element should be scrolled into view before taking a screenshot.
|
|
2697
|
-
* Defaults to false.
|
|
2698
2790
|
* @return {!Promise<string>} A promise that will be
|
|
2699
2791
|
* resolved to the screenshot as a base-64 encoded PNG.
|
|
2700
2792
|
*/
|
|
2701
|
-
takeScreenshot(
|
|
2793
|
+
takeScreenshot() {
|
|
2702
2794
|
return this.execute_(
|
|
2703
|
-
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2704
|
-
'scroll',
|
|
2705
|
-
scroll
|
|
2706
|
-
)
|
|
2795
|
+
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2707
2796
|
)
|
|
2708
2797
|
}
|
|
2709
2798
|
}
|
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",
|
|
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
|
@@ -448,7 +448,7 @@ DriverService.Builder = class {
|
|
|
448
448
|
|
|
449
449
|
/**
|
|
450
450
|
* Manages the life and death of the
|
|
451
|
-
* <a href="
|
|
451
|
+
* <a href="https://www.selenium.dev/downloads/">
|
|
452
452
|
* standalone Selenium server</a>.
|
|
453
453
|
*/
|
|
454
454
|
class SeleniumServer extends DriverService {
|
|
@@ -562,7 +562,7 @@ SeleniumServer.Options = class {
|
|
|
562
562
|
/**
|
|
563
563
|
* A {@link webdriver.FileDetector} that may be used when running
|
|
564
564
|
* against a remote
|
|
565
|
-
* [Selenium server](
|
|
565
|
+
* [Selenium server](https://www.selenium.dev/downloads/).
|
|
566
566
|
*
|
|
567
567
|
* When a file path on the local machine running this script is entered with
|
|
568
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
|
@@ -40,7 +40,7 @@ const ie = require('../ie')
|
|
|
40
40
|
const remote = require('../remote')
|
|
41
41
|
const safari = require('../safari')
|
|
42
42
|
const opera = require('../opera')
|
|
43
|
-
const { Browser } = require('../lib/capabilities')
|
|
43
|
+
const { Browser, Capabilities } = require('../lib/capabilities')
|
|
44
44
|
const { Builder } = require('../index')
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -236,7 +236,8 @@ function init(force = false) {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
|
|
239
|
-
const URL_MAP =
|
|
239
|
+
const URL_MAP =
|
|
240
|
+
/** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
|
|
240
241
|
|
|
241
242
|
/**
|
|
242
243
|
* Defines the environment a {@linkplain suite test suite} is running against.
|
|
@@ -249,9 +250,9 @@ class Environment {
|
|
|
249
250
|
* Selenium server to test against.
|
|
250
251
|
*/
|
|
251
252
|
constructor(browser, url = undefined) {
|
|
252
|
-
browser = /** @type {!TargetBrowser} */ (
|
|
253
|
-
Object.assign({}, browser)
|
|
254
|
-
)
|
|
253
|
+
browser = /** @type {!TargetBrowser} */ (
|
|
254
|
+
Object.seal(Object.assign({}, browser))
|
|
255
|
+
)
|
|
255
256
|
|
|
256
257
|
TARGET_MAP.set(this, browser)
|
|
257
258
|
URL_MAP.set(this, url || null)
|
|
@@ -289,7 +290,7 @@ class Environment {
|
|
|
289
290
|
const realBuild = builder.build
|
|
290
291
|
builder.build = function () {
|
|
291
292
|
|
|
292
|
-
builder.
|
|
293
|
+
builder.withCapabilities(Capabilities[browser.name].apply(Capabilities))
|
|
293
294
|
|
|
294
295
|
if (browser.capabilities) {
|
|
295
296
|
builder.getCapabilities().merge(browser.capabilities)
|