selenium-webdriver 4.1.1 → 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.
@@ -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)
@@ -1197,14 +1202,25 @@ class WebDriver {
1197
1202
  * @return {!Promise<resolved>} A new CDP instance.
1198
1203
  */
1199
1204
  async createCDPConnection(target) {
1205
+ let debuggerUrl = null
1206
+
1200
1207
  const caps = await this.getCapabilities()
1201
- const seCdp = caps['map_'].get('se:cdp')
1202
- const vendorInfo =
1203
- caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
1204
- caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
1205
- caps['map_'].get('moz:debuggerAddress') ||
1206
- new Map()
1207
- const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
1208
+
1209
+ if (process.env.SELENIUM_REMOTE_URL) {
1210
+ const host = new URL(process.env.SELENIUM_REMOTE_URL).host
1211
+ const sessionId = await this.getSession().then((session) =>
1212
+ session.getId()
1213
+ )
1214
+ debuggerUrl = `ws://${host}/session/${sessionId}/se/cdp`
1215
+ } else {
1216
+ const seCdp = caps['map_'].get('se:cdp')
1217
+ const vendorInfo =
1218
+ caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
1219
+ caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
1220
+ caps['map_'].get('moz:debuggerAddress') ||
1221
+ new Map()
1222
+ debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
1223
+ }
1208
1224
  this._wsUrl = await this.getWsUrl(debuggerUrl, target, caps)
1209
1225
  return new Promise((resolve, reject) => {
1210
1226
  try {
@@ -1352,12 +1368,11 @@ class WebDriver {
1352
1368
  if (params.method === 'Fetch.requestPaused') {
1353
1369
  const requestPausedParams = params['params']
1354
1370
  if (requestPausedParams.request.url == httpResponse.urlToIntercept) {
1355
- connection.execute('Fetch.continueRequest', {
1371
+ connection.execute('Fetch.fulfillRequest', {
1356
1372
  requestId: requestPausedParams['requestId'],
1357
- url: httpResponse.urlToIntercept,
1358
- method: httpResponse.method,
1359
- headers: httpResponse.headers,
1360
- postData: httpResponse.body,
1373
+ responseCode: 200,
1374
+ responseHeaders: httpResponse.headers,
1375
+ body: httpResponse.body,
1361
1376
  })
1362
1377
  callback()
1363
1378
  } else {
@@ -1504,6 +1519,110 @@ class WebDriver {
1504
1519
  }
1505
1520
  })
1506
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
+ }
1507
1626
  }
1508
1627
 
1509
1628
  /**
@@ -1945,14 +2064,10 @@ class Window {
1945
2064
  * @return {!Promise<{x: number, y: number, width: number, height: number}>}
1946
2065
  * A promise that will resolve to the window rect of the current window.
1947
2066
  */
1948
- async getRect() {
1949
- try {
1950
- return await this.driver_.execute(
1951
- new command.Command(command.Name.GET_WINDOW_RECT)
1952
- )
1953
- } catch (ex) {
1954
- throw ex
1955
- }
2067
+ getRect() {
2068
+ return this.driver_.execute(
2069
+ new command.Command(command.Name.GET_WINDOW_RECT)
2070
+ )
1956
2071
  }
1957
2072
 
1958
2073
  /**
@@ -1969,19 +2084,15 @@ class Window {
1969
2084
  * A promise that will resolve to the current window's updated window
1970
2085
  * rect.
1971
2086
  */
1972
- async setRect({ x, y, width, height }) {
1973
- try {
1974
- return await this.driver_.execute(
1975
- new command.Command(command.Name.SET_WINDOW_RECT).setParameters({
1976
- x,
1977
- y,
1978
- width,
1979
- height,
1980
- })
1981
- )
1982
- } catch (ex) {
1983
- throw ex
1984
- }
2087
+ setRect({ x, y, width, height }) {
2088
+ return this.driver_.execute(
2089
+ new command.Command(command.Name.SET_WINDOW_RECT).setParameters({
2090
+ x,
2091
+ y,
2092
+ width,
2093
+ height,
2094
+ })
2095
+ )
1985
2096
  }
1986
2097
 
1987
2098
  /**
@@ -2159,13 +2270,23 @@ class TargetLocator {
2159
2270
  * If the specified frame can not be found, the returned promise will be
2160
2271
  * rejected with a {@linkplain error.NoSuchFrameError}.
2161
2272
  *
2162
- * @param {(number|WebElement|null)} id The frame locator.
2273
+ * @param {(number|string|WebElement|null)} id The frame locator.
2163
2274
  * @return {!Promise<void>} A promise that will be resolved
2164
2275
  * when the driver has changed focus to the specified frame.
2165
2276
  */
2166
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
+
2167
2285
  return this.driver_.execute(
2168
- 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
+ )
2169
2290
  )
2170
2291
  }
2171
2292
 
@@ -2522,12 +2643,12 @@ class WebElement {
2522
2643
  if (type === 'number') {
2523
2644
  key = String(key)
2524
2645
  } else if (type !== 'string') {
2525
- throw TypeError('each key must be a number of string; got ' + type)
2646
+ throw TypeError('each key must be a number or string; got ' + type)
2526
2647
  }
2527
2648
 
2528
2649
  // The W3C protocol requires keys to be specified as an array where
2529
2650
  // each element is a single key.
2530
- keys.push(...key.split(''))
2651
+ keys.push(...key)
2531
2652
  })
2532
2653
 
2533
2654
  if (!this.driver_.fileDetector_) {
@@ -2544,7 +2665,10 @@ class WebElement {
2544
2665
  keys.join('')
2545
2666
  )
2546
2667
  } catch (ex) {
2547
- console.log('Error trying parse string as a file with file detector; sending keys instead' + ex)
2668
+ console.log(
2669
+ 'Error trying parse string as a file with file detector; sending keys instead' +
2670
+ ex
2671
+ )
2548
2672
  }
2549
2673
 
2550
2674
  return this.execute_(
@@ -2715,14 +2839,8 @@ class WebElement {
2715
2839
  * @return {!Promise<{width: number, height: number, x: number, y: number}>}
2716
2840
  * A promise that will resolve with the element's rect.
2717
2841
  */
2718
- async getRect() {
2719
- try {
2720
- return await this.execute_(
2721
- new command.Command(command.Name.GET_ELEMENT_RECT)
2722
- )
2723
- } catch (err) {
2724
- throw err;
2725
- }
2842
+ getRect() {
2843
+ return this.execute_(new command.Command(command.Name.GET_ELEMENT_RECT))
2726
2844
  }
2727
2845
 
2728
2846
  /**
@@ -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.1",
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": "^8.1.0",
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'