selenium-webdriver 4.2.0 → 4.3.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,11 @@
1
+ ## v4.3.0
2
+
3
+ * Fix: using fulfillRequest when intercepting (#10764)
4
+ * Add support CDP 103 and remove CDP 100
5
+ * Remove unused param in input.js & minor code cleanup
6
+ * Fixes lint issues and updates npm packages
7
+ * feat: Added virtual authenticator (#10663)
8
+
1
9
  ## v4.2.0
2
10
 
3
11
  * Delete old android-driver
package/chrome.js CHANGED
@@ -290,9 +290,8 @@ Driver.prototype.VENDOR_COMMAND_PREFIX = 'goog'
290
290
  module.exports = {
291
291
  Driver: Driver,
292
292
  Options,
293
- ServiceBuilder ,
293
+ ServiceBuilder,
294
294
  getDefaultService,
295
295
  setDefaultService,
296
- locateSynchronously
296
+ locateSynchronously,
297
297
  }
298
-
package/chromium.js CHANGED
@@ -860,7 +860,7 @@ class Driver extends webdriver.WebDriver {
860
860
  * @return {!promise.Thenable<void>} A promise that will be resolved
861
861
  * when the mirror command has been issued to the device.
862
862
  */
863
- startDesktopMirroring(deviceName) {
863
+ startDesktopMirroring(deviceName) {
864
864
  return this.schedule(
865
865
  new command.Command(Command.START_CAST_DESKTOP_MIRRORING).setParameter(
866
866
  'sinkName',
@@ -19,7 +19,7 @@ class CDPConnection {
19
19
  constructor(wsConnection) {
20
20
  this._wsConnection = wsConnection
21
21
  this.cmd_id = 0
22
- this.targetID = null;
22
+ this.targetID = null
23
23
  this.sessionId = null
24
24
  }
25
25
 
package/firefox.js CHANGED
@@ -672,7 +672,7 @@ class Driver extends webdriver.WebDriver {
672
672
  */
673
673
  async installAddon(path, temporary = false) {
674
674
  let stats = fs.statSync(path)
675
- let buf;
675
+ let buf
676
676
  if (stats.isDirectory()) {
677
677
  let zip = new Zip()
678
678
  await zip.addDir(path)
package/ie.js CHANGED
@@ -402,9 +402,9 @@ function createServiceFromCapabilities(capabilities) {
402
402
  if (!exe || !fs.existsSync(exe)) {
403
403
  throw Error(
404
404
  `${IEDRIVER_EXE} could not be found on the current PATH. Please ` +
405
- `download the latest version of ${IEDRIVER_EXE} from ` +
406
- 'https://www.selenium.dev/downloads/ and ' +
407
- 'ensure it can be found on your system PATH.'
405
+ `download the latest version of ${IEDRIVER_EXE} from ` +
406
+ 'https://www.selenium.dev/downloads/ and ' +
407
+ 'ensure it can be found on your system PATH.'
408
408
  )
409
409
  }
410
410
 
package/index.js CHANGED
@@ -77,7 +77,7 @@ function startSeleniumServer(jar) {
77
77
  function ensureFileDetectorsAreEnabled(ctor) {
78
78
  return class extends ctor {
79
79
  /** @param {input.FileDetector} detector */
80
- setFileDetector (detector) {
80
+ setFileDetector(detector) {
81
81
  webdriver.WebDriver.prototype.setFileDetector.call(this, detector)
82
82
  }
83
83
  }
package/lib/command.js CHANGED
@@ -164,6 +164,15 @@ const Name = {
164
164
  FIND_ELEMENT_FROM_SHADOWROOT: 'findElementFromShadowRoot',
165
165
  FIND_ELEMENTS_FROM_SHADOWROOT: 'findElementsFromShadowRoot',
166
166
 
167
+ // Virtual Authenticator Commands
168
+ ADD_VIRTUAL_AUTHENTICATOR: 'addVirtualAuthenticator',
169
+ REMOVE_VIRTUAL_AUTHENTICATOR: 'removeVirtualAuthenticator',
170
+ ADD_CREDENTIAL: 'addCredential',
171
+ GET_CREDENTIALS: 'getCredentials',
172
+ REMOVE_CREDENTIAL: 'removeCredential',
173
+ REMOVE_ALL_CREDENTIALS: 'removeAllCredentials',
174
+ SET_USER_VERIFIED: 'setUserVerified',
175
+
167
176
  GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
168
177
  GET_LOG: 'getLog',
169
178
 
package/lib/http.js CHANGED
@@ -347,6 +347,44 @@ const W3C_COMMAND_MAP = new Map([
347
347
 
348
348
  // Server Extensions
349
349
  [cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
350
+
351
+ // Virtual Authenticator
352
+ [
353
+ cmd.Name.ADD_VIRTUAL_AUTHENTICATOR,
354
+ post('/session/:sessionId/webauthn/authenticator'),
355
+ ],
356
+ [
357
+ cmd.Name.REMOVE_VIRTUAL_AUTHENTICATOR,
358
+ del('/session/:sessionId/webauthn/authenticator/:authenticatorId'),
359
+ ],
360
+ [
361
+ cmd.Name.ADD_CREDENTIAL,
362
+ post(
363
+ '/session/:sessionId/webauthn/authenticator/:authenticatorId/credential'
364
+ ),
365
+ ],
366
+ [
367
+ cmd.Name.GET_CREDENTIALS,
368
+ get(
369
+ '/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials'
370
+ ),
371
+ ],
372
+ [
373
+ cmd.Name.REMOVE_CREDENTIAL,
374
+ del(
375
+ '/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId'
376
+ ),
377
+ ],
378
+ [
379
+ cmd.Name.REMOVE_ALL_CREDENTIALS,
380
+ del(
381
+ '/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials'
382
+ ),
383
+ ],
384
+ [
385
+ cmd.Name.SET_USER_VERIFIED,
386
+ post('/session/:sessionId/webauthn/authenticator/:authenticatorId/uv'),
387
+ ],
350
388
  ])
351
389
 
352
390
  /**
@@ -472,6 +510,7 @@ class Executor {
472
510
  this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
473
511
 
474
512
  let httpResponse = /** @type {!Response} */ (response)
513
+
475
514
  let { isW3C, value } = parseHttpResponse(command, httpResponse)
476
515
 
477
516
  if (command.getName() === cmd.Name.NEW_SESSION) {
@@ -530,6 +569,7 @@ function parseHttpResponse(command, httpResponse) {
530
569
  }
531
570
 
532
571
  let parsed = tryParse(httpResponse.body)
572
+
533
573
  if (parsed && typeof parsed === 'object') {
534
574
  let value = parsed.value
535
575
  let isW3C =
package/lib/input.js CHANGED
@@ -346,16 +346,42 @@ class Pointer extends Device {
346
346
 
347
347
  /**
348
348
  * @param {!Button=} button The button to press.
349
+ * @param width
350
+ * @param height
351
+ * @param pressure
352
+ * @param tangentialPressure
353
+ * @param tiltX
354
+ * @param tiltY
355
+ * @param twist
356
+ * @param altitudeAngle
357
+ * @param azimuthAngle
349
358
  * @return {!Action} An action to press the specified button with this device.
350
359
  * @package
351
360
  */
352
- press(button = Button.LEFT, width = 0, height = 0, pressure = 0,
353
- tangentialPressure = 0, tiltX = 0, tiltY = 0, twist = 0,
354
- altitudeAngle = 0, azimuthAngle = 0) {
361
+ press(
362
+ button = Button.LEFT,
363
+ width = 0,
364
+ height = 0,
365
+ pressure = 0,
366
+ tangentialPressure = 0,
367
+ tiltX = 0,
368
+ tiltY = 0,
369
+ twist = 0,
370
+ altitudeAngle = 0,
371
+ azimuthAngle = 0
372
+ ) {
355
373
  return {
356
- type: Action.Type.POINTER_DOWN, button, width, height,
357
- pressure, tangentialPressure, tiltX, tiltY, twist,
358
- altitudeAngle, azimuthAngle
374
+ type: Action.Type.POINTER_DOWN,
375
+ button,
376
+ width,
377
+ height,
378
+ pressure,
379
+ tangentialPressure,
380
+ tiltX,
381
+ tiltY,
382
+ twist,
383
+ altitudeAngle,
384
+ azimuthAngle,
359
385
  }
360
386
  }
361
387
 
@@ -385,15 +411,36 @@ class Pointer extends Device {
385
411
  * @return {!Action} The new action.
386
412
  * @package
387
413
  */
388
- move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT,
389
- width = 0, height = 0, pressure = 0,
390
- tangentialPressure = 0, tiltX = 0, tiltY = 0, twist = 0,
391
- altitudeAngle = 0, azimuthAngle = 0
414
+ move({
415
+ x = 0,
416
+ y = 0,
417
+ duration = 100,
418
+ origin = Origin.VIEWPORT,
419
+ width = 0,
420
+ height = 0,
421
+ pressure = 0,
422
+ tangentialPressure = 0,
423
+ tiltX = 0,
424
+ tiltY = 0,
425
+ twist = 0,
426
+ altitudeAngle = 0,
427
+ azimuthAngle = 0,
392
428
  }) {
393
429
  return {
394
- type: Action.Type.POINTER_MOVE, origin, duration, x, y,
395
- width, height, pressure, tangentialPressure, tiltX, tiltY, twist,
396
- altitudeAngle, azimuthAngle
430
+ type: Action.Type.POINTER_MOVE,
431
+ origin,
432
+ duration,
433
+ x,
434
+ y,
435
+ width,
436
+ height,
437
+ pressure,
438
+ tangentialPressure,
439
+ tiltX,
440
+ tiltY,
441
+ twist,
442
+ altitudeAngle,
443
+ azimuthAngle,
397
444
  }
398
445
  }
399
446
  }
@@ -410,10 +457,9 @@ Pointer.Type = {
410
457
 
411
458
  class Wheel extends Device {
412
459
  /**
413
- * @param {string} id the device ID.
414
- * @param {Pointer.Type} type the pointer type.
460
+ * @param {string} id the device ID..
415
461
  */
416
- constructor(id, type) {
462
+ constructor(id) {
417
463
  super(Device.Type.WHEEL, id)
418
464
  }
419
465
 
@@ -423,15 +469,19 @@ class Wheel extends Device {
423
469
  * @param {number} y starting y coordinate
424
470
  * @param {number} deltaX Delta X to scroll to target
425
471
  * @param {number} deltaY Delta Y to scroll to target
472
+ * @param {WebElement} origin element origin
426
473
  * @param {number} duration duration ratio be the ratio of time delta and duration
427
474
  * @returns {!Action} An action to scroll with this device.
428
475
  */
429
476
  scroll(x, y, deltaX, deltaY, origin, duration) {
430
477
  return {
431
478
  type: Action.Type.SCROLL,
432
- duration: duration, x: x, y: y,
433
- deltaX: deltaX, deltaY: deltaY,
434
- origin: origin
479
+ duration: duration,
480
+ x: x,
481
+ y: y,
482
+ deltaX: deltaX,
483
+ deltaY: deltaY,
484
+ origin: origin,
435
485
  }
436
486
  }
437
487
  }
@@ -795,10 +845,13 @@ class Actions {
795
845
  * @param {number} deltax delta x to scroll to target
796
846
  * @param {number} deltay delta y to scroll to target
797
847
  * @param {number} duration duration ratio be the ratio of time delta and duration
798
- * @returns {!Action} An action to scroll with this device.
848
+ * @returns {!Actions} An action to scroll with this device.
799
849
  */
800
850
  scroll(x, y, targetDeltaX, targetDeltaY, origin, duration) {
801
- return this.insert(this.wheel_, this.wheel_.scroll(x, y, targetDeltaX, targetDeltaY, origin, duration))
851
+ return this.insert(
852
+ this.wheel_,
853
+ this.wheel_.scroll(x, y, targetDeltaX, targetDeltaY, origin, duration)
854
+ )
802
855
  }
803
856
 
804
857
  /**
@@ -0,0 +1,230 @@
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
+ 'use strict'
19
+
20
+ /**
21
+ * Options for the creation of virtual authenticators.
22
+ * @see http://w3c.github.io/webauthn/#sctn-automation
23
+ */
24
+ class VirtualAuthenticatorOptions {
25
+
26
+ static Protocol = {
27
+ "CTAP2": 'ctap2',
28
+ "U2F": 'ctap1/u2f',
29
+ }
30
+
31
+ static Transport = {
32
+ "BLE": 'ble',
33
+ "USB": 'usb',
34
+ "NFC": 'nfc',
35
+ "INTERNAL": 'internal',
36
+ }
37
+
38
+ /**
39
+ * Constructor to initialise VirtualAuthenticatorOptions object.
40
+ */
41
+ constructor() {
42
+ this._protocol = VirtualAuthenticatorOptions.Protocol["CTAP2"]
43
+ this._transport = VirtualAuthenticatorOptions.Transport["USB"]
44
+ this._hasResidentKey = false
45
+ this._hasUserVerification = false
46
+ this._isUserConsenting = true
47
+ this._isUserVerified = false
48
+ }
49
+
50
+ getProtocol() {
51
+ return this._protocol
52
+ }
53
+
54
+ setProtocol(protocol) {
55
+ this._protocol = protocol
56
+ }
57
+
58
+ getTransport() {
59
+ return this._transport
60
+ }
61
+
62
+ setTransport(transport) {
63
+ this._transport = transport
64
+ }
65
+
66
+ getHasResidentKey() {
67
+ return this._hasResidentKey
68
+ }
69
+
70
+ setHasResidentKey(value) {
71
+ this._hasResidentKey = value
72
+ }
73
+
74
+ getHasUserVerification() {
75
+ return this._hasUserVerification
76
+ }
77
+
78
+ setHasUserVerification(value) {
79
+ this._hasUserVerification = value
80
+ }
81
+
82
+ getIsUserConsenting() {
83
+ return this._isUserConsenting
84
+ }
85
+
86
+ setIsUserConsenting(value) {
87
+ this._isUserConsenting = value
88
+ }
89
+
90
+ getIsUserVerified() {
91
+ return this._isUserVerified
92
+ }
93
+
94
+ setIsUserVerified(value) {
95
+ this._isUserVerified = value
96
+ }
97
+
98
+ toDict() {
99
+ return {
100
+ "protocol": this.getProtocol(),
101
+ "transport": this.getTransport(),
102
+ "hasResidentKey": this.getHasResidentKey(),
103
+ "hasUserVerification": this.getHasUserVerification(),
104
+ "isUserConsenting": this.getIsUserConsenting(),
105
+ "isUserVerified": this.getIsUserVerified(),
106
+
107
+ }
108
+ }
109
+ }
110
+
111
+ /**
112
+ * A credential stored in a virtual authenticator.
113
+ * @see https://w3c.github.io/webauthn/#credential-parameters
114
+ */
115
+ class Credential {
116
+ constructor(
117
+ credentialId,
118
+ isResidentCredential,
119
+ rpId,
120
+ userHandle,
121
+ privateKey,
122
+ signCount
123
+ ) {
124
+ this._id = credentialId
125
+ this._isResidentCredential = isResidentCredential
126
+ this._rpId = rpId
127
+ this._userHandle = userHandle
128
+ this._privateKey = privateKey
129
+ this._signCount = signCount
130
+ }
131
+
132
+ id() {
133
+ return this._id
134
+ }
135
+
136
+ isResidentCredential() {
137
+ return this._isResidentCredential
138
+ }
139
+
140
+ rpId() {
141
+ return this._rpId
142
+ }
143
+
144
+ userHandle() {
145
+ if (this._userHandle != null) {
146
+ return this._userHandle
147
+ }
148
+ return null
149
+ }
150
+
151
+ privateKey() {
152
+ return this._privateKey
153
+ }
154
+
155
+ signCount() {
156
+ return this._signCount
157
+ }
158
+
159
+ /**
160
+ * Creates a resident (i.e. stateless) credential.
161
+ * @param id Unique base64 encoded string.
162
+ * @param rpId Relying party identifier.
163
+ * @param userHandle userHandle associated to the credential. Must be Base64 encoded string.
164
+ * @param privateKey Base64 encoded PKCS
165
+ * @param signCount initial value for a signature counter.
166
+ * @returns A resident credential
167
+ */
168
+ createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
169
+ return new Credential(id, true, rpId, userHandle, privateKey, signCount)
170
+ }
171
+
172
+ /**
173
+ * Creates a non-resident (i.e. stateless) credential.
174
+ * @param id Unique base64 encoded string.
175
+ * @param rpId Relying party identifier.
176
+ * @param privateKey Base64 encoded PKCS
177
+ * @param signCount initial value for a signature counter.
178
+ * @returns A non-resident credential
179
+ */
180
+ createNonResidentCredential(id, rpId, privateKey, signCount) {
181
+ return new Credential(id, false, rpId, null, privateKey, signCount)
182
+ }
183
+
184
+ toDict() {
185
+ let credentialData = {
186
+ 'credentialId': Buffer.from(this._id).toString('base64url'),
187
+ 'isResidentCredential': this._isResidentCredential,
188
+ 'rpId': this._rpId,
189
+ 'privateKey': Buffer.from(this._privateKey, 'binary').toString('base64url'),
190
+ 'signCount': this._signCount,
191
+ }
192
+
193
+ if (this.userHandle() != null) {
194
+ credentialData['userHandle'] = Buffer.from(this._userHandle).toString('base64url')
195
+ }
196
+
197
+ return credentialData
198
+ }
199
+
200
+ /**
201
+ * Creates a credential from a map.
202
+ */
203
+ fromDict(data) {
204
+ let id = new Uint8Array(Buffer.from(data['credentialId'], 'base64url'))
205
+ let isResidentCredential = data['isResidentCredential']
206
+ let rpId = data['rpId']
207
+ let privateKey = Buffer.from(data['privateKey'], 'base64url').toString('binary')
208
+ let signCount = data['signCount']
209
+ let userHandle
210
+
211
+ if ('userHandle' in data) {
212
+ userHandle = new Uint8Array(Buffer.from(data['userHandle'], 'base64url'))
213
+ } else {
214
+ userHandle = null
215
+ }
216
+ return new Credential(
217
+ id,
218
+ isResidentCredential,
219
+ rpId,
220
+ userHandle,
221
+ privateKey,
222
+ signCount
223
+ )
224
+ }
225
+ }
226
+
227
+ module.exports = {
228
+ Credential,
229
+ VirtualAuthenticatorOptions,
230
+ }
package/lib/webdriver.js CHANGED
@@ -37,6 +37,7 @@ const { Capabilities } = require('./capabilities')
37
37
  const path = require('path')
38
38
  const { NoSuchElementError } = require('./error')
39
39
  const cdpTargets = ['page', 'browser']
40
+ const Credential = require('./virtual_authenticator').Credential
40
41
 
41
42
  // Capability names that are defined in the W3C spec.
42
43
  const W3C_CAPABILITY_NAMES = new Set([
@@ -679,6 +680,9 @@ class WebDriver {
679
680
 
680
681
  /** @private @const {(function(this: void): ?|undefined)} */
681
682
  this.onQuit_ = onQuit
683
+
684
+ /** @private {./virtual_authenticator}*/
685
+ this.authenticatorId_ = null
682
686
  }
683
687
 
684
688
  /**
@@ -730,6 +734,7 @@ class WebDriver {
730
734
  /** @override */
731
735
  async execute(command) {
732
736
  command.setParameter('sessionId', this.session_)
737
+
733
738
  let parameters = await toWireValue(command.getParameters())
734
739
  command.setParameters(parameters)
735
740
  let value = await this.executor_.execute(command)
@@ -1363,12 +1368,11 @@ class WebDriver {
1363
1368
  if (params.method === 'Fetch.requestPaused') {
1364
1369
  const requestPausedParams = params['params']
1365
1370
  if (requestPausedParams.request.url == httpResponse.urlToIntercept) {
1366
- connection.execute('Fetch.continueRequest', {
1371
+ connection.execute('Fetch.fulfillRequest', {
1367
1372
  requestId: requestPausedParams['requestId'],
1368
- url: httpResponse.urlToIntercept,
1369
- method: httpResponse.method,
1370
- headers: httpResponse.headers,
1371
- postData: httpResponse.body,
1373
+ responseCode: 200,
1374
+ responseHeaders: httpResponse.headers,
1375
+ body: httpResponse.body,
1372
1376
  })
1373
1377
  callback()
1374
1378
  } else {
@@ -1515,6 +1519,110 @@ class WebDriver {
1515
1519
  }
1516
1520
  })
1517
1521
  }
1522
+
1523
+ /**
1524
+ *
1525
+ * @returns The value of authenticator ID added
1526
+ */
1527
+ virtualAuthenticatorId() {
1528
+ return this.authenticatorId_
1529
+ }
1530
+
1531
+ /**
1532
+ * Adds a virtual authenticator with the given options.
1533
+ * @param options VirtualAuthenticatorOptions object to set authenticator optons.
1534
+ */
1535
+ async addVirtualAuthenticator(options) {
1536
+ this.authenticatorId_ = await this.execute(
1537
+ new command.Command(command.Name.ADD_VIRTUAL_AUTHENTICATOR).setParameters(
1538
+ options.toDict()
1539
+ )
1540
+ )
1541
+ }
1542
+
1543
+ /**
1544
+ * Removes a previously added virtual authenticator. The authenticator is no
1545
+ * longer valid after removal, so no methods may be called.
1546
+ */
1547
+ async removeVirtualAuthenticator() {
1548
+ await this.execute(
1549
+ new command.Command(
1550
+ command.Name.REMOVE_VIRTUAL_AUTHENTICATOR
1551
+ ).setParameter('authenticatorId', this.authenticatorId_)
1552
+ )
1553
+ this.authenticatorId_ = null
1554
+ }
1555
+
1556
+ /**
1557
+ * Injects a credential into the authenticator.
1558
+ * @param credential Credential to be added
1559
+ */
1560
+ async addCredential(credential) {
1561
+ credential = credential.toDict()
1562
+ credential['authenticatorId'] = this.authenticatorId_
1563
+ await this.execute(
1564
+ new command.Command(command.Name.ADD_CREDENTIAL).setParameters(credential)
1565
+ )
1566
+ }
1567
+
1568
+ /**
1569
+ *
1570
+ * @returns The list of credentials owned by the authenticator.
1571
+ */
1572
+ async getCredentials() {
1573
+ let credential_data = await this.execute(
1574
+ new command.Command(command.Name.GET_CREDENTIALS).setParameter(
1575
+ 'authenticatorId',
1576
+ this.virtualAuthenticatorId()
1577
+ )
1578
+ )
1579
+ var credential_list = []
1580
+ for (var i = 0; i < credential_data.length; i++) {
1581
+ credential_list.push(new Credential().fromDict(credential_data[i]))
1582
+ }
1583
+ return credential_list
1584
+ }
1585
+
1586
+ /**
1587
+ * Removes a credential from the authenticator.
1588
+ * @param credential_id The ID of the credential to be removed.
1589
+ */
1590
+ async removeCredential(credential_id) {
1591
+ // If credential_id is not a base64url, then convert it to base64url.
1592
+ if (Array.isArray(credential_id)) {
1593
+ credential_id = Buffer.from(credential_id).toString('base64url')
1594
+ }
1595
+
1596
+ await this.execute(
1597
+ new command.Command(command.Name.REMOVE_CREDENTIAL)
1598
+ .setParameter('credentialId', credential_id)
1599
+ .setParameter('authenticatorId', this.authenticatorId_)
1600
+ )
1601
+ }
1602
+
1603
+ /**
1604
+ * Removes all the credentials from the authenticator.
1605
+ */
1606
+ async removeAllCredentials() {
1607
+ await this.execute(
1608
+ new command.Command(command.Name.REMOVE_ALL_CREDENTIALS).setParameter(
1609
+ 'authenticatorId',
1610
+ this.authenticatorId_
1611
+ )
1612
+ )
1613
+ }
1614
+
1615
+ /**
1616
+ * Sets whether the authenticator will simulate success or fail on user verification.
1617
+ * @param verified true if the authenticator will pass user verification, false otherwise.
1618
+ */
1619
+ async setUserVerified(verified) {
1620
+ await this.execute(
1621
+ new command.Command(command.Name.SET_USER_VERIFIED)
1622
+ .setParameter('authenticatorId', this.authenticatorId_)
1623
+ .setParameter('isUserVerified', verified)
1624
+ )
1625
+ }
1518
1626
  }
1519
1627
 
1520
1628
  /**
@@ -2765,17 +2873,18 @@ class WebElement {
2765
2873
  * when the form has been submitted.
2766
2874
  */
2767
2875
  submit() {
2768
- const script = "var form = arguments[0];\n" +
2769
- "while (form.nodeName != \"FORM\" && form.parentNode) {\n" +
2770
- " form = form.parentNode;\n" +
2771
- "}\n" +
2876
+ const script =
2877
+ 'var form = arguments[0];\n' +
2878
+ 'while (form.nodeName != "FORM" && form.parentNode) {\n' +
2879
+ ' form = form.parentNode;\n' +
2880
+ '}\n' +
2772
2881
  "if (!form) { throw Error('Unable to find containing form element'); }\n" +
2773
2882
  "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" +
2774
2883
  "var e = form.ownerDocument.createEvent('Event');\n" +
2775
2884
  "e.initEvent('submit', true, true);\n" +
2776
- "if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n";
2885
+ 'if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n'
2777
2886
 
2778
- this.driver_.executeScript(script, this);
2887
+ this.driver_.executeScript(script, this)
2779
2888
  }
2780
2889
 
2781
2890
  /**
package/net/index.js CHANGED
@@ -48,7 +48,10 @@ function getIPAddress(loopback, family) {
48
48
  }
49
49
 
50
50
  for (let ipAddress of interfaces[key]) {
51
- if ((ipAddress.family === family || `IPv${ipAddress.family}` === family) && ipAddress.internal === loopback) {
51
+ if (
52
+ (ipAddress.family === family || `IPv${ipAddress.family}` === family) &&
53
+ ipAddress.internal === loopback
54
+ ) {
52
55
  return ipAddress.address
53
56
  }
54
57
  }
@@ -73,7 +76,11 @@ function getAddress(family = 'IPv4') {
73
76
  * @return {(string|undefined)} The IP address or undefined if not available.
74
77
  */
75
78
  function getLoopbackAddress(family = 'IPv4') {
76
- return getIPAddress(true, family)
79
+ let address = getIPAddress(true, family)
80
+ if (address === '127.0.0.1') {
81
+ address = 'localhost'
82
+ }
83
+ return address
77
84
  }
78
85
 
79
86
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selenium-webdriver",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "description": "The official WebDriver JavaScript bindings from the Selenium project",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -23,26 +23,26 @@
23
23
  "node": ">= 10.15.0"
24
24
  },
25
25
  "dependencies": {
26
- "jszip": "^3.6.0",
26
+ "jszip": "^3.10.0",
27
27
  "tmp": "^0.2.1",
28
- "ws": ">=7.4.6"
28
+ "ws": ">=8.7.0"
29
29
  },
30
30
  "devDependencies": {
31
- "eslint": "^7.26.0",
32
- "eslint-config-prettier": "^8.3.0",
31
+ "eslint": "^8.16.0",
32
+ "eslint-config-prettier": "^8.5.0",
33
33
  "eslint-plugin-no-only-tests": "^2.6.0",
34
34
  "eslint-plugin-node": "^11.1.0",
35
- "eslint-plugin-prettier": "^3.4.0",
35
+ "eslint-plugin-prettier": "^4.0.0",
36
36
  "express": "^4.17.1",
37
37
  "jasmine": "^3.8.0",
38
- "mocha": "^9.2.2",
39
- "multer": "^1.4.2",
40
- "prettier": "^2.3.0",
38
+ "mocha": "^10.0.0",
39
+ "multer": "^1.4.5-lts.1",
40
+ "prettier": "^2.6.1",
41
41
  "serve-index": "^1.9.1",
42
42
  "sinon": "^10.0.0"
43
43
  },
44
44
  "scripts": {
45
- "lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --ext js lib/http.js \"**/*.js\"",
45
+ "lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --fix --ext js lib/http.js \"**/*.js\"",
46
46
  "test": "npm run lint && mocha -t 600000 --recursive test",
47
47
  "test-jasmine": "jasmine JASMINE_CONFIG_PATH=jasmine.json"
48
48
  },
package/testing/index.js CHANGED
@@ -39,14 +39,14 @@ const firefox = require('../firefox')
39
39
  const ie = require('../ie')
40
40
  const remote = require('../remote')
41
41
  const safari = require('../safari')
42
- const { Browser, Capabilities } = require('../lib/capabilities')
42
+ const { Browser } = require('../lib/capabilities')
43
43
  const { Builder } = require('../index')
44
44
 
45
45
  /**
46
46
  * Describes a browser targeted by a {@linkplain suite test suite}.
47
47
  * @record
48
48
  */
49
- function TargetBrowser() { }
49
+ function TargetBrowser() {}
50
50
 
51
51
  /**
52
52
  * The {@linkplain Browser name} of the targeted browser.
@@ -208,7 +208,7 @@ function init(force = false) {
208
208
  if (seleniumJar && seleniumUrl) {
209
209
  throw Error(
210
210
  'Ambiguous test configuration: both SELENIUM_REMOTE_URL' +
211
- ' && SELENIUM_SERVER_JAR environment variables are set'
211
+ ' && SELENIUM_SERVER_JAR environment variables are set'
212
212
  )
213
213
  }
214
214
 
@@ -216,8 +216,8 @@ function init(force = false) {
216
216
  if ((seleniumJar || seleniumUrl) && envBrowsers.length === 0) {
217
217
  throw Error(
218
218
  'Ambiguous test configuration: when either the SELENIUM_REMOTE_URL or' +
219
- ' SELENIUM_SERVER_JAR environment variable is set, the' +
220
- ' SELENIUM_BROWSER variable must also be set.'
219
+ ' SELENIUM_SERVER_JAR environment variable is set, the' +
220
+ ' SELENIUM_BROWSER variable must also be set.'
221
221
  )
222
222
  }
223
223
 
@@ -234,7 +234,8 @@ function init(force = false) {
234
234
  }
235
235
 
236
236
  const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
237
- const URL_MAP = /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
237
+ const URL_MAP =
238
+ /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
238
239
 
239
240
  /**
240
241
  * Defines the environment a {@linkplain suite test suite} is running against.
@@ -247,9 +248,9 @@ class Environment {
247
248
  * Selenium server to test against.
248
249
  */
249
250
  constructor(browser, url = undefined) {
250
- browser = /** @type {!TargetBrowser} */ (Object.seal(
251
- Object.assign({}, browser)
252
- ))
251
+ browser = /** @type {!TargetBrowser} */ (
252
+ Object.seal(Object.assign({}, browser))
253
+ )
253
254
 
254
255
  TARGET_MAP.set(this, browser)
255
256
  URL_MAP.set(this, url || null)
@@ -284,19 +285,16 @@ class Environment {
284
285
  const builder = new Builder()
285
286
  builder.disableEnvironmentOverrides()
286
287
 
287
-
288
-
289
288
  const realBuild = builder.build
290
289
  builder.build = function () {
291
- builder.forBrowser(browser.name, browser.version, browser.platform);
292
-
290
+ builder.forBrowser(browser.name, browser.version, browser.platform)
293
291
 
294
292
  if (browser.capabilities) {
295
- builder.getCapabilities().merge(browser.capabilities);
293
+ builder.getCapabilities().merge(browser.capabilities)
296
294
  }
297
295
 
298
296
  if (browser.name === 'firefox') {
299
- builder.setCapability('moz:debuggerAddress', true);
297
+ builder.setCapability('moz:debuggerAddress', true)
300
298
  }
301
299
 
302
300
  if (typeof urlOrServer === 'string') {
@@ -315,7 +313,7 @@ class Environment {
315
313
  * Configuration options for a {@linkplain ./index.suite test suite}.
316
314
  * @record
317
315
  */
318
- function SuiteOptions() { }
316
+ function SuiteOptions() {}
319
317
 
320
318
  /**
321
319
  * The browsers to run the test suite against.
@@ -442,7 +440,7 @@ function suite(fn, options = undefined) {
442
440
  * @param {function(): boolean} predicateFn A predicate to call to determine
443
441
  * if the test should be suppressed. This function MUST be synchronous.
444
442
  * @return {{describe: !Function, it: !Function}} an object with wrapped
445
- * versions of the `describe` and `it` wtest functions.
443
+ * versions of the `describe` and `it` test functions.
446
444
  */
447
445
  function ignore(predicateFn) {
448
446
  const isJasmine = global.jasmine && typeof global.jasmine === 'object'
@@ -490,8 +488,8 @@ function getTestHook(name) {
490
488
  if (type !== 'function') {
491
489
  throw TypeError(
492
490
  `Expected global.${name} to be a function, but is ${type}.` +
493
- ' This can happen if you try using this module when running with' +
494
- ' node directly instead of using jasmine or mocha'
491
+ ' This can happen if you try using this module when running with' +
492
+ ' node directly instead of using jasmine or mocha'
495
493
  )
496
494
  }
497
495
  return fn