selenium-webdriver 4.8.1 → 4.9.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,325 @@
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 {
19
+ EvaluateResultType,
20
+ EvaluateResultSuccess,
21
+ EvaluateResultException,
22
+ ExceptionDetails,
23
+ } = require('./evaluateResult')
24
+ const { RealmInfo } = require('./realmInfo')
25
+ const { RemoteValue } = require('./protocolValue')
26
+
27
+ class ScriptManager {
28
+ constructor(driver) {
29
+ this._driver = driver
30
+ }
31
+
32
+ async init(browsingContextId) {
33
+ if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
34
+ throw Error('WebDriver instance must support BiDi protocol')
35
+ }
36
+
37
+ this.bidi = await this._driver.getBidi()
38
+ this._browsingContextId = browsingContextId
39
+ }
40
+
41
+ async disownRealmScript(realmId, handles) {
42
+ const params = {
43
+ method: 'script.disown',
44
+ params: {
45
+ handles: handles,
46
+ target: {
47
+ realm: realmId,
48
+ },
49
+ },
50
+ }
51
+
52
+ await this.bidi.send(params)
53
+ }
54
+
55
+ async disownBrowsingContextScript(
56
+ browsingContextId,
57
+ handles,
58
+ sandbox = null
59
+ ) {
60
+ const params = {
61
+ method: 'script.disown',
62
+ params: {
63
+ handles: handles,
64
+ target: {
65
+ context: browsingContextId,
66
+ },
67
+ },
68
+ }
69
+
70
+ if (sandbox != null) {
71
+ params.params.target['sandbox'] = sandbox
72
+ }
73
+
74
+ await this.bidi.send(params)
75
+ }
76
+
77
+ async callFunctionInRealm(
78
+ realmId,
79
+ functionDeclaration,
80
+ awaitPromise,
81
+ argumentValueList = null,
82
+ thisParameter = null,
83
+ resultOwnership = null
84
+ ) {
85
+ const params = this.getCallFunctionParams(
86
+ 'realm',
87
+ realmId,
88
+ null,
89
+ functionDeclaration,
90
+ awaitPromise,
91
+ argumentValueList,
92
+ thisParameter,
93
+ resultOwnership
94
+ )
95
+
96
+ const command = {
97
+ method: 'script.callFunction',
98
+ params,
99
+ }
100
+
101
+ let response = await this.bidi.send(command)
102
+ return this.createEvaluateResult(response)
103
+ }
104
+
105
+ async callFunctionInBrowsingContext(
106
+ browsingContextId,
107
+ functionDeclaration,
108
+ awaitPromise,
109
+ argumentValueList = null,
110
+ thisParameter = null,
111
+ resultOwnership = null,
112
+ sandbox = null
113
+ ) {
114
+ const params = this.getCallFunctionParams(
115
+ 'contextTarget',
116
+ browsingContextId,
117
+ sandbox,
118
+ functionDeclaration,
119
+ awaitPromise,
120
+ argumentValueList,
121
+ thisParameter,
122
+ resultOwnership
123
+ )
124
+
125
+ const command = {
126
+ method: 'script.callFunction',
127
+ params,
128
+ }
129
+ const response = await this.bidi.send(command)
130
+ return this.createEvaluateResult(response)
131
+ }
132
+
133
+ async evaluateFunctionInRealm(
134
+ realmId,
135
+ expression,
136
+ awaitPromise,
137
+ resultOwnership = null
138
+ ) {
139
+ const params = this.getEvaluateParams(
140
+ 'realm',
141
+ realmId,
142
+ null,
143
+ expression,
144
+ awaitPromise,
145
+ resultOwnership
146
+ )
147
+
148
+ const command = {
149
+ method: 'script.evaluate',
150
+ params,
151
+ }
152
+
153
+ let response = await this.bidi.send(command)
154
+ return this.createEvaluateResult(response)
155
+ }
156
+
157
+ async evaluateFunctionInBrowsingContext(
158
+ browsingContextId,
159
+ expression,
160
+ awaitPromise,
161
+ resultOwnership = null,
162
+ sandbox = null
163
+ ) {
164
+ const params = this.getEvaluateParams(
165
+ 'contextTarget',
166
+ browsingContextId,
167
+ sandbox,
168
+ expression,
169
+ awaitPromise,
170
+ resultOwnership
171
+ )
172
+
173
+ const command = {
174
+ method: 'script.evaluate',
175
+ params,
176
+ }
177
+
178
+ let response = await this.bidi.send(command)
179
+ return this.createEvaluateResult(response)
180
+ }
181
+
182
+ getCallFunctionParams(
183
+ targetType,
184
+ id,
185
+ sandbox,
186
+ functionDeclaration,
187
+ awaitPromise,
188
+ argumentValueList = null,
189
+ thisParameter = null,
190
+ resultOwnership = null
191
+ ) {
192
+ const params = {
193
+ functionDeclaration: functionDeclaration,
194
+ awaitPromise: awaitPromise,
195
+ }
196
+ if (targetType === 'contextTarget') {
197
+ if (sandbox != null) {
198
+ params['target'] = { context: id, sandbox: sandbox }
199
+ } else {
200
+ params['target'] = { context: id }
201
+ }
202
+ } else {
203
+ params['target'] = { realm: id }
204
+ }
205
+
206
+ if (argumentValueList != null) {
207
+ let argumentParams = []
208
+ argumentValueList.forEach((argumentValue) => {
209
+ argumentParams.push(argumentValue.asMap())
210
+ })
211
+ params['arguments'] = argumentParams
212
+ }
213
+
214
+ if (thisParameter != null) {
215
+ params['this'] = thisParameter
216
+ }
217
+
218
+ if (resultOwnership != null) {
219
+ params['resultOwnership'] = resultOwnership
220
+ }
221
+
222
+ return params
223
+ }
224
+
225
+ getEvaluateParams(
226
+ targetType,
227
+ id,
228
+ sandbox,
229
+ expression,
230
+ awaitPromise,
231
+ resultOwnership = null
232
+ ) {
233
+ const params = {
234
+ expression: expression,
235
+ awaitPromise: awaitPromise,
236
+ }
237
+ if (targetType === 'contextTarget') {
238
+ if (sandbox != null) {
239
+ params['target'] = { context: id, sandbox: sandbox }
240
+ } else {
241
+ params['target'] = { context: id }
242
+ }
243
+ } else {
244
+ params['target'] = { realm: id }
245
+ }
246
+ if (resultOwnership != null) {
247
+ params['resultOwnership'] = resultOwnership
248
+ }
249
+
250
+ return params
251
+ }
252
+
253
+ createEvaluateResult(response) {
254
+ var type = response.result.type
255
+ var realmId = response.result.realm
256
+ var evaluateResult
257
+
258
+ if (type === EvaluateResultType.SUCCESS) {
259
+ var result = response.result.result
260
+ evaluateResult = new EvaluateResultSuccess(
261
+ realmId,
262
+ new RemoteValue(result)
263
+ )
264
+ } else {
265
+ var exceptionDetails = response.result.exceptionDetails
266
+ evaluateResult = new EvaluateResultException(
267
+ realmId,
268
+ new ExceptionDetails(exceptionDetails)
269
+ )
270
+ }
271
+ return evaluateResult
272
+ }
273
+
274
+ realmInfoMapper(realms) {
275
+ var realmsList = []
276
+ realms.forEach((realm) => {
277
+ realmsList.push(RealmInfo.fromJson(realm))
278
+ })
279
+ return realmsList
280
+ }
281
+
282
+ async getAllRealms() {
283
+ const command = {
284
+ method: 'script.getRealms',
285
+ params: {},
286
+ }
287
+ let response = await this.bidi.send(command)
288
+ return this.realmInfoMapper(response.result.realms)
289
+ }
290
+
291
+ async getRealmsByType(type) {
292
+ const command = {
293
+ method: 'script.getRealms',
294
+ params: { type: type },
295
+ }
296
+ let response = await this.bidi.send(command)
297
+ return this.realmInfoMapper(response.result.realms)
298
+ }
299
+
300
+ async getRealmsInBrowsingContext(browsingContext) {
301
+ const command = {
302
+ method: 'script.getRealms',
303
+ params: { context: browsingContext },
304
+ }
305
+ let response = await this.bidi.send(command)
306
+ return this.realmInfoMapper(response.result.realms)
307
+ }
308
+
309
+ async getRealmsInBrowsingContextByType(browsingContext, type) {
310
+ const command = {
311
+ method: 'script.getRealms',
312
+ params: { context: browsingContext, type: type },
313
+ }
314
+ let response = await this.bidi.send(command)
315
+ return this.realmInfoMapper(response.result.realms)
316
+ }
317
+ }
318
+
319
+ async function getScriptManagerInstance(browsingContextId, driver) {
320
+ let instance = new ScriptManager(driver)
321
+ await instance.init(browsingContextId)
322
+ return instance
323
+ }
324
+
325
+ module.exports = getScriptManagerInstance
Binary file
Binary file
Binary file
package/chrome.js CHANGED
@@ -130,7 +130,6 @@
130
130
  const io = require('./io')
131
131
  const { Browser } = require('./lib/capabilities')
132
132
  const chromium = require('./chromium')
133
- const { driverLocation } = require('./common/seleniumManager')
134
133
 
135
134
  /**
136
135
  * Name of the ChromeDriver executable.
@@ -158,28 +157,6 @@ class ServiceBuilder extends chromium.ServiceBuilder {
158
157
  */
159
158
  constructor(opt_exe) {
160
159
  let exe = opt_exe || locateSynchronously()
161
-
162
- if (!exe) {
163
- console.log(
164
- ` The ChromeDriver could not be found on the current PATH, trying Selenium Manager`
165
- )
166
-
167
- try {
168
- exe = driverLocation(Browser.CHROME)
169
- } catch (err) {
170
- console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
171
- }
172
- }
173
-
174
- if (!exe) {
175
- throw Error(
176
- `The ChromeDriver could not be found on the current PATH.
177
- Please download the latest version of the ChromeDriver
178
- from http://chromedriver.storage.googleapis.com/index.html
179
- and ensure it can be found on your PATH.`
180
- )
181
- }
182
-
183
160
  super(exe)
184
161
  }
185
162
  }
package/chromium.js CHANGED
@@ -83,6 +83,7 @@ const error = require('./lib/error')
83
83
  const Symbols = require('./lib/symbols')
84
84
  const webdriver = require('./lib/webdriver')
85
85
  const remote = require('./remote')
86
+ const { getPath } = require('./common/driverFinder')
86
87
 
87
88
  /**
88
89
  * Custom command names supported by Chromium WebDriver.
@@ -684,6 +685,9 @@ class Driver extends webdriver.WebDriver {
684
685
  configureExecutor(executor, this.VENDOR_COMMAND_PREFIX)
685
686
  } else {
686
687
  let service = opt_serviceExecutor || this.getDefaultService()
688
+ if (!service.getExecutable()) {
689
+ service.setExecutable(getPath(service, caps))
690
+ }
687
691
  onQuit = () => service.kill()
688
692
  executor = createExecutor(service.start(), this.VENDOR_COMMAND_PREFIX)
689
693
  }
@@ -0,0 +1,59 @@
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
+ /**
19
+ * This implementation is still in beta, and may change.
20
+ *
21
+ * Utility to find if a given file is present and executable.
22
+ */
23
+
24
+ const { driverLocation } = require('./seleniumManager')
25
+ const fs = require('fs')
26
+
27
+ /**
28
+ * Determines the path of the correct Selenium Manager binary
29
+ * @returns {string}
30
+ */
31
+ function getPath(service, capabilities) {
32
+ try {
33
+ return pathExists(service.getExecutable()) || driverLocation(capabilities)
34
+ } catch (e) {
35
+ throw Error(
36
+ `Unable to obtain browser driver.
37
+ For more information on how to install drivers see
38
+ https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/. ${e}`
39
+ )
40
+ }
41
+ }
42
+
43
+ /**
44
+ * _Synchronously_ attempts to locate the driver executable on the current
45
+ * system.
46
+ *
47
+ * @param {!string} driverPath
48
+ *
49
+ * @return {?string} the located executable, or `null`.
50
+ */
51
+ function pathExists(driverPath) {
52
+ if (!driverPath || !fs.existsSync(driverPath)) {
53
+ return null
54
+ }
55
+ return driverPath
56
+ }
57
+
58
+ // PUBLIC API
59
+ module.exports = { getPath }
@@ -24,13 +24,13 @@
24
24
  const { platform } = require('process')
25
25
  const path = require('path')
26
26
  const fs = require('fs')
27
- const execSync = require('child_process').execSync
27
+ const spawnSync = require('child_process').spawnSync
28
28
 
29
29
  /**
30
30
  * currently supported browsers for selenium-manager
31
31
  * @type {string[]}
32
32
  */
33
- const Browser = ['chrome', 'firefox', 'edge', 'iexplorer']
33
+ const Browser = ['chrome', 'firefox', 'edge', 'MicrosoftEdge', 'iexplorer']
34
34
 
35
35
  /**
36
36
  * Determines the path of the correct Selenium Manager binary
@@ -47,7 +47,14 @@ function getBinary() {
47
47
  const file =
48
48
  directory === 'windows' ? 'selenium-manager.exe' : 'selenium-manager'
49
49
 
50
- const filePath = path.join(__dirname, '..', '/bin', directory, file)
50
+ let seleniumManagerBasePath
51
+ if (process.env.SELENIUM_MANAGER_BASE_PATH) {
52
+ seleniumManagerBasePath = process.env.SELENIUM_MANAGER_BASE_PATH
53
+ } else {
54
+ seleniumManagerBasePath = path.join(__dirname, '..', '/bin')
55
+ }
56
+
57
+ const filePath = path.join(seleniumManagerBasePath, directory, file)
51
58
 
52
59
  if (!fs.existsSync(filePath)) {
53
60
  throw new Error(`Unable to obtain Selenium Manager`)
@@ -58,33 +65,61 @@ function getBinary() {
58
65
 
59
66
  /**
60
67
  * Determines the path of the correct driver
61
- * @param {Browser|string} browser name to fetch the driver
68
+ * @param {Capabilities} options browser options to fetch the driver
62
69
  * @returns {string} path of the driver location
63
70
  */
64
71
 
65
- function driverLocation(browser) {
66
- if (!Browser.includes(browser.toLocaleString())) {
72
+ function driverLocation(options) {
73
+ if (!Browser.includes(options.getBrowserName().toLocaleString())) {
67
74
  throw new Error(
68
- `Unable to locate driver associated with browser name: ${browser}`
75
+ `Unable to locate driver associated with browser name: ${options.getBrowserName()}`
69
76
  )
70
77
  }
71
78
 
72
- let args = [getBinary(), '--browser', browser]
73
- let result
79
+ let args = ['--browser', options.getBrowserName(), '--output', 'json']
80
+
81
+ if (options.getBrowserVersion() && options.getBrowserVersion() !== "") {
82
+ args.push("--browser-version", options.getBrowserVersion())
83
+ }
84
+
85
+ const vendorOptions = options.get('goog:chromeOptions') || options.get('ms:edgeOptions')
86
+ || options.get('moz:firefoxOptions')
87
+ if (vendorOptions && vendorOptions.binary && vendorOptions.binary !== "") {
88
+ args.push("--browser-path", '"' + vendorOptions.binary + '"')
89
+ }
74
90
 
91
+ const smBinary = getBinary()
92
+ const spawnResult = spawnSync(smBinary, args)
93
+ let output
94
+ if (spawnResult.status) {
95
+ let errorMessage
96
+ if (spawnResult.stderr.toString()) {
97
+ errorMessage = spawnResult.stderr.toString()
98
+ }
99
+ if (spawnResult.stdout.toString()) {
100
+ try {
101
+ output = JSON.parse(spawnResult.stdout.toString())
102
+ errorMessage = output.result.message
103
+ } catch (e) {
104
+ errorMessage = e.toString()
105
+ }
106
+ }
107
+ throw new Error(`Error executing command for ${smBinary} with ${args}: ${errorMessage}`)
108
+ }
75
109
  try {
76
- result = execSync(args.join(' ')).toString()
110
+ output = JSON.parse(spawnResult.stdout.toString())
77
111
  } catch (e) {
78
- throw new Error(
79
- `Error executing command with ${args}\n${e.stdout.toString()}${e.stderr.toString()}`
80
- )
112
+ throw new Error(`Error executing command for ${smBinary} with ${args}: ${e.toString()}`)
81
113
  }
82
114
 
83
- if (!result.startsWith('INFO\t')) {
84
- throw new Error(`Unsuccessful command executed: ${args}\n${result}`)
115
+
116
+ for (const key in output.logs) {
117
+ if (output.logs[key].level === 'WARN') {
118
+ console.warn(`${output.logs[key].message}`)
119
+ }
85
120
  }
86
121
 
87
- return result.replace('INFO\t', '').trim()
122
+ return output.result.message
88
123
  }
89
124
 
90
125
  // PUBLIC API
package/edge.js CHANGED
@@ -80,7 +80,6 @@
80
80
  const { Browser } = require('./lib/capabilities')
81
81
  const io = require('./io')
82
82
  const chromium = require('./chromium')
83
- const { driverLocation } = require('./common/seleniumManager')
84
83
 
85
84
  /**
86
85
  * Name of the EdgeDriver executable.
@@ -107,28 +106,6 @@ class ServiceBuilder extends chromium.ServiceBuilder {
107
106
  */
108
107
  constructor(opt_exe) {
109
108
  let exe = opt_exe || locateSynchronously()
110
-
111
- if (!exe) {
112
- console.log(
113
- `The WebDriver for Edge could not be found on the current PATH, trying Selenium Manager`
114
- )
115
-
116
- try {
117
- exe = driverLocation('edge')
118
- } catch (err) {
119
- console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
120
- }
121
- }
122
-
123
- if (!exe) {
124
- throw Error(
125
- `The WebDriver for Edge could not be found on the current PATH. Please download the ` +
126
- `latest version of ${EDGEDRIVER_CHROMIUM_EXE} from ` +
127
- `https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ ` +
128
- `and ensure it can be found on your PATH.`
129
- )
130
- }
131
-
132
109
  super(exe)
133
110
  this.setLoopback(true)
134
111
  }
package/firefox.js CHANGED
@@ -127,7 +127,7 @@ const webdriver = require('./lib/webdriver')
127
127
  const zip = require('./io/zip')
128
128
  const { Browser, Capabilities } = require('./lib/capabilities')
129
129
  const { Zip } = require('./io/zip')
130
- const { driverLocation } = require('./common/seleniumManager')
130
+ const { getPath } = require('./common/driverFinder')
131
131
 
132
132
  /**
133
133
  * Thrown when there an add-on is malformed.
@@ -471,29 +471,7 @@ function locateSynchronously() {
471
471
  * @throws {Error}
472
472
  */
473
473
  function findGeckoDriver() {
474
- let exe = locateSynchronously()
475
-
476
- if (!exe) {
477
- console.log(
478
- `The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH, trying Selenium Manager`
479
- )
480
-
481
- try {
482
- exe = driverLocation(Browser.FIREFOX)
483
- } catch (err) {
484
- console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
485
- }
486
- }
487
-
488
- if (!exe) {
489
- throw Error(
490
- `The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH.
491
- Please download the latest version from https://github.com/mozilla/geckodriver/releases/
492
- and ensure it can be found on your PATH.`
493
- )
494
- }
495
-
496
- return exe
474
+ return locateSynchronously()
497
475
  }
498
476
 
499
477
  /**
@@ -630,10 +608,16 @@ class Driver extends webdriver.WebDriver {
630
608
  executor = opt_executor
631
609
  configureExecutor(executor)
632
610
  } else if (opt_executor instanceof remote.DriverService) {
611
+ if (!opt_executor.getExecutable()) {
612
+ opt_executor.setExecutable(getPath(opt_executor, opt_config))
613
+ }
633
614
  executor = createExecutor(opt_executor.start())
634
615
  onQuit = () => opt_executor.kill()
635
616
  } else {
636
617
  let service = new ServiceBuilder().build()
618
+ if (!service.getExecutable()) {
619
+ service.setExecutable(getPath(service, opt_config))
620
+ }
637
621
  executor = createExecutor(service.start())
638
622
  onQuit = () => service.kill()
639
623
  }
package/http/index.js CHANGED
@@ -54,7 +54,8 @@ function getRequestOptions(aUrl) {
54
54
  options.search = null
55
55
  options.hash = null
56
56
  options.path = options.pathname
57
- options.hostname = options.hostname === 'localhost' ? '127.0.0.1' : options.hostname // To support Node 17 and above. Refer https://github.com/nodejs/node/issues/40702 for details.
57
+ options.hostname =
58
+ options.hostname === 'localhost' ? '127.0.0.1' : options.hostname // To support Node 17 and above. Refer https://github.com/nodejs/node/issues/40702 for details.
58
59
  return options
59
60
  }
60
61