selenium-webdriver 4.16.0 → 4.17.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 CHANGED
@@ -1,3 +1,20 @@
1
+ ## 4.17.0
2
+
3
+ * Add javascript to Selenium Manager input for tracking (see #13288)
4
+ * remove deprecated headless methods and associated references
5
+ * Add script message event (#13153)
6
+ * Update channel name from Aurora to Dev
7
+ * Remove firefox_channels.js example as Channels is deprecated
8
+ * Add Input module command (#13360)
9
+ * remove all references to firefox-bin
10
+ * download files from remote server (#13102)
11
+ * Add auth required event
12
+ * Add traverse history command
13
+ * Add test to get iframe's browsing context
14
+ * Add Input module JS command
15
+ * Add test for node properties in
16
+ * Add CDP for Chrome 121 and remove 118
17
+
1
18
  ## 4.16.0
2
19
 
3
20
  #### :bug: Bug fix
package/LICENSE CHANGED
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright 2023 Software Freedom Conservancy (SFC)
190
+ Copyright 2024 Software Freedom Conservancy (SFC)
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
package/NOTICE CHANGED
@@ -1,2 +1,2 @@
1
- Copyright 2011-2023 Software Freedom Conservancy
1
+ Copyright 2011-2024 Software Freedom Conservancy
2
2
  Copyright 2004-2011 Selenium committers
@@ -186,7 +186,7 @@ class BrowsingContext {
186
186
  params: {
187
187
  context: this._id,
188
188
  clip: {
189
- type: 'viewport',
189
+ type: 'box',
190
190
  x: x,
191
191
  y: y,
192
192
  width: width,
@@ -195,7 +195,10 @@ class BrowsingContext {
195
195
  },
196
196
  }
197
197
 
198
+ console.log(JSON.stringify(params))
199
+
198
200
  const response = await this.bidi.send(params)
201
+ console.log(JSON.stringify(response))
199
202
  this.checkErrorInScreenshot(response)
200
203
  return response['result']['data']
201
204
  }
@@ -309,6 +312,25 @@ class BrowsingContext {
309
312
  throw Error(result['error'])
310
313
  }
311
314
  }
315
+
316
+ async traverseHistory(delta) {
317
+ const params = {
318
+ method: 'browsingContext.traverseHistory',
319
+ params: {
320
+ context: this._id,
321
+ delta: delta,
322
+ },
323
+ }
324
+ await this.bidi.send(params)
325
+ }
326
+
327
+ async forward() {
328
+ await this.traverseHistory(1)
329
+ }
330
+
331
+ async back() {
332
+ await this.traverseHistory(-1)
333
+ }
312
334
  }
313
335
 
314
336
  class NavigateResult {
package/bidi/input.js ADDED
@@ -0,0 +1,100 @@
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
+ // type: module added to package.json
19
+ // import { WebElement } from '../lib/webdriver'
20
+ const { WebElement } = require('../lib/webdriver')
21
+
22
+ class Input {
23
+ constructor(driver) {
24
+ this._driver = driver
25
+ }
26
+
27
+ async init() {
28
+ if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
29
+ throw Error('WebDriver instance must support BiDi protocol')
30
+ }
31
+
32
+ this.bidi = await this._driver.getBidi()
33
+ }
34
+
35
+ async perform(browsingContextId, actions) {
36
+ const _actions = await updateActions(actions)
37
+
38
+ const command = {
39
+ method: 'input.performActions',
40
+ params: {
41
+ context: browsingContextId,
42
+ actions: _actions,
43
+ },
44
+ }
45
+
46
+ let response = await this.bidi.send(command)
47
+
48
+ return response
49
+ }
50
+
51
+ async release(browsingContextId) {
52
+ const command = {
53
+ method: 'input.releaseActions',
54
+ params: {
55
+ context: browsingContextId,
56
+ },
57
+ }
58
+ return await this.bidi.send(command)
59
+ }
60
+ }
61
+
62
+ async function updateActions(actions) {
63
+ const _actions = []
64
+ for (const action of actions) {
65
+ const sequenceList = action.actions
66
+ let updatedSequenceList = []
67
+
68
+ if (action.type === 'pointer' || action.type === 'wheel') {
69
+ for (const sequence of sequenceList) {
70
+ if (
71
+ (sequence.type === 'pointerMove' || sequence.type === 'scroll') &&
72
+ sequence.origin instanceof WebElement
73
+ ) {
74
+ const element = sequence.origin
75
+ const elementId = await element.getId()
76
+ sequence.origin = {
77
+ type: 'element',
78
+ element: { sharedId: elementId },
79
+ }
80
+ }
81
+ updatedSequenceList.push(sequence)
82
+ }
83
+
84
+ const updatedAction = { ...action, actions: updatedSequenceList }
85
+ _actions.push(updatedAction)
86
+ } else {
87
+ _actions.push(action)
88
+ }
89
+ }
90
+
91
+ return _actions
92
+ }
93
+
94
+ async function getInputInstance(driver) {
95
+ let instance = new Input(driver)
96
+ await instance.init()
97
+ return instance
98
+ }
99
+
100
+ module.exports = getInputInstance
@@ -39,6 +39,10 @@ class NetworkInspector {
39
39
  await this.subscribeAndHandleEvent('network.responseCompleted', callback)
40
40
  }
41
41
 
42
+ async authRequired(callback) {
43
+ await this.subscribeAndHandleEvent('network.authRequired', callback)
44
+ }
45
+
42
46
  async subscribeAndHandleEvent(eventType, callback) {
43
47
  if (this._browsingContextIds != null) {
44
48
  await this.bidi.subscribe(eventType, this._browsingContextIds)
@@ -42,6 +42,7 @@ const NonPrimitiveType = {
42
42
  OBJECT: 'object',
43
43
  REGULAR_EXPRESSION: 'regexp',
44
44
  SET: 'set',
45
+ CHANNEL: 'channel',
45
46
 
46
47
  findByName(name) {
47
48
  return (
@@ -25,7 +25,7 @@ const TYPE_CONSTANT = 'type'
25
25
  const VALUE_CONSTANT = 'value'
26
26
  const RemoteReferenceType = {
27
27
  HANDLE: 'handle',
28
- SHARED_ID: 'shareId',
28
+ SHARED_ID: 'sharedId',
29
29
  }
30
30
 
31
31
  class LocalValue {
@@ -98,6 +98,10 @@ class LocalValue {
98
98
  return new LocalValue(NonPrimitiveType.SET, value)
99
99
  }
100
100
 
101
+ static createChannelValue(value) {
102
+ return new LocalValue(NonPrimitiveType.CHANNEL, value)
103
+ }
104
+
101
105
  toJson() {
102
106
  let toReturn = {}
103
107
  toReturn[TYPE_CONSTANT] = this.type
@@ -165,12 +169,12 @@ class RemoteValue {
165
169
  }
166
170
 
167
171
  class ReferenceValue {
168
- constructor(handle, shareId) {
172
+ constructor(handle, sharedId) {
169
173
  if (handle === RemoteReferenceType.HANDLE) {
170
- this.handle = shareId
174
+ this.handle = sharedId
171
175
  } else {
172
176
  this.handle = handle
173
- this.shareId = shareId
177
+ this.sharedId = sharedId
174
178
  }
175
179
  }
176
180
 
@@ -180,8 +184,8 @@ class ReferenceValue {
180
184
  toReturn[RemoteReferenceType.HANDLE] = this.handle
181
185
  }
182
186
 
183
- if (this.shareId != null) {
184
- toReturn[RemoteReferenceType.SHARED_ID] = this.shareId
187
+ if (this.sharedId != null) {
188
+ toReturn[RemoteReferenceType.SHARED_ID] = this.sharedId
185
189
  }
186
190
 
187
191
  return toReturn
@@ -195,10 +199,56 @@ class RegExpValue {
195
199
  }
196
200
  }
197
201
 
202
+ class SerializationOptions {
203
+ constructor(
204
+ maxDomDepth = 0,
205
+ maxObjectDepth = null,
206
+ includeShadowTree = 'none'
207
+ ) {
208
+ this._maxDomDepth = maxDomDepth
209
+ this._maxObjectDepth = maxObjectDepth
210
+
211
+ if (['none', 'open', 'all'].includes(includeShadowTree)) {
212
+ throw Error(
213
+ `Valid types are 'none', 'open', and 'all'. Received: ${includeShadowTree}`
214
+ )
215
+ }
216
+ this._includeShadowTree = includeShadowTree
217
+ }
218
+ }
219
+
220
+ class ChannelValue {
221
+ constructor(channel, options = undefined, resultOwnership = undefined) {
222
+ this.channel = channel
223
+
224
+ if (options !== undefined) {
225
+ if (options instanceof SerializationOptions) {
226
+ this.options = options
227
+ } else {
228
+ throw Error(
229
+ `Pass in SerializationOptions object. Received: ${options} `
230
+ )
231
+ }
232
+ }
233
+
234
+ if (resultOwnership != undefined) {
235
+ if (['root', 'none'].includes(resultOwnership)) {
236
+ this.resultOwnership = resultOwnership
237
+ } else {
238
+ throw Error(
239
+ `Valid types are 'root' and 'none. Received: ${resultOwnership}`
240
+ )
241
+ }
242
+ }
243
+ }
244
+ }
245
+
198
246
  module.exports = {
247
+ ChannelValue,
199
248
  LocalValue,
200
249
  RemoteValue,
201
250
  ReferenceValue,
202
251
  RemoteReferenceType,
203
252
  RegExpValue,
253
+ SerializationOptions,
204
254
  }
package/bidi/realmInfo.js CHANGED
@@ -96,4 +96,5 @@ class WindowRealmInfo extends RealmInfo {
96
96
  module.exports = {
97
97
  RealmInfo,
98
98
  RealmType,
99
+ WindowRealmInfo,
99
100
  }
@@ -21,8 +21,10 @@ const {
21
21
  EvaluateResultException,
22
22
  ExceptionDetails,
23
23
  } = require('./evaluateResult')
24
- const { RealmInfo } = require('./realmInfo')
24
+ const { Message } = require('./scriptTypes')
25
+ const { RealmInfo, RealmType, WindowRealmInfo } = require('./realmInfo')
25
26
  const { RemoteValue } = require('./protocolValue')
27
+ const { Source } = require('./scriptTypes')
26
28
  const { WebDriverError } = require('../lib/error')
27
29
 
28
30
  class ScriptManager {
@@ -348,6 +350,55 @@ class ScriptManager {
348
350
  let response = await this.bidi.send(command)
349
351
  return this.realmInfoMapper(response.result.realms)
350
352
  }
353
+
354
+ async onMessage(callback) {
355
+ await this.subscribeAndHandleEvent('script.message', callback)
356
+ }
357
+
358
+ async onRealmCreated(callback) {
359
+ await this.subscribeAndHandleEvent('script.realmCreated', callback)
360
+ }
361
+
362
+ async subscribeAndHandleEvent(eventType, callback) {
363
+ if (this._browsingContextIds != null) {
364
+ await this.bidi.subscribe(eventType, this._browsingContextIds)
365
+ } else {
366
+ await this.bidi.subscribe(eventType)
367
+ }
368
+ await this._on(callback)
369
+ }
370
+
371
+ async _on(callback) {
372
+ this.ws = await this.bidi.socket
373
+ this.ws.on('message', (event) => {
374
+ const { params } = JSON.parse(Buffer.from(event.toString()))
375
+ if (params) {
376
+ let response = null
377
+ if ('channel' in params) {
378
+ response = new Message(
379
+ params.channel,
380
+ new RemoteValue(params.data),
381
+ new Source(params.source)
382
+ )
383
+ } else if ('realm' in params) {
384
+ if (params.type === RealmType.WINDOW) {
385
+ response = new WindowRealmInfo(
386
+ params.realm,
387
+ params.origin,
388
+ params.type,
389
+ params.context,
390
+ params.sandbox
391
+ )
392
+ } else if (params.realm !== null && params.type !== null) {
393
+ response = new RealmInfo(params.realm, params.origin, params.type)
394
+ } else if (params.realm !== null) {
395
+ response = params.realm
396
+ }
397
+ }
398
+ callback(response)
399
+ }
400
+ })
401
+ }
351
402
  }
352
403
 
353
404
  async function getScriptManagerInstance(browsingContextId, driver) {
@@ -0,0 +1,58 @@
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
+ class Message {
19
+ constructor(channel, data, source) {
20
+ this._channel = channel
21
+ this._data = data
22
+ this._source = source
23
+ }
24
+
25
+ get channel() {
26
+ return this._channel
27
+ }
28
+
29
+ get data() {
30
+ return this._data
31
+ }
32
+
33
+ get source() {
34
+ return this._source
35
+ }
36
+ }
37
+
38
+ class Source {
39
+ constructor(source) {
40
+ this._browsingContextId = null
41
+ this._realmId = source.realm
42
+
43
+ // Browsing context is returned as an optional parameter
44
+ if ('context' in source) {
45
+ this._browsingContextId = source.context
46
+ }
47
+ }
48
+
49
+ get browsingContextId() {
50
+ return this._browsingContextId
51
+ }
52
+
53
+ get realmId() {
54
+ return this._realmId
55
+ }
56
+ }
57
+
58
+ module.exports = { Message, Source }
Binary file
Binary file
Binary file
package/chrome.js CHANGED
@@ -36,17 +36,12 @@
36
36
  * a unique browser session with a clean user profile (unless otherwise
37
37
  * configured through the {@link Options} class).
38
38
  *
39
- * __Headless Chrome__ <a id="headless"></a>
40
- *
41
- * To start Chrome in headless mode, simply call
42
- * {@linkplain Options#headless Options.headless()}.
43
- *
44
39
  * let chrome = require('selenium-webdriver/chrome');
45
40
  * let {Builder} = require('selenium-webdriver');
46
41
  *
47
42
  * let driver = new Builder()
48
43
  * .forBrowser('chrome')
49
- * .setChromeOptions(new chrome.Options().headless())
44
+ * .setChromeOptions(new chrome.Options())
50
45
  * .build();
51
46
  *
52
47
  * __Customizing the ChromeDriver Server__ <a id="custom-server"></a>
package/chromium.js CHANGED
@@ -36,17 +36,12 @@
36
36
  * a unique browser session with a clean user profile (unless otherwise
37
37
  * configured through the {@link Options} class).
38
38
  *
39
- * __Headless Chromium__ <a id="headless"></a>
40
- *
41
- * To start the browser in headless mode, simply call
42
- * {@linkplain Options#headless Options.headless()}.
43
- *
44
39
  * let chrome = require('selenium-webdriver/chrome');
45
40
  * let {Builder} = require('selenium-webdriver');
46
41
  *
47
42
  * let driver = new Builder()
48
43
  * .forBrowser('chrome')
49
- * .setChromeOptions(new chrome.Options().headless())
44
+ * .setChromeOptions(new chrome.Options())
50
45
  * .build();
51
46
  *
52
47
  * __Customizing the Chromium WebDriver Server__ <a id="custom-server"></a>
@@ -310,34 +305,6 @@ class Options extends Capabilities {
310
305
  return this
311
306
  }
312
307
 
313
- /**
314
- * @deprecated Use {@link Options#addArguments} instead.
315
- * @example
316
- * options.addArguments('--headless=chrome'); (or)
317
- * options.addArguments('--headless');
318
- * @example
319
- *
320
- * Recommended to use '--headless=chrome' as argument for browsers v94-108.
321
- * Recommended to use '--headless=new' as argument for browsers v109+.
322
- *
323
- * Configures the driver to start the browser in headless mode.
324
- *
325
- * > __NOTE:__ Resizing the browser window in headless mode is only supported
326
- * > in Chromium 60+. Users are encouraged to set an initial window size with
327
- * > the {@link #windowSize windowSize({width, height})} option.
328
- *
329
- * > __NOTE__: For security, Chromium disables downloads by default when
330
- * > in headless mode (to prevent sites from silently downloading files to
331
- * > your machine). After creating a session, you may call
332
- * > {@link ./chrome.Driver#setDownloadPath setDownloadPath} to re-enable
333
- * > downloads, saving files in the specified directory.
334
- *
335
- * @return {!Options} A self reference.
336
- */
337
- headless() {
338
- return this.addArguments('headless')
339
- }
340
-
341
308
  /**
342
309
  * Sets the initial window size.
343
310
  *
@@ -35,7 +35,7 @@ let debugMessagePrinted = false
35
35
  * Determines the path of the correct Selenium Manager binary
36
36
  * @returns {string}
37
37
  */
38
- function getBinary() {
38
+ function getBinary () {
39
39
  const directory = {
40
40
  darwin: 'macos',
41
41
  win32: 'windows',
@@ -48,7 +48,8 @@ function getBinary() {
48
48
 
49
49
  let seleniumManagerBasePath = path.join(__dirname, '..', '/bin')
50
50
 
51
- const filePath = process.env.SE_MANAGER_PATH || path.join(seleniumManagerBasePath, directory, file)
51
+ const filePath = process.env.SE_MANAGER_PATH || path.join(
52
+ seleniumManagerBasePath, directory, file)
52
53
 
53
54
  if (!fs.existsSync(filePath)) {
54
55
  throw new Error(`Unable to obtain Selenium Manager at ${filePath}`)
@@ -69,8 +70,9 @@ function getBinary() {
69
70
  * browser location
70
71
  */
71
72
 
72
- function driverLocation(options) {
73
- let args = ['--browser', options.getBrowserName(), '--output', 'json']
73
+ function driverLocation (options) {
74
+ let args = ['--browser', options.getBrowserName(), '--language-binding',
75
+ 'javascript', '--output', 'json']
74
76
 
75
77
  if (options.getBrowserVersion() && options.getBrowserVersion() !== '') {
76
78
  args.push('--browser-version', options.getBrowserVersion())
@@ -139,7 +141,7 @@ function driverLocation(options) {
139
141
  }
140
142
  }
141
143
 
142
- function logOutput(output) {
144
+ function logOutput (output) {
143
145
  for (const key in output.logs) {
144
146
  if (output.logs[key].level === 'WARN') {
145
147
  log_.warning(`${output.logs[key].message}`)
@@ -41,10 +41,10 @@ const height = 480
41
41
  let driver = new Builder()
42
42
  .forBrowser('chrome')
43
43
  .setChromeOptions(
44
- new chrome.Options().headless().windowSize({ width, height })
44
+ new chrome.Options().addArguments('-headless').windowSize({ width, height })
45
45
  )
46
46
  .setFirefoxOptions(
47
- new firefox.Options().headless().windowSize({ width, height })
47
+ new firefox.Options().addArguments('-headless').windowSize({ width, height })
48
48
  )
49
49
  .build()
50
50