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.
- package/CHANGES.md +946 -743
- package/README.md +71 -72
- package/bidi/addInterceptParameters.js +65 -45
- package/bidi/argumentValue.js +1 -0
- package/bidi/browser.js +94 -0
- package/bidi/browsingContext.js +214 -32
- package/bidi/browsingContextInspector.js +51 -0
- package/bidi/browsingContextTypes.js +31 -0
- package/bidi/captureScreenshotParameters.js +96 -0
- package/bidi/clipRectangle.js +127 -0
- package/bidi/continueRequestParameters.js +122 -0
- package/bidi/continueResponseParameters.js +129 -0
- package/bidi/cookieFilter.js +139 -0
- package/bidi/createContextParameters.js +73 -0
- package/bidi/evaluateResult.js +17 -0
- package/bidi/index.js +25 -26
- package/bidi/input.js +47 -3
- package/bidi/interceptPhase.js +5 -1
- package/bidi/logEntries.js +66 -0
- package/bidi/network.js +176 -23
- package/bidi/networkTypes.js +394 -17
- package/bidi/partialCookie.js +115 -0
- package/bidi/partitionDescriptor.js +102 -0
- package/bidi/partitionKey.js +53 -0
- package/bidi/protocolType.js +20 -0
- package/bidi/protocolValue.js +140 -9
- package/bidi/provideResponseParameters.js +123 -0
- package/bidi/realmInfo.js +27 -0
- package/bidi/resultOwnership.js +4 -0
- package/bidi/scriptManager.js +131 -5
- package/bidi/scriptTypes.js +36 -0
- package/bidi/storage.js +201 -0
- package/bidi/urlPattern.js +36 -3
- package/bin/linux/selenium-manager +0 -0
- package/bin/macos/selenium-manager +0 -0
- package/bin/windows/selenium-manager.exe +0 -0
- package/chromium.js +13 -9
- package/common/driverFinder.js +36 -4
- package/common/seleniumManager.js +7 -39
- package/devtools/CDPConnection.js +1 -0
- package/devtools/networkinterceptor.js +1 -0
- package/eslint.config.js +107 -0
- package/firefox.js +20 -6
- package/http/index.js +6 -6
- package/http/util.js +1 -0
- package/ie.js +8 -4
- package/index.js +11 -0
- package/io/exec.js +1 -1
- package/io/index.js +3 -2
- package/io/zip.js +1 -1
- package/lib/atoms/find-elements.js +26 -26
- package/lib/atoms/is-displayed.js +24 -97
- package/lib/capabilities.js +5 -5
- package/lib/http.js +8 -4
- package/lib/input.js +0 -1
- package/lib/pinnedScript.js +1 -1
- package/lib/select.js +8 -8
- package/lib/until.js +3 -3
- package/lib/util.js +1 -0
- package/lib/virtual_authenticator.js +8 -8
- package/lib/webdriver.js +8 -3
- package/net/index.js +1 -1
- package/net/portprober.js +1 -1
- package/package.json +21 -13
- package/remote/index.js +1 -1
- package/remote/util.js +2 -2
- package/safari.js +3 -3
- package/testing/index.js +17 -8
package/bidi/storage.js
ADDED
|
@@ -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
|
package/bidi/urlPattern.js
CHANGED
|
@@ -15,21 +15,42 @@
|
|
|
15
15
|
// specific language governing permissions and limitations
|
|
16
16
|
// under the License.
|
|
17
17
|
|
|
18
|
-
|
|
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 {
|
|
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 } =
|
|
625
|
+
const { driverPath, browserPath } = getBinaryPaths(caps)
|
|
625
626
|
service.setExecutable(driverPath)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
vendorOptions
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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 {(
|
|
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
|
package/common/driverFinder.js
CHANGED
|
@@ -21,16 +21,19 @@
|
|
|
21
21
|
* Utility to find if a given file is present and executable.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const
|
|
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
|
|
33
|
+
function getBinaryPaths(capabilities) {
|
|
32
34
|
try {
|
|
33
|
-
|
|
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 = {
|
|
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 {
|
|
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
|
|
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 = {
|
|
115
|
+
module.exports = { binaryPaths }
|
package/eslint.config.js
ADDED
|
@@ -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 {
|
|
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 } =
|
|
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 } =
|
|
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
|
/**
|