selenium-webdriver 4.1.2 → 4.3.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.
@@ -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
  /**
@@ -2162,13 +2270,23 @@ class TargetLocator {
2162
2270
  * If the specified frame can not be found, the returned promise will be
2163
2271
  * rejected with a {@linkplain error.NoSuchFrameError}.
2164
2272
  *
2165
- * @param {(number|WebElement|null)} id The frame locator.
2273
+ * @param {(number|string|WebElement|null)} id The frame locator.
2166
2274
  * @return {!Promise<void>} A promise that will be resolved
2167
2275
  * when the driver has changed focus to the specified frame.
2168
2276
  */
2169
2277
  frame(id) {
2278
+ let frameReference = id
2279
+ if (typeof id === 'string') {
2280
+ frameReference = this.driver_
2281
+ .findElement({ id })
2282
+ .catch((_) => this.driver_.findElement({ name: id }))
2283
+ }
2284
+
2170
2285
  return this.driver_.execute(
2171
- new command.Command(command.Name.SWITCH_TO_FRAME).setParameter('id', id)
2286
+ new command.Command(command.Name.SWITCH_TO_FRAME).setParameter(
2287
+ 'id',
2288
+ frameReference
2289
+ )
2172
2290
  )
2173
2291
  }
2174
2292
 
@@ -2755,13 +2873,18 @@ class WebElement {
2755
2873
  * when the form has been submitted.
2756
2874
  */
2757
2875
  submit() {
2758
- const form = this.findElement({ xpath: './ancestor-or-self::form' })
2759
- this.driver_.executeScript(
2760
- "var e = arguments[0].ownerDocument.createEvent('Event');" +
2761
- "e.initEvent('submit', true, true);" +
2762
- 'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }',
2763
- form
2764
- )
2876
+ const script =
2877
+ 'var form = arguments[0];\n' +
2878
+ 'while (form.nodeName != "FORM" && form.parentNode) {\n' +
2879
+ ' form = form.parentNode;\n' +
2880
+ '}\n' +
2881
+ "if (!form) { throw Error('Unable to find containing form element'); }\n" +
2882
+ "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" +
2883
+ "var e = form.ownerDocument.createEvent('Event');\n" +
2884
+ "e.initEvent('submit', true, true);\n" +
2885
+ 'if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n'
2886
+
2887
+ this.driver_.executeScript(script, this)
2765
2888
  }
2766
2889
 
2767
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 && 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.1.2",
3
+ "version": "4.3.1",
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,8 +39,7 @@ const firefox = require('../firefox')
39
39
  const ie = require('../ie')
40
40
  const remote = require('../remote')
41
41
  const safari = require('../safari')
42
- const opera = require('../opera')
43
- const { Browser, Capabilities } = require('../lib/capabilities')
42
+ const { Browser } = require('../lib/capabilities')
44
43
  const { Builder } = require('../index')
45
44
 
46
45
  /**
@@ -123,7 +122,6 @@ function getAvailableBrowsers() {
123
122
  [firefox.locateSynchronously, Browser.FIREFOX],
124
123
  [ie.locateSynchronously, Browser.INTERNET_EXPLORER],
125
124
  [safari.locateSynchronously, Browser.SAFARI],
126
- [opera.locateSynchronously, Browser.OPERA],
127
125
  ]
128
126
 
129
127
  let availableBrowsers = []
@@ -236,7 +234,8 @@ function init(force = false) {
236
234
  }
237
235
 
238
236
  const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
239
- const URL_MAP = /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
237
+ const URL_MAP =
238
+ /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
240
239
 
241
240
  /**
242
241
  * Defines the environment a {@linkplain suite test suite} is running against.
@@ -249,9 +248,9 @@ class Environment {
249
248
  * Selenium server to test against.
250
249
  */
251
250
  constructor(browser, url = undefined) {
252
- browser = /** @type {!TargetBrowser} */ (Object.seal(
253
- Object.assign({}, browser)
254
- ))
251
+ browser = /** @type {!TargetBrowser} */ (
252
+ Object.seal(Object.assign({}, browser))
253
+ )
255
254
 
256
255
  TARGET_MAP.set(this, browser)
257
256
  URL_MAP.set(this, url || null)
@@ -286,19 +285,16 @@ class Environment {
286
285
  const builder = new Builder()
287
286
  builder.disableEnvironmentOverrides()
288
287
 
289
-
290
-
291
288
  const realBuild = builder.build
292
289
  builder.build = function () {
293
- builder.forBrowser(browser.name, browser.version, browser.platform);
294
-
290
+ builder.forBrowser(browser.name, browser.version, browser.platform)
295
291
 
296
292
  if (browser.capabilities) {
297
- builder.getCapabilities().merge(browser.capabilities);
293
+ builder.getCapabilities().merge(browser.capabilities)
298
294
  }
299
295
 
300
- if(browser.name === 'firefox') {
301
- builder.setCapability('moz:debuggerAddress', true);
296
+ if (browser.name === 'firefox') {
297
+ builder.setCapability('moz:debuggerAddress', true)
302
298
  }
303
299
 
304
300
  if (typeof urlOrServer === 'string') {
@@ -444,7 +440,7 @@ function suite(fn, options = undefined) {
444
440
  * @param {function(): boolean} predicateFn A predicate to call to determine
445
441
  * if the test should be suppressed. This function MUST be synchronous.
446
442
  * @return {{describe: !Function, it: !Function}} an object with wrapped
447
- * versions of the `describe` and `it` wtest functions.
443
+ * versions of the `describe` and `it` test functions.
448
444
  */
449
445
  function ignore(predicateFn) {
450
446
  const isJasmine = global.jasmine && typeof global.jasmine === 'object'