selenium-webdriver 4.18.1 → 4.20.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.
Files changed (68) hide show
  1. package/CHANGES.md +946 -743
  2. package/README.md +71 -72
  3. package/bidi/addInterceptParameters.js +65 -45
  4. package/bidi/argumentValue.js +1 -0
  5. package/bidi/browser.js +94 -0
  6. package/bidi/browsingContext.js +214 -32
  7. package/bidi/browsingContextInspector.js +51 -0
  8. package/bidi/browsingContextTypes.js +31 -0
  9. package/bidi/captureScreenshotParameters.js +96 -0
  10. package/bidi/clipRectangle.js +127 -0
  11. package/bidi/continueRequestParameters.js +122 -0
  12. package/bidi/continueResponseParameters.js +129 -0
  13. package/bidi/cookieFilter.js +139 -0
  14. package/bidi/createContextParameters.js +73 -0
  15. package/bidi/evaluateResult.js +17 -0
  16. package/bidi/index.js +25 -26
  17. package/bidi/input.js +47 -3
  18. package/bidi/interceptPhase.js +5 -1
  19. package/bidi/logEntries.js +66 -0
  20. package/bidi/network.js +176 -23
  21. package/bidi/networkTypes.js +394 -17
  22. package/bidi/partialCookie.js +115 -0
  23. package/bidi/partitionDescriptor.js +102 -0
  24. package/bidi/partitionKey.js +53 -0
  25. package/bidi/protocolType.js +20 -0
  26. package/bidi/protocolValue.js +140 -9
  27. package/bidi/provideResponseParameters.js +123 -0
  28. package/bidi/realmInfo.js +27 -0
  29. package/bidi/resultOwnership.js +4 -0
  30. package/bidi/scriptManager.js +131 -5
  31. package/bidi/scriptTypes.js +36 -0
  32. package/bidi/storage.js +201 -0
  33. package/bidi/urlPattern.js +36 -3
  34. package/bin/linux/selenium-manager +0 -0
  35. package/bin/macos/selenium-manager +0 -0
  36. package/bin/windows/selenium-manager.exe +0 -0
  37. package/chromium.js +13 -9
  38. package/common/driverFinder.js +36 -4
  39. package/common/seleniumManager.js +7 -39
  40. package/devtools/CDPConnection.js +1 -0
  41. package/devtools/networkinterceptor.js +1 -0
  42. package/eslint.config.js +107 -0
  43. package/firefox.js +20 -6
  44. package/http/index.js +6 -6
  45. package/http/util.js +1 -0
  46. package/ie.js +8 -4
  47. package/index.js +11 -0
  48. package/io/exec.js +1 -1
  49. package/io/index.js +3 -2
  50. package/io/zip.js +1 -1
  51. package/lib/atoms/find-elements.js +26 -26
  52. package/lib/atoms/is-displayed.js +24 -97
  53. package/lib/capabilities.js +5 -5
  54. package/lib/http.js +8 -4
  55. package/lib/input.js +0 -1
  56. package/lib/pinnedScript.js +1 -1
  57. package/lib/select.js +8 -8
  58. package/lib/until.js +3 -3
  59. package/lib/util.js +1 -0
  60. package/lib/virtual_authenticator.js +8 -8
  61. package/lib/webdriver.js +8 -3
  62. package/net/index.js +1 -1
  63. package/net/portprober.js +1 -1
  64. package/package.json +21 -13
  65. package/remote/index.js +1 -1
  66. package/remote/util.js +2 -2
  67. package/safari.js +3 -3
  68. package/testing/index.js +17 -8
@@ -0,0 +1,201 @@
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
+ const { CookieFilter } = require('./cookieFilter')
19
+ const { BrowsingContextPartitionDescriptor, StorageKeyPartitionDescriptor } = require('./partitionDescriptor')
20
+ const { PartitionKey } = require('./partitionKey')
21
+ const { PartialCookie } = require('./partialCookie')
22
+ const { Cookie, BytesValue } = require('./networkTypes')
23
+
24
+ /**
25
+ * Represents commands of Storage module.
26
+ * Described in https://w3c.github.io/webdriver-bidi/#module-storage.
27
+ * @class
28
+ */
29
+ class Storage {
30
+ constructor(driver) {
31
+ this._driver = driver
32
+ }
33
+
34
+ async init() {
35
+ if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
36
+ throw Error('WebDriver instance must support BiDi protocol')
37
+ }
38
+
39
+ this.bidi = await this._driver.getBidi()
40
+ }
41
+
42
+ /**
43
+ * Retrieves cookies based on the provided filter and partition.
44
+ *
45
+ * @param {CookieFilter} [filter] - The filter to apply to the cookies.
46
+ * @param {(BrowsingContextPartitionDescriptor|StorageKeyPartitionDescriptor)} [partition] - The partition to retrieve cookies from.
47
+ * @returns {Promise<{ cookies: Cookie[], partitionKey?: PartitionKey }>} - A promise that resolves to an object containing the retrieved cookies and an optional partition key.
48
+ * @throws {Error} If the filter parameter is provided but is not an instance of CookieFilter.
49
+ * @throws {Error} If the partition parameter is provided but is not an instance of BrowsingContextPartitionDescriptor or StorageKeyPartitionDescriptor.
50
+ */
51
+ async getCookies(filter = undefined, partition = undefined) {
52
+ if (filter !== undefined && !(filter instanceof CookieFilter)) {
53
+ throw new Error(`Params must be an instance of CookieFilter. Received:'${filter}'`)
54
+ }
55
+
56
+ if (
57
+ partition !== undefined &&
58
+ !(partition instanceof (BrowsingContextPartitionDescriptor || StorageKeyPartitionDescriptor))
59
+ ) {
60
+ throw new Error(`Params must be an instance of PartitionDescriptor. Received:'${partition}'`)
61
+ }
62
+
63
+ const command = {
64
+ method: 'storage.getCookies',
65
+ params: {
66
+ filter: filter ? Object.fromEntries(filter.asMap()) : undefined,
67
+ partition: partition ? Object.fromEntries(partition.asMap()) : undefined,
68
+ },
69
+ }
70
+
71
+ let response = await this.bidi.send(command)
72
+
73
+ let cookies = []
74
+ response.result.cookies.forEach((cookie) => {
75
+ cookies.push(
76
+ new Cookie(
77
+ cookie.name,
78
+ new BytesValue(cookie.value.type, cookie.value.value),
79
+ cookie.domain,
80
+ cookie.path,
81
+ cookie.size,
82
+ cookie.httpOnly,
83
+ cookie.secure,
84
+ cookie.sameSite,
85
+ cookie.expiry,
86
+ ),
87
+ )
88
+ })
89
+
90
+ if (Object.prototype.hasOwnProperty.call(response.result, 'partitionKey')) {
91
+ if (
92
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'userContext') &&
93
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'sourceOrigin')
94
+ ) {
95
+ let partitionKey = new PartitionKey(
96
+ response.result.partitionKey.userContext,
97
+ response.result.partitionKey.sourceOrigin,
98
+ )
99
+ return { cookies, partitionKey }
100
+ }
101
+
102
+ return { cookies }
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Sets a cookie using the provided cookie object and partition.
108
+ *
109
+ * @param {PartialCookie} cookie - The cookie object to set.
110
+ * @param {(BrowsingContextPartitionDescriptor|StorageKeyPartitionDescriptor)} [partition] - The partition to use for the cookie.
111
+ * @returns {PartitionKey} The partition key of the set cookie.
112
+ * @throws {Error} If the cookie parameter is not an instance of PartialCookie or if the partition parameter is not an instance of PartitionDescriptor.
113
+ */
114
+ async setCookie(cookie, partition = undefined) {
115
+ if (!(cookie instanceof PartialCookie)) {
116
+ throw new Error(`Params must be an instance of PartialCookie. Received:'${cookie}'`)
117
+ }
118
+
119
+ if (
120
+ partition !== undefined &&
121
+ !(partition instanceof (BrowsingContextPartitionDescriptor || StorageKeyPartitionDescriptor))
122
+ ) {
123
+ throw new Error(`Params must be an instance of PartitionDescriptor. Received:'${partition}'`)
124
+ }
125
+
126
+ const command = {
127
+ method: 'storage.setCookie',
128
+ params: {
129
+ cookie: cookie ? Object.fromEntries(cookie.asMap()) : undefined,
130
+ partition: partition ? Object.fromEntries(partition.asMap()) : undefined,
131
+ },
132
+ }
133
+
134
+ let response = await this.bidi.send(command)
135
+
136
+ if (Object.prototype.hasOwnProperty.call(response.result, 'partitionKey')) {
137
+ if (
138
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'userContext') &&
139
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'sourceOrigin')
140
+ ) {
141
+ let partitionKey = new PartitionKey(
142
+ response.result.partitionKey.userContext,
143
+ response.result.partitionKey.sourceOrigin,
144
+ )
145
+ return partitionKey
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Deletes cookies based on the provided filter and partition.
152
+ *
153
+ * @param {CookieFilter} [cookieFilter] - The filter to apply to the cookies. Must be an instance of CookieFilter.
154
+ * @param {(BrowsingContextPartitionDescriptor|StorageKeyPartitionDescriptor)} [partition] - The partition to delete cookies from. Must be an instance of either BrowsingContextPartitionDescriptor or StorageKeyPartitionDescriptor.
155
+ * @returns {PartitionKey} - The partition key of the deleted cookies, if available.
156
+ * @throws {Error} - If the provided parameters are not of the correct type.
157
+ */
158
+ async deleteCookies(cookieFilter = undefined, partition = undefined) {
159
+ if (cookieFilter !== undefined && !(cookieFilter instanceof CookieFilter)) {
160
+ throw new Error(`Params must be an instance of CookieFilter. Received:'${cookieFilter}'`)
161
+ }
162
+
163
+ if (
164
+ partition !== undefined &&
165
+ !(partition instanceof (BrowsingContextPartitionDescriptor || StorageKeyPartitionDescriptor))
166
+ ) {
167
+ throw new Error(`Params must be an instance of PartitionDescriptor. Received:'${partition}'`)
168
+ }
169
+
170
+ const command = {
171
+ method: 'storage.deleteCookies',
172
+ params: {
173
+ filter: cookieFilter ? Object.fromEntries(cookieFilter.asMap()) : undefined,
174
+ partition: partition ? Object.fromEntries(partition.asMap()) : undefined,
175
+ },
176
+ }
177
+
178
+ let response = await this.bidi.send(command)
179
+
180
+ if (Object.prototype.hasOwnProperty.call(response.result, 'partitionKey')) {
181
+ if (
182
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'userContext') &&
183
+ Object.prototype.hasOwnProperty.call(response.result.partitionKey, 'sourceOrigin')
184
+ ) {
185
+ let partitionKey = new PartitionKey(
186
+ response.result.partitionKey.userContext,
187
+ response.result.partitionKey.sourceOrigin,
188
+ )
189
+ return partitionKey
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ async function getStorageInstance(driver) {
196
+ let instance = new Storage(driver)
197
+ await instance.init()
198
+ return instance
199
+ }
200
+
201
+ module.exports = getStorageInstance
@@ -15,21 +15,42 @@
15
15
  // specific language governing permissions and limitations
16
16
  // under the License.
17
17
 
18
- const {LocalValue} = require('./protocolValue')
19
-
18
+ /**
19
+ * Represents a URL pattern to intercept.
20
+ * Described in network.UrlPatternPattern https://w3c.github.io/webdriver-bidi/#type-network-UrlPattern
21
+ */
20
22
  class UrlPattern {
21
23
  #map = new Map()
22
24
 
25
+ /**
26
+ * Sets the protocol for the URL pattern.
27
+ *
28
+ * @param {string} protocol - The protocol to set.
29
+ * @returns {UrlPattern} - Returns the updated instance of the URL pattern for chaining.
30
+ */
23
31
  protocol(protocol) {
24
32
  this.#map.set('protocol', protocol)
25
33
  return this
26
34
  }
27
35
 
36
+ /**
37
+ * Sets the hostname for the URL pattern.
38
+ *
39
+ * @param {string} hostname - The hostname to set.
40
+ * @returns {UrlPattern} - Returns the updated instance of the URL pattern for chaining.
41
+ */
28
42
  hostname(hostname) {
29
43
  this.#map.set('hostname', hostname)
30
44
  return this
31
45
  }
32
46
 
47
+ /**
48
+ * Sets the port for the URL pattern.
49
+ *
50
+ * @param {number} port - The port number to set.
51
+ * @returns {UrlPattern} - Returns the updated instance of the URL pattern for chaining.
52
+ * @throws {Error} - Throws an error if the port is not a number.
53
+ */
33
54
  port(port) {
34
55
  if (typeof port === 'number') {
35
56
  this.#map.set('port', port.toString())
@@ -39,11 +60,23 @@ class UrlPattern {
39
60
  return this
40
61
  }
41
62
 
63
+ /**
64
+ * Sets the pathname for the URL pattern.
65
+ *
66
+ * @param {string} pathname - The pathname to set.
67
+ * @returns {UrlPattern} - Returns the updated instance of the URL pattern for chaining.
68
+ */
42
69
  pathname(pathname) {
43
70
  this.#map.set('pathname', pathname)
44
71
  return this
45
72
  }
46
73
 
74
+ /**
75
+ * Sets the search parameter in the URL pattern.
76
+ *
77
+ * @param {string} search - The search parameter to be set.
78
+ * @returns {UrlPattern} - Returns the updated instance of the URL pattern for chaining.
79
+ */
47
80
  search(search) {
48
81
  this.#map.set('search', search)
49
82
  return this
@@ -55,4 +88,4 @@ class UrlPattern {
55
88
  }
56
89
  }
57
90
 
58
- module.exports = {UrlPattern}
91
+ module.exports = { UrlPattern }
Binary file
Binary file
Binary file
package/chromium.js CHANGED
@@ -78,7 +78,7 @@ const error = require('./lib/error')
78
78
  const Symbols = require('./lib/symbols')
79
79
  const webdriver = require('./lib/webdriver')
80
80
  const remote = require('./remote')
81
- const { getPath } = require('./common/driverFinder')
81
+ const { getBinaryPaths } = require('./common/driverFinder')
82
82
 
83
83
  /**
84
84
  * Custom command names supported by Chromium WebDriver.
@@ -287,6 +287,7 @@ class Options extends Capabilities {
287
287
  throw TypeError('Arguments must be {width, height} with numbers > 0')
288
288
  }
289
289
  }
290
+
290
291
  checkArg(width)
291
292
  checkArg(height)
292
293
  return this.addArguments(`window-size=${width},${height}`)
@@ -621,14 +622,17 @@ class Driver extends webdriver.WebDriver {
621
622
  } else {
622
623
  let service = opt_serviceExecutor || this.getDefaultService()
623
624
  if (!service.getExecutable()) {
624
- const { driverPath, browserPath } = getPath(caps)
625
+ const { driverPath, browserPath } = getBinaryPaths(caps)
625
626
  service.setExecutable(driverPath)
626
- const vendorOptions = caps.get(vendorCapabilityKey)
627
- if (vendorOptions) {
628
- vendorOptions['binary'] = browserPath
629
- caps.set(vendorCapabilityKey, vendorOptions)
630
- } else {
631
- caps.set(vendorCapabilityKey, { binary: browserPath })
627
+ if (browserPath) {
628
+ const vendorOptions = caps.get(vendorCapabilityKey)
629
+ if (vendorOptions) {
630
+ vendorOptions['binary'] = browserPath
631
+ caps.set(vendorCapabilityKey, vendorOptions)
632
+ } else {
633
+ caps.set(vendorCapabilityKey, { binary: browserPath })
634
+ }
635
+ caps.delete(Capability.BROWSER_VERSION)
632
636
  }
633
637
  }
634
638
  onQuit = () => service.kill()
@@ -742,7 +746,7 @@ class Driver extends webdriver.WebDriver {
742
746
  * Set a permission state to the given value.
743
747
  *
744
748
  * @param {string} name A name of the permission to update.
745
- * @param {('granted'|'denied'|'prompt')} state State to set permission to.
749
+ * @param {("granted"|"denied"|"prompt")} state State to set permission to.
746
750
  * @returns {!Promise<Object>} A promise that will be resolved when the
747
751
  * command has finished.
748
752
  * @see <https://w3c.github.io/permissions/#permission-registry> for valid
@@ -21,16 +21,19 @@
21
21
  * Utility to find if a given file is present and executable.
22
22
  */
23
23
 
24
- const { driverLocation } = require('./seleniumManager')
24
+ const path = require('node:path')
25
+ const { binaryPaths } = require('./seleniumManager')
25
26
 
26
27
  /**
27
28
  * Determines the path of the correct Selenium Manager binary
29
+ * @param {Capabilities} capabilities browser options to fetch the driver
28
30
  * @returns {{browserPath: string, driverPath: string}} path of the driver
29
31
  * and browser location
30
32
  */
31
- function getPath(capabilities) {
33
+ function getBinaryPaths(capabilities) {
32
34
  try {
33
- return driverLocation(capabilities)
35
+ const args = getArgs(capabilities)
36
+ return binaryPaths(args)
34
37
  } catch (e) {
35
38
  throw Error(
36
39
  `Unable to obtain browser driver.
@@ -40,5 +43,34 @@ function getPath(capabilities) {
40
43
  }
41
44
  }
42
45
 
46
+ function getArgs(options) {
47
+ let args = ['--browser', options.getBrowserName(), '--language-binding', 'javascript', '--output', 'json']
48
+
49
+ if (options.getBrowserVersion() && options.getBrowserVersion() !== '') {
50
+ args.push('--browser-version', options.getBrowserVersion())
51
+ }
52
+
53
+ const vendorOptions =
54
+ options.get('goog:chromeOptions') || options.get('ms:edgeOptions') || options.get('moz:firefoxOptions')
55
+ if (vendorOptions && vendorOptions.binary && vendorOptions.binary !== '') {
56
+ args.push('--browser-path', path.resolve(vendorOptions.binary))
57
+ }
58
+
59
+ const proxyOptions = options.getProxy()
60
+
61
+ // Check if proxyOptions exists and has properties
62
+ if (proxyOptions && Object.keys(proxyOptions).length > 0) {
63
+ const httpProxy = proxyOptions['httpProxy']
64
+ const sslProxy = proxyOptions['sslProxy']
65
+
66
+ if (httpProxy !== undefined) {
67
+ args.push('--proxy', httpProxy)
68
+ } else if (sslProxy !== undefined) {
69
+ args.push('--proxy', sslProxy)
70
+ }
71
+ }
72
+ return args
73
+ }
74
+
43
75
  // PUBLIC API
44
- module.exports = { getPath }
76
+ module.exports = { getBinaryPaths }
@@ -21,11 +21,10 @@
21
21
  * Wrapper for getting information from the Selenium Manager binaries
22
22
  */
23
23
 
24
- const { platform } = require('process')
25
- const path = require('path')
26
- const fs = require('fs')
27
- const spawnSync = require('child_process').spawnSync
28
- const { Capability } = require('../lib/capabilities')
24
+ const { platform } = require('node:process')
25
+ const path = require('node:path')
26
+ const fs = require('node:fs')
27
+ const spawnSync = require('node:child_process').spawnSync
29
28
  const logging = require('../lib/logging')
30
29
 
31
30
  const log_ = logging.getLogger(logging.Type.DRIVER)
@@ -63,38 +62,12 @@ function getBinary() {
63
62
 
64
63
  /**
65
64
  * Determines the path of the correct driver
66
- * @param {Capabilities} options browser options to fetch the driver
65
+ * @param {string[]} args arguments to invoke Selenium Manager
67
66
  * @returns {{browserPath: string, driverPath: string}} path of the driver and
68
67
  * browser location
69
68
  */
70
69
 
71
- function driverLocation(options) {
72
- let args = ['--browser', options.getBrowserName(), '--language-binding', 'javascript', '--output', 'json']
73
-
74
- if (options.getBrowserVersion() && options.getBrowserVersion() !== '') {
75
- args.push('--browser-version', options.getBrowserVersion())
76
- }
77
-
78
- const vendorOptions =
79
- options.get('goog:chromeOptions') || options.get('ms:edgeOptions') || options.get('moz:firefoxOptions')
80
- if (vendorOptions && vendorOptions.binary && vendorOptions.binary !== '') {
81
- args.push('--browser-path', path.resolve(vendorOptions.binary))
82
- }
83
-
84
- const proxyOptions = options.getProxy()
85
-
86
- // Check if proxyOptions exists and has properties
87
- if (proxyOptions && Object.keys(proxyOptions).length > 0) {
88
- const httpProxy = proxyOptions['httpProxy']
89
- const sslProxy = proxyOptions['sslProxy']
90
-
91
- if (httpProxy !== undefined) {
92
- args.push('--proxy', httpProxy)
93
- } else if (sslProxy !== undefined) {
94
- args.push('--proxy', sslProxy)
95
- }
96
- }
97
-
70
+ function binaryPaths(args) {
98
71
  const smBinary = getBinary()
99
72
  const spawnResult = spawnSync(smBinary, args)
100
73
  let output
@@ -120,11 +93,6 @@ function driverLocation(options) {
120
93
  throw new Error(`Error executing command for ${smBinary} with ${args}: ${e.toString()}`)
121
94
  }
122
95
 
123
- // Once driverPath is available, delete browserVersion from payload
124
- if (output.result.driver_path) {
125
- options.delete(Capability.BROWSER_VERSION)
126
- }
127
-
128
96
  logOutput(output)
129
97
  return {
130
98
  driverPath: output.result.driver_path,
@@ -144,4 +112,4 @@ function logOutput(output) {
144
112
  }
145
113
 
146
114
  // PUBLIC API
147
- module.exports = { driverLocation }
115
+ module.exports = { binaryPaths }
@@ -18,6 +18,7 @@
18
18
  const logging = require('../lib/logging')
19
19
 
20
20
  const RESPONSE_TIMEOUT = 1000 * 30
21
+
21
22
  class CDPConnection {
22
23
  constructor(wsConnection) {
23
24
  this._wsConnection = wsConnection
@@ -85,4 +85,5 @@ class HttpResponse {
85
85
  return this.returnMethod
86
86
  }
87
87
  }
88
+
88
89
  exports.HttpResponse = HttpResponse
@@ -0,0 +1,107 @@
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
+ const globals = require('globals')
19
+ const noOnlyTests = require('eslint-plugin-no-only-tests')
20
+ const js = require('@eslint/js')
21
+ const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended')
22
+ const mochaPlugin = require('eslint-plugin-mocha')
23
+ const nodePlugin = require('eslint-plugin-n')
24
+
25
+ module.exports = [
26
+ js.configs.recommended,
27
+ eslintPluginPrettierRecommended,
28
+ mochaPlugin.configs.flat.recommended,
29
+ nodePlugin.configs['flat/recommended-script'],
30
+ {
31
+ languageOptions: {
32
+ globals: {
33
+ mocha: true,
34
+ es6: true,
35
+ ...globals.node,
36
+ },
37
+ parserOptions: {
38
+ ecmaVersion: 2022,
39
+ },
40
+ },
41
+ files: ['**/*.js', 'lib/http.js'],
42
+ ignores: ['node_modules/*', 'generator/*', 'devtools/generator/'],
43
+ plugins: {
44
+ 'no-only-tests': noOnlyTests,
45
+ },
46
+ rules: {
47
+ 'no-const-assign': 'error',
48
+ 'no-this-before-super': 'error',
49
+ 'no-undef': 'error',
50
+ 'no-unreachable': 'error',
51
+ 'no-unused-vars': [
52
+ 'error',
53
+ {
54
+ varsIgnorePattern: '^_',
55
+ args: 'all',
56
+ argsIgnorePattern: '^_',
57
+ },
58
+ ],
59
+ 'constructor-super': 'error',
60
+ 'valid-typeof': 'error',
61
+ 'no-only-tests/no-only-tests': 'error',
62
+ 'n/no-deprecated-api': ['error'],
63
+ 'n/no-missing-import': ['error'],
64
+ 'n/no-missing-require': ['error'],
65
+ 'n/no-mixed-requires': ['error'],
66
+ 'n/no-new-require': ['error'],
67
+ 'n/no-unpublished-import': ['error'],
68
+ 'n/no-unpublished-require': [
69
+ 'error',
70
+ {
71
+ allowModules: [
72
+ 'globals',
73
+ '@eslint/js',
74
+ 'eslint-plugin-mocha',
75
+ 'eslint-plugin-prettier',
76
+ 'eslint-plugin-n',
77
+ 'eslint-plugin-no-only-tests',
78
+ ],
79
+ tryExtensions: ['.js'],
80
+ },
81
+ ],
82
+ 'n/prefer-node-protocol': ['error'],
83
+ 'mocha/no-skipped-tests': ['off'],
84
+ 'mocha/no-mocha-arrows': ['off'],
85
+ 'mocha/no-setup-in-describe': ['off'],
86
+ 'mocha/no-top-level-hooks': ['off'],
87
+ 'mocha/no-sibling-hooks': ['off'],
88
+ 'mocha/no-exports': ['off'],
89
+ 'mocha/no-empty-description': ['off'],
90
+ 'mocha/max-top-level-suites': ['off'],
91
+ 'mocha/consistent-spacing-between-blocks': ['off'],
92
+ 'mocha/no-nested-tests': ['off'],
93
+ 'mocha/no-pending-tests': ['off'],
94
+ 'mocha/no-identical-title': ['off'],
95
+ 'prettier/prettier': [
96
+ 'error',
97
+ {
98
+ endOfLine: 'lf',
99
+ printWidth: 120,
100
+ semi: false,
101
+ singleQuote: true,
102
+ trailingComma: 'all',
103
+ },
104
+ ],
105
+ },
106
+ },
107
+ ]
package/firefox.js CHANGED
@@ -107,8 +107,8 @@
107
107
 
108
108
  'use strict'
109
109
 
110
- const fs = require('fs')
111
- const path = require('path')
110
+ const fs = require('node:fs')
111
+ const path = require('node:path')
112
112
  const Symbols = require('./lib/symbols')
113
113
  const command = require('./lib/command')
114
114
  const http = require('./http')
@@ -116,9 +116,9 @@ const io = require('./io')
116
116
  const remote = require('./remote')
117
117
  const webdriver = require('./lib/webdriver')
118
118
  const zip = require('./io/zip')
119
- const { Browser, Capabilities } = require('./lib/capabilities')
119
+ const { Browser, Capabilities, Capability } = require('./lib/capabilities')
120
120
  const { Zip } = require('./io/zip')
121
- const { getPath } = require('./common/driverFinder')
121
+ const { getBinaryPaths } = require('./common/driverFinder')
122
122
  const FIREFOX_CAPABILITY_KEY = 'moz:firefoxOptions'
123
123
 
124
124
  /**
@@ -444,6 +444,7 @@ const ExtensionCommand = {
444
444
  SET_CONTEXT: 'setContext',
445
445
  INSTALL_ADDON: 'install addon',
446
446
  UNINSTALL_ADDON: 'uninstall addon',
447
+ FULL_PAGE_SCREENSHOT: 'fullPage screenshot',
447
448
  }
448
449
 
449
450
  /**
@@ -470,6 +471,8 @@ function configureExecutor(executor) {
470
471
  executor.defineCommand(ExtensionCommand.INSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/install')
471
472
 
472
473
  executor.defineCommand(ExtensionCommand.UNINSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/uninstall')
474
+
475
+ executor.defineCommand(ExtensionCommand.FULL_PAGE_SCREENSHOT, 'GET', '/session/:sessionId/moz/screenshot/full')
473
476
  }
474
477
 
475
478
  /**
@@ -538,7 +541,7 @@ class Driver extends webdriver.WebDriver {
538
541
  configureExecutor(executor)
539
542
  } else if (opt_executor instanceof remote.DriverService) {
540
543
  if (!opt_executor.getExecutable()) {
541
- const { driverPath, browserPath } = getPath(caps)
544
+ const { driverPath, browserPath } = getBinaryPaths(caps)
542
545
  opt_executor.setExecutable(driverPath)
543
546
  firefoxBrowserPath = browserPath
544
547
  }
@@ -547,7 +550,7 @@ class Driver extends webdriver.WebDriver {
547
550
  } else {
548
551
  let service = new ServiceBuilder().build()
549
552
  if (!service.getExecutable()) {
550
- const { driverPath, browserPath } = getPath(caps)
553
+ const { driverPath, browserPath } = getBinaryPaths(caps)
551
554
  service.setExecutable(driverPath)
552
555
  firefoxBrowserPath = browserPath
553
556
  }
@@ -563,6 +566,7 @@ class Driver extends webdriver.WebDriver {
563
566
  } else {
564
567
  caps.set(FIREFOX_CAPABILITY_KEY, { binary: firefoxBrowserPath })
565
568
  }
569
+ caps.delete(Capability.BROWSER_VERSION)
566
570
  }
567
571
 
568
572
  return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
@@ -645,6 +649,16 @@ class Driver extends webdriver.WebDriver {
645
649
  id = await Promise.resolve(id)
646
650
  return this.execute(new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter('id', id))
647
651
  }
652
+
653
+ /**
654
+ * Take full page screenshot of the visible region
655
+ *
656
+ * @return {!Promise<string>} A promise that will be
657
+ * resolved to the screenshot as a base-64 encoded PNG.
658
+ */
659
+ takeFullPageScreenshot() {
660
+ return this.execute(new command.Command(ExtensionCommand.FULL_PAGE_SCREENSHOT))
661
+ }
648
662
  }
649
663
 
650
664
  /**