selenium-webdriver 4.25.0 → 4.27.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 CHANGED
@@ -1,3 +1,19 @@
1
+ ## 4.27.0
2
+
3
+ - Add CDP for Chrome 131 and remove 128
4
+ - Add Firefox CDP deprecation warnings
5
+ - Update supported versions for Chrome DevTools
6
+ - [bidi] Allow passing in uri for authentication handlers (#14386)
7
+ - [bidi] Enable locate node tests for Chrome and Edge
8
+ - [bidi] Ensure start nodes are serialized
9
+
10
+ ## 4.26.0
11
+
12
+ - Add CDP for Chrome 130 and remove 127
13
+ - Fix sendKeys command fail on FileDetector.handleFile error (#14663)
14
+ - Update dependencies to latest versions to resolve security alerts
15
+ - Close BiDi websocket connection (#14507)
16
+
1
17
  ## 4.25.0
2
18
 
3
19
  - Add CDP for Chrome 129 and remove 126
@@ -156,8 +156,7 @@ class BrowsingContext {
156
156
  method: 'browsingContext.create',
157
157
  params: Object.fromEntries(parameters),
158
158
  }
159
- const res = await this.bidi.send(params)
160
- return res
159
+ return await this.bidi.send(params)
161
160
  }
162
161
 
163
162
  /**
@@ -528,10 +527,15 @@ class BrowsingContext {
528
527
  throw Error(`Pass in an array of ReferenceValue objects. Received: ${startNodes}`)
529
528
  }
530
529
 
530
+ let startNodesSerialized = undefined
531
+
531
532
  if (startNodes !== undefined && Array.isArray(startNodes)) {
533
+ startNodesSerialized = []
532
534
  startNodes.forEach((node) => {
533
535
  if (!(node instanceof ReferenceValue)) {
534
536
  throw Error(`Pass in a ReferenceValue object. Received: ${node}`)
537
+ } else {
538
+ startNodesSerialized.push(node.asMap())
535
539
  }
536
540
  })
537
541
  }
@@ -544,7 +548,7 @@ class BrowsingContext {
544
548
  maxNodeCount: maxNodeCount,
545
549
  sandbox: sandbox,
546
550
  serializationOptions: serializationOptions,
547
- startNodes: startNodes,
551
+ startNodes: startNodesSerialized,
548
552
  },
549
553
  }
550
554
 
@@ -47,7 +47,7 @@ class ContinueRequestParameters {
47
47
  * Sets the cookies for the request.
48
48
  *
49
49
  * @param {Header[]} cookieHeaders - An array of cookie headers.
50
- * @returns {continueRequestParameters} - The current instance of the ContinueRequestParameters for chaining.
50
+ * @returns {ContinueRequestParameters} - The current instance of the ContinueRequestParameters for chaining.
51
51
  * @throws {Error} - If a cookie header is not an instance of Header.
52
52
  */
53
53
  cookies(cookieHeaders) {
package/bidi/filterBy.js CHANGED
@@ -21,7 +21,7 @@ class FilterBy {
21
21
  }
22
22
 
23
23
  static logLevel(level) {
24
- if (level === undefined || (level != undefined && !['debug', 'error', 'info', 'warning'].includes(level))) {
24
+ if (level === undefined || (level !== undefined && !['debug', 'error', 'info', 'warning'].includes(level))) {
25
25
  throw Error(
26
26
  `Please pass valid log level. Valid log levels are 'debug', 'error', 'info' and 'warning'. Received: ${level}`,
27
27
  )
@@ -21,12 +21,13 @@ const { Source } = require('./scriptTypes')
21
21
 
22
22
  /**
23
23
  * Represents a base log entry.
24
- * Desribed in https://w3c.github.io/webdriver-bidi/#types-log-logentry.
24
+ * Described in https://w3c.github.io/webdriver-bidi/#types-log-logentry.
25
25
  */
26
26
  class BaseLogEntry {
27
27
  /**
28
28
  * Creates a new instance of BaseLogEntry.
29
29
  * @param {string} level - The log level.
30
+ * @param {source} source - Script Source
30
31
  * @param {string} text - The log source.
31
32
  * @param {string} text - The log text.
32
33
  * @param {number} timeStamp - The log timestamp.
@@ -86,6 +87,7 @@ class GenericLogEntry extends BaseLogEntry {
86
87
  /**
87
88
  * Creates an instance of GenericLogEntry.
88
89
  * @param {string} level - The log level.
90
+ * @param {source} source - Script Source
89
91
  * @param {string} text - The log text.
90
92
  * @param {Date} timeStamp - The log timestamp.
91
93
  * @param {string} type - The log type.
@@ -145,7 +145,7 @@ class LocalValue {
145
145
  /**
146
146
  * Creates a new LocalValue object from the passed object.
147
147
  *
148
- * @param {Object} map - The object.
148
+ * @param {Object} object - The object.
149
149
  * @returns {LocalValue} - The created LocalValue object.
150
150
  */
151
151
  static createObjectValue(object) {
@@ -431,7 +431,7 @@ class ChannelValue {
431
431
  }
432
432
  }
433
433
 
434
- if (resultOwnership != undefined) {
434
+ if (resultOwnership !== undefined) {
435
435
  if (['root', 'none'].includes(resultOwnership)) {
436
436
  this.resultOwnership = resultOwnership
437
437
  } else {
package/bidi/storage.js CHANGED
@@ -138,11 +138,7 @@ class Storage {
138
138
  Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'userContext') &&
139
139
  Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'sourceOrigin')
140
140
  ) {
141
- let partitionKey = new PartitionKey(
142
- response.result.partitionKey.userContext,
143
- response.result.partitionKey.sourceOrigin,
144
- )
145
- return partitionKey
141
+ return new PartitionKey(response.result.partitionKey.userContext, response.result.partitionKey.sourceOrigin)
146
142
  }
147
143
  }
148
144
  }
@@ -182,11 +178,7 @@ class Storage {
182
178
  Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'userContext') &&
183
179
  Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'sourceOrigin')
184
180
  ) {
185
- let partitionKey = new PartitionKey(
186
- response.result.partitionKey.userContext,
187
- response.result.partitionKey.sourceOrigin,
188
- )
189
- return partitionKey
181
+ return new PartitionKey(response.result.partitionKey.userContext, response.result.partitionKey.sourceOrigin)
190
182
  }
191
183
  }
192
184
  }
Binary file
Binary file
Binary file
package/lib/network.js CHANGED
@@ -20,9 +20,10 @@ const { InterceptPhase } = require('../bidi/interceptPhase')
20
20
  const { AddInterceptParameters } = require('../bidi/addInterceptParameters')
21
21
 
22
22
  class Network {
23
+ #callbackId = 0
23
24
  #driver
24
25
  #network
25
- #callBackInterceptIdMap = new Map()
26
+ #authHandlers = new Map()
26
27
 
27
28
  constructor(driver) {
28
29
  this.#driver = driver
@@ -30,7 +31,7 @@ class Network {
30
31
 
31
32
  // This should be done in the constructor.
32
33
  // But since it needs to call async methods we cannot do that in the constructor.
33
- // We can have a separate async method that initialises the Script instance.
34
+ // We can have a separate async method that initialises the Network instance.
34
35
  // However, that pattern does not allow chaining the methods as we would like the user to use it.
35
36
  // Since it involves awaiting to get the instance and then another await to call the method.
36
37
  // Using this allows the user to do this "await driver.network.addAuthenticationHandler(callback)"
@@ -39,39 +40,51 @@ class Network {
39
40
  return
40
41
  }
41
42
  this.#network = await network(this.#driver)
42
- }
43
43
 
44
- async addAuthenticationHandler(username, password) {
45
- await this.#init()
44
+ await this.#network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED))
46
45
 
47
- const interceptId = await this.#network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED))
46
+ await this.#network.authRequired(async (event) => {
47
+ const requestId = event.request.request
48
+ const uri = event.request.url
49
+ const credentials = this.getAuthCredentials(uri)
50
+ if (credentials !== null) {
51
+ await this.#network.continueWithAuth(requestId, credentials.username, credentials.password)
52
+ return
53
+ }
48
54
 
49
- const id = await this.#network.authRequired(async (event) => {
50
- await this.#network.continueWithAuth(event.request.request, username, password)
55
+ await this.#network.continueWithAuthNoCredentials(requestId)
51
56
  })
57
+ }
58
+
59
+ getAuthCredentials(uri) {
60
+ for (let [, value] of this.#authHandlers) {
61
+ if (uri.match(value.uri)) {
62
+ return value
63
+ }
64
+ }
65
+ return null
66
+ }
67
+ async addAuthenticationHandler(username, password, uri = '//') {
68
+ await this.#init()
52
69
 
53
- this.#callBackInterceptIdMap.set(id, interceptId)
70
+ const id = this.#callbackId++
71
+
72
+ this.#authHandlers.set(id, { username, password, uri })
54
73
  return id
55
74
  }
56
75
 
57
76
  async removeAuthenticationHandler(id) {
58
77
  await this.#init()
59
78
 
60
- const interceptId = this.#callBackInterceptIdMap.get(id)
61
-
62
- await this.#network.removeIntercept(interceptId)
63
- await this.#network.removeCallback(id)
64
-
65
- this.#callBackInterceptIdMap.delete(id)
79
+ if (this.#authHandlers.has(id)) {
80
+ this.#authHandlers.delete(id)
81
+ } else {
82
+ throw Error(`Callback with id ${id} not found`)
83
+ }
66
84
  }
67
85
 
68
86
  async clearAuthenticationHandlers() {
69
- for (const [key, value] of this.#callBackInterceptIdMap.entries()) {
70
- await this.#network.removeIntercept(value)
71
- await this.#network.removeCallback(key)
72
- }
73
-
74
- this.#callBackInterceptIdMap.clear()
87
+ this.#authHandlers.clear()
75
88
  }
76
89
  }
77
90
 
package/lib/webdriver.js CHANGED
@@ -789,8 +789,13 @@ class WebDriver {
789
789
  // If the websocket connection is not closed,
790
790
  // and we are running CDP sessions against the Selenium Grid,
791
791
  // the node process never exits since the websocket connection is open until the Grid is shutdown.
792
- if (this._wsConnection !== undefined) {
793
- this._wsConnection.close()
792
+ if (this._cdpWsConnection !== undefined) {
793
+ this._cdpWsConnection.close()
794
+ }
795
+
796
+ // Close the BiDi websocket connection
797
+ if (this._bidiConnection !== undefined) {
798
+ this._bidiConnection.close()
794
799
  }
795
800
  })
796
801
  }
@@ -1225,6 +1230,12 @@ class WebDriver {
1225
1230
 
1226
1231
  const caps = await this.getCapabilities()
1227
1232
 
1233
+ if (caps['map_'].get('browserName') === 'firefox') {
1234
+ console.warn(
1235
+ 'CDP support for Firefox is deprecated and will be removed in future versions. Please switch to WebDriver BiDi.',
1236
+ )
1237
+ }
1238
+
1228
1239
  if (process.env.SELENIUM_REMOTE_URL) {
1229
1240
  const host = new URL(process.env.SELENIUM_REMOTE_URL).host
1230
1241
  const sessionId = await this.getSession().then((session) => session.getId())
@@ -1241,18 +1252,18 @@ class WebDriver {
1241
1252
  this._wsUrl = await this.getWsUrl(debuggerUrl, target, caps)
1242
1253
  return new Promise((resolve, reject) => {
1243
1254
  try {
1244
- this._wsConnection = new WebSocket(this._wsUrl.replace('localhost', '127.0.0.1'))
1245
- this._cdpConnection = new cdp.CdpConnection(this._wsConnection)
1255
+ this._cdpWsConnection = new WebSocket(this._wsUrl.replace('localhost', '127.0.0.1'))
1256
+ this._cdpConnection = new cdp.CdpConnection(this._cdpWsConnection)
1246
1257
  } catch (err) {
1247
1258
  reject(err)
1248
1259
  return
1249
1260
  }
1250
1261
 
1251
- this._wsConnection.on('open', async () => {
1262
+ this._cdpWsConnection.on('open', async () => {
1252
1263
  await this.getCdpTargets()
1253
1264
  })
1254
1265
 
1255
- this._wsConnection.on('message', async (message) => {
1266
+ this._cdpWsConnection.on('message', async (message) => {
1256
1267
  const params = JSON.parse(message)
1257
1268
  if (params.result) {
1258
1269
  if (params.result.targetInfos) {
@@ -1273,7 +1284,7 @@ class WebDriver {
1273
1284
  }
1274
1285
  })
1275
1286
 
1276
- this._wsConnection.on('error', (error) => {
1287
+ this._cdpWsConnection.on('error', (error) => {
1277
1288
  reject(error)
1278
1289
  })
1279
1290
  })
@@ -1288,9 +1299,12 @@ class WebDriver {
1288
1299
  * @returns {BIDI}
1289
1300
  */
1290
1301
  async getBidi() {
1291
- const caps = await this.getCapabilities()
1292
- let WebSocketUrl = caps['map_'].get('webSocketUrl')
1293
- return new BIDI(WebSocketUrl.replace('localhost', '127.0.0.1'))
1302
+ if (this._bidiConnection === undefined) {
1303
+ const caps = await this.getCapabilities()
1304
+ let WebSocketUrl = caps['map_'].get('webSocketUrl')
1305
+ this._bidiConnection = new BIDI(WebSocketUrl.replace('localhost', '127.0.0.1'))
1306
+ }
1307
+ return this._bidiConnection
1294
1308
  }
1295
1309
 
1296
1310
  /**
@@ -1338,7 +1352,7 @@ class WebDriver {
1338
1352
  * @param connection CDP Connection
1339
1353
  */
1340
1354
  async register(username, password, connection) {
1341
- this._wsConnection.on('message', (message) => {
1355
+ this._cdpWsConnection.on('message', (message) => {
1342
1356
  const params = JSON.parse(message)
1343
1357
 
1344
1358
  if (params.method === 'Fetch.authRequired') {
@@ -1383,7 +1397,7 @@ class WebDriver {
1383
1397
  * @param callback callback called when we intercept requests.
1384
1398
  */
1385
1399
  async onIntercept(connection, httpResponse, callback) {
1386
- this._wsConnection.on('message', (message) => {
1400
+ this._cdpWsConnection.on('message', (message) => {
1387
1401
  const params = JSON.parse(message)
1388
1402
  if (params.method === 'Fetch.requestPaused') {
1389
1403
  const requestPausedParams = params['params']
@@ -1420,7 +1434,7 @@ class WebDriver {
1420
1434
  * @returns {Promise<void>}
1421
1435
  */
1422
1436
  async onLogEvent(connection, callback) {
1423
- this._wsConnection.on('message', (message) => {
1437
+ this._cdpWsConnection.on('message', (message) => {
1424
1438
  const params = JSON.parse(message)
1425
1439
  if (params.method === 'Runtime.consoleAPICalled') {
1426
1440
  const consoleEventParams = params['params']
@@ -1457,7 +1471,7 @@ class WebDriver {
1457
1471
  async onLogException(connection, callback) {
1458
1472
  await connection.execute('Runtime.enable', {}, null)
1459
1473
 
1460
- this._wsConnection.on('message', (message) => {
1474
+ this._cdpWsConnection.on('message', (message) => {
1461
1475
  const params = JSON.parse(message)
1462
1476
 
1463
1477
  if (params.method === 'Runtime.exceptionThrown') {
@@ -1510,7 +1524,7 @@ class WebDriver {
1510
1524
  null,
1511
1525
  )
1512
1526
 
1513
- this._wsConnection.on('message', async (message) => {
1527
+ this._cdpWsConnection.on('message', async (message) => {
1514
1528
  const params = JSON.parse(message)
1515
1529
  if (params.method === 'Runtime.bindingCalled') {
1516
1530
  let payload = JSON.parse(params['params']['payload'])
@@ -2744,6 +2758,7 @@ class WebElement {
2744
2758
  keys = await this.driver_.fileDetector_.handleFile(this.driver_, keys.join(''))
2745
2759
  } catch (ex) {
2746
2760
  this.log_.severe('Error trying parse string as a file with file detector; sending keys instead' + ex)
2761
+ keys = keys.join('')
2747
2762
  }
2748
2763
 
2749
2764
  return this.execute_(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selenium-webdriver",
3
- "version": "4.25.0",
3
+ "version": "4.27.0",
4
4
  "description": "The official WebDriver JavaScript bindings from the Selenium project",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -23,30 +23,30 @@
23
23
  "node": ">= 14.21.0"
24
24
  },
25
25
  "dependencies": {
26
- "@bazel/runfiles": "^5.8.1",
26
+ "@bazel/runfiles": "^6.3.1",
27
27
  "jszip": "^3.10.1",
28
28
  "tmp": "^0.2.3",
29
29
  "ws": "^8.18.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@eslint/js": "^9.8.0",
32
+ "@eslint/js": "^9.14.0",
33
33
  "clean-jsdoc-theme": "^4.3.0",
34
- "eslint": "^9.8.0",
34
+ "eslint": "^9.14.0",
35
35
  "eslint-config-prettier": "^9.1.0",
36
36
  "eslint-plugin-mocha": "^10.5.0",
37
- "eslint-plugin-n": "^17.10.1",
38
- "eslint-plugin-no-only-tests": "^3.1.0",
37
+ "eslint-plugin-n": "^17.13.1",
38
+ "eslint-plugin-no-only-tests": "^3.3.0",
39
39
  "eslint-plugin-prettier": "^5.2.1",
40
- "express": "^4.19.2",
41
- "globals": "^15.9.0",
40
+ "express": "^4.21.1",
41
+ "globals": "^15.12.0",
42
42
  "has-flag": "^5.0.1",
43
- "jsdoc": "^4.0.3",
44
- "mocha": "^10.7.0",
43
+ "jsdoc": "^4.0.4",
44
+ "mocha": "^10.8.2",
45
45
  "mocha-junit-reporter": "^2.2.1",
46
46
  "multer": "1.4.5-lts.1",
47
47
  "prettier": "^3.3.3",
48
48
  "serve-index": "^1.9.1",
49
- "sinon": "^18.0.0",
49
+ "sinon": "^19.0.2",
50
50
  "supports-color": "^9.4.0"
51
51
  },
52
52
  "scripts": {
@@ -61,5 +61,15 @@
61
61
  },
62
62
  "publishConfig": {
63
63
  "registry": "https://registry.npmjs.org/"
64
- }
64
+ },
65
+ "funding": [
66
+ {
67
+ "type": "github",
68
+ "url": "https://github.com/sponsors/SeleniumHQ"
69
+ },
70
+ {
71
+ "type": "opencollective",
72
+ "url": "https://opencollective.com/selenium"
73
+ }
74
+ ]
65
75
  }