selenium-webdriver 4.0.0-beta.1 → 4.0.0-rc-1

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/lib/webdriver.js CHANGED
@@ -35,6 +35,8 @@ const WebSocket = require('ws')
35
35
  const http = require('../http/index')
36
36
  const fs = require('fs')
37
37
  const { Capabilities } = require('./capabilities')
38
+ const path = require('path')
39
+ const { NoSuchElementError } = require('./error')
38
40
 
39
41
  // Capability names that are defined in the W3C spec.
40
42
  const W3C_CAPABILITY_NAMES = new Set([
@@ -970,16 +972,46 @@ class WebDriver {
970
972
  /** @override */
971
973
  findElement(locator) {
972
974
  let id
973
- locator = by.checkedLocator(locator)
975
+ let cmd = null
976
+
977
+ if (locator instanceof RelativeBy) {
978
+ cmd = new command.Command(
979
+ command.Name.FIND_ELEMENTS_RELATIVE
980
+ ).setParameter('args', locator.marshall())
981
+ } else {
982
+ locator = by.checkedLocator(locator)
983
+ }
984
+
974
985
  if (typeof locator === 'function') {
975
986
  id = this.findElementInternal_(locator, this)
976
- } else {
977
- let cmd = new command.Command(command.Name.FIND_ELEMENT)
987
+ return new WebElementPromise(this, id)
988
+ } else if (cmd === null) {
989
+ cmd = new command.Command(command.Name.FIND_ELEMENT)
978
990
  .setParameter('using', locator.using)
979
991
  .setParameter('value', locator.value)
980
- id = this.execute(cmd)
981
992
  }
982
- return new WebElementPromise(this, id)
993
+
994
+ id = this.execute(cmd)
995
+ if (locator instanceof RelativeBy) {
996
+ return this.normalize_(id)
997
+ } else {
998
+ return new WebElementPromise(this, id)
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * @param {!Function} webElementPromise The webElement in unresolved state
1004
+ * @return {!Promise<!WebElement>} First single WebElement from array of resolved promises
1005
+ */
1006
+ async normalize_(webElementPromise) {
1007
+ let result = await webElementPromise
1008
+ if (result.length === 0) {
1009
+ throw new NoSuchElementError(
1010
+ 'Cannot locate an element with provided parameters'
1011
+ )
1012
+ } else {
1013
+ return result[0]
1014
+ }
983
1015
  }
984
1016
 
985
1017
  /**
@@ -1159,12 +1191,13 @@ class WebDriver {
1159
1191
  */
1160
1192
  async createCDPConnection(target) {
1161
1193
  const caps = await this.getCapabilities()
1162
- const seOptions = caps['map_'].get('se:options') || new Map()
1194
+ const seCdp = caps['map_'].get('se:cdp')
1163
1195
  const vendorInfo =
1164
1196
  caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
1197
+ caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
1165
1198
  caps['map_'].get('moz:debuggerAddress') ||
1166
1199
  new Map()
1167
- const debuggerUrl = seOptions['cdp'] || vendorInfo['debuggerAddress'] || vendorInfo
1200
+ const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
1168
1201
  this._wsUrl = await this.getWsUrl(debuggerUrl, target)
1169
1202
 
1170
1203
  return new Promise((resolve, reject) => {
@@ -1196,12 +1229,21 @@ class WebDriver {
1196
1229
  if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
1197
1230
  throw new error.InvalidArgumentError('invalid target value')
1198
1231
  }
1199
- let path = '/json/version'
1200
-
1232
+ let path
1233
+ if (target === 'page') {
1234
+ path = '/json'
1235
+ } else {
1236
+ path = '/json/version'
1237
+ }
1201
1238
  let request = new http.Request('GET', path)
1202
1239
  let client = new http.HttpClient('http://' + debuggerAddress)
1203
1240
  let response = await client.send(request)
1204
- let url = JSON.parse(response.body)['webSocketDebuggerUrl']
1241
+ let url
1242
+ if (target.toLowerCase() === 'page') {
1243
+ url = JSON.parse(response.body)[0]['webSocketDebuggerUrl']
1244
+ } else {
1245
+ url = JSON.parse(response.body)['webSocketDebuggerUrl']
1246
+ }
1205
1247
 
1206
1248
  return url
1207
1249
  }
@@ -1362,7 +1404,10 @@ class WebDriver {
1362
1404
  .toString()
1363
1405
  } catch {
1364
1406
  mutationListener = fs
1365
- .readFileSync('./atoms/mutation-listener.js', 'utf-8')
1407
+ .readFileSync(
1408
+ path.resolve(__dirname, './atoms/mutation-listener.js'),
1409
+ 'utf-8'
1410
+ )
1366
1411
  .toString()
1367
1412
  }
1368
1413
 
@@ -2680,18 +2725,12 @@ class WebElement {
2680
2725
  * Take a screenshot of the visible region encompassed by this element's
2681
2726
  * bounding rectangle.
2682
2727
  *
2683
- * @param {boolean=} scroll Optional argument that indicates whether the
2684
- * element should be scrolled into view before taking a screenshot.
2685
- * Defaults to false.
2686
2728
  * @return {!Promise<string>} A promise that will be
2687
2729
  * resolved to the screenshot as a base-64 encoded PNG.
2688
2730
  */
2689
- takeScreenshot(scroll = false) {
2731
+ takeScreenshot() {
2690
2732
  return this.execute_(
2691
- new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT).setParameter(
2692
- 'scroll',
2693
- scroll
2694
- )
2733
+ new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
2695
2734
  )
2696
2735
  }
2697
2736
  }
package/opera.js CHANGED
@@ -383,9 +383,9 @@ class Driver extends webdriver.WebDriver {
383
383
  caps = options.toCapabilities(caps)
384
384
  }
385
385
 
386
- return /** @type {!Driver} */ (super.createSession(executor, caps, () =>
387
- service.kill()
388
- ))
386
+ return /** @type {!Driver} */ (
387
+ super.createSession(executor, caps, () => service.kill())
388
+ )
389
389
  }
390
390
 
391
391
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selenium-webdriver",
3
- "version": "4.0.0-beta.1",
3
+ "version": "4.0.0-rc-1",
4
4
  "description": "The official WebDriver JavaScript bindings from the Selenium project",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -23,24 +23,24 @@
23
23
  "node": ">= 10.15.0"
24
24
  },
25
25
  "dependencies": {
26
- "jszip": "^3.5.0",
27
- "rimraf": "^2.7.1",
26
+ "jszip": "^3.6.0",
27
+ "rimraf": "^3.0.2",
28
28
  "tmp": "^0.2.1",
29
- "ws": "^7.3.1"
29
+ "ws": ">=7.4.6"
30
30
  },
31
31
  "devDependencies": {
32
- "eslint": "^7.15.0",
33
- "eslint-config-prettier": "^7.0.0",
34
- "eslint-plugin-no-only-tests": "^2.4.0",
32
+ "eslint": "^7.26.0",
33
+ "eslint-config-prettier": "^8.3.0",
34
+ "eslint-plugin-no-only-tests": "^2.6.0",
35
35
  "eslint-plugin-node": "^11.1.0",
36
- "eslint-plugin-prettier": "^3.2.0",
36
+ "eslint-plugin-prettier": "^3.4.0",
37
37
  "express": "^4.17.1",
38
- "jasmine": "^3.6.1",
38
+ "jasmine": "^3.8.0",
39
39
  "mocha": "^8.1.0",
40
40
  "multer": "^1.4.2",
41
- "prettier": "^2.2.1",
41
+ "prettier": "^2.3.0",
42
42
  "serve-index": "^1.9.1",
43
- "sinon": "^7.5.0"
43
+ "sinon": "^10.0.0"
44
44
  },
45
45
  "scripts": {
46
46
  "lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --ext js lib/http.js \"**/*.js\"",
package/remote/index.js CHANGED
@@ -210,7 +210,7 @@ class DriverService {
210
210
  }
211
211
 
212
212
  return resolveCommandLineFlags(this.args_).then((args) => {
213
- var command = exec(self.executable_, {
213
+ const command = exec(self.executable_, {
214
214
  args: args,
215
215
  env: self.env_,
216
216
  stdio: self.stdio_,
@@ -218,8 +218,8 @@ class DriverService {
218
218
 
219
219
  resolveCommand(command)
220
220
 
221
- var earlyTermination = command.result().then(function (result) {
222
- var error =
221
+ const earlyTermination = command.result().then(function (result) {
222
+ const error =
223
223
  result.code == null
224
224
  ? Error('Server was killed with ' + result.signal)
225
225
  : Error('Server terminated early with status ' + result.code)
@@ -229,7 +229,7 @@ class DriverService {
229
229
  throw error
230
230
  })
231
231
 
232
- var hostname = self.hostname_
232
+ let hostname = self.hostname_
233
233
  if (!hostname) {
234
234
  hostname =
235
235
  (!self.loopbackOnly_ && net.getAddress()) ||
@@ -338,9 +338,8 @@ DriverService.Builder = class {
338
338
  * @this {THIS}
339
339
  * @template THIS
340
340
  */
341
- addArguments(var_args) { // eslint-disable-line
342
- let args = Array.prototype.slice.call(arguments, 0)
343
- this.options_.args = this.options_.args.concat(args)
341
+ addArguments(...arguments_) {
342
+ this.options_.args = this.options_.args.concat(arguments_)
344
343
  return this
345
344
  }
346
345
 
@@ -449,7 +448,7 @@ DriverService.Builder = class {
449
448
 
450
449
  /**
451
450
  * Manages the life and death of the
452
- * <a href="http://selenium-release.storage.googleapis.com/index.html">
451
+ * <a href="https://www.selenium.dev/downloads/">
453
452
  * standalone Selenium server</a>.
454
453
  */
455
454
  class SeleniumServer extends DriverService {
@@ -563,7 +562,7 @@ SeleniumServer.Options = class {
563
562
  /**
564
563
  * A {@link webdriver.FileDetector} that may be used when running
565
564
  * against a remote
566
- * [Selenium server](http://selenium-release.storage.googleapis.com/index.html).
565
+ * [Selenium server](https://www.selenium.dev/downloads/).
567
566
  *
568
567
  * When a file path on the local machine running this script is entered with
569
568
  * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
package/safari.js CHANGED
@@ -154,9 +154,9 @@ class Driver extends webdriver.WebDriver {
154
154
  service.start().then((url) => new http.HttpClient(url))
155
155
  )
156
156
 
157
- return /** @type {!Driver} */ (super.createSession(executor, caps, () =>
158
- service.kill()
159
- ))
157
+ return /** @type {!Driver} */ (
158
+ super.createSession(executor, caps, () => service.kill())
159
+ )
160
160
  }
161
161
  }
162
162
 
package/testing/index.js CHANGED
@@ -47,7 +47,7 @@ const { Builder } = require('../index')
47
47
  * Describes a browser targeted by a {@linkplain suite test suite}.
48
48
  * @record
49
49
  */
50
- function TargetBrowser() { }
50
+ function TargetBrowser() {}
51
51
 
52
52
  /**
53
53
  * The {@linkplain Browser name} of the targeted browser.
@@ -100,7 +100,7 @@ function getBrowsersToTestFromEnv() {
100
100
  const parts = spec.split(/:/, 3)
101
101
  let name = parts[0]
102
102
  if (name === 'ie') {
103
- name = Browser.IE
103
+ name = Browser.INTERNET_EXPLORER
104
104
  } else if (name === 'edge') {
105
105
  name = Browser.EDGE
106
106
  }
@@ -120,13 +120,8 @@ function getAvailableBrowsers() {
120
120
  let targets = [
121
121
  [chrome.locateSynchronously, Browser.CHROME],
122
122
  [edge.locateSynchronously, Browser.EDGE],
123
- [
124
- () => edge.locateSynchronously('msedge'),
125
- Browser.EDGE,
126
- { 'ms:edgeChromium': true },
127
- ],
128
123
  [firefox.locateSynchronously, Browser.FIREFOX],
129
- [ie.locateSynchronously, Browser.IE],
124
+ [ie.locateSynchronously, Browser.INTERNET_EXPLORER],
130
125
  [safari.locateSynchronously, Browser.SAFARI],
131
126
  [opera.locateSynchronously, Browser.OPERA],
132
127
  ]
@@ -215,7 +210,7 @@ function init(force = false) {
215
210
  if (seleniumJar && seleniumUrl) {
216
211
  throw Error(
217
212
  'Ambiguous test configuration: both SELENIUM_REMOTE_URL' +
218
- ' && SELENIUM_SERVER_JAR environment variables are set'
213
+ ' && SELENIUM_SERVER_JAR environment variables are set'
219
214
  )
220
215
  }
221
216
 
@@ -223,8 +218,8 @@ function init(force = false) {
223
218
  if ((seleniumJar || seleniumUrl) && envBrowsers.length === 0) {
224
219
  throw Error(
225
220
  'Ambiguous test configuration: when either the SELENIUM_REMOTE_URL or' +
226
- ' SELENIUM_SERVER_JAR environment variable is set, the' +
227
- ' SELENIUM_BROWSER variable must also be set.'
221
+ ' SELENIUM_SERVER_JAR environment variable is set, the' +
222
+ ' SELENIUM_BROWSER variable must also be set.'
228
223
  )
229
224
  }
230
225
 
@@ -241,7 +236,8 @@ function init(force = false) {
241
236
  }
242
237
 
243
238
  const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
244
- const URL_MAP = /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
239
+ const URL_MAP =
240
+ /** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
245
241
 
246
242
  /**
247
243
  * Defines the environment a {@linkplain suite test suite} is running against.
@@ -254,9 +250,9 @@ class Environment {
254
250
  * Selenium server to test against.
255
251
  */
256
252
  constructor(browser, url = undefined) {
257
- browser = /** @type {!TargetBrowser} */ (Object.seal(
258
- Object.assign({}, browser)
259
- ))
253
+ browser = /** @type {!TargetBrowser} */ (
254
+ Object.seal(Object.assign({}, browser))
255
+ )
260
256
 
261
257
  TARGET_MAP.set(this, browser)
262
258
  URL_MAP.set(this, url || null)
@@ -293,7 +289,6 @@ class Environment {
293
289
 
294
290
  const realBuild = builder.build
295
291
  builder.build = function () {
296
-
297
292
  builder.forBrowser(browser.name, browser.version, browser.platform)
298
293
 
299
294
  if (browser.capabilities) {
@@ -316,7 +311,7 @@ class Environment {
316
311
  * Configuration options for a {@linkplain ./index.suite test suite}.
317
312
  * @record
318
313
  */
319
- function SuiteOptions() { }
314
+ function SuiteOptions() {}
320
315
 
321
316
  /**
322
317
  * The browsers to run the test suite against.
@@ -491,8 +486,8 @@ function getTestHook(name) {
491
486
  if (type !== 'function') {
492
487
  throw TypeError(
493
488
  `Expected global.${name} to be a function, but is ${type}.` +
494
- ' This can happen if you try using this module when running with' +
495
- ' node directly instead of using jasmine or mocha'
489
+ ' This can happen if you try using this module when running with' +
490
+ ' node directly instead of using jasmine or mocha'
496
491
  )
497
492
  }
498
493
  return fn