selenium-webdriver 4.0.0-alpha.8 → 4.0.0-beta.4

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 (53) hide show
  1. package/CHANGES.md +41 -1
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +0 -0
  5. package/chrome.js +0 -0
  6. package/chromium.js +23 -202
  7. package/devtools/CDPConnection.js +16 -18
  8. package/edge.js +89 -130
  9. package/example/chrome_android.js +45 -0
  10. package/{lib/atoms/make-atoms-module.js → example/chrome_mobile_emulation.js} +26 -15
  11. package/example/firefox_channels.js +84 -0
  12. package/example/google_search.js +50 -0
  13. package/example/google_search_test.js +70 -0
  14. package/example/headless.js +63 -0
  15. package/example/logging.js +64 -0
  16. package/firefox.js +10 -9
  17. package/http/index.js +0 -0
  18. package/http/util.js +3 -3
  19. package/ie.js +0 -0
  20. package/index.js +2 -2
  21. package/io/exec.js +3 -3
  22. package/io/index.js +12 -13
  23. package/io/zip.js +5 -6
  24. package/lib/atoms/find-elements.js +78 -78
  25. package/lib/atoms/get-attribute.js +64 -68
  26. package/lib/atoms/is-displayed.js +27 -27
  27. package/lib/atoms/mutation-listener.js +55 -0
  28. package/lib/by.js +1 -1
  29. package/lib/capabilities.js +2 -3
  30. package/lib/command.js +2 -15
  31. package/lib/error.js +0 -0
  32. package/lib/http.js +17 -30
  33. package/lib/input.js +2 -286
  34. package/lib/logging.js +0 -0
  35. package/lib/promise.js +4 -16
  36. package/lib/proxy.js +0 -0
  37. package/lib/session.js +0 -0
  38. package/lib/symbols.js +0 -0
  39. package/lib/until.js +17 -15
  40. package/lib/webdriver.js +336 -16
  41. package/net/index.js +0 -0
  42. package/net/portprober.js +8 -8
  43. package/opera.js +0 -0
  44. package/package.json +13 -13
  45. package/proxy.js +0 -0
  46. package/remote/index.js +6 -7
  47. package/safari.js +0 -0
  48. package/testing/index.js +504 -0
  49. package/.eslintrc.js +0 -27
  50. package/.prettierrc +0 -4
  51. package/devtools/generator/protocol-dts-generator.js +0 -429
  52. package/lib/README +0 -5
  53. package/lib/actions.js +0 -626
package/lib/webdriver.js CHANGED
@@ -29,7 +29,14 @@ const input = require('./input')
29
29
  const logging = require('./logging')
30
30
  const promise = require('./promise')
31
31
  const Symbols = require('./symbols')
32
+ const cdpTargets = ['page', 'browser']
33
+ const cdp = require('../devtools/CDPConnection')
34
+ const WebSocket = require('ws')
35
+ const http = require('../http/index')
36
+ const fs = require('fs')
32
37
  const { Capabilities } = require('./capabilities')
38
+ const path = require('path')
39
+ const { NoSuchElementError } = require('./error')
33
40
 
34
41
  // Capability names that are defined in the W3C spec.
35
42
  const W3C_CAPABILITY_NAMES = new Set([
@@ -239,14 +246,14 @@ class IWebDriver {
239
246
  * result.
240
247
  * @template T
241
248
  */
242
- execute(command) {} // eslint-disable-line
249
+ execute(command) { } // eslint-disable-line
243
250
 
244
251
  /**
245
252
  * Sets the {@linkplain input.FileDetector file detector} that should be
246
253
  * used with this instance.
247
254
  * @param {input.FileDetector} detector The detector to use or `null`.
248
255
  */
249
- setFileDetector(detector) {} // eslint-disable-line
256
+ setFileDetector(detector) { } // eslint-disable-line
250
257
 
251
258
  /**
252
259
  * @return {!command.Executor} The command executor used by this instance.
@@ -285,7 +292,7 @@ class IWebDriver {
285
292
  * for details).
286
293
  * @return {!input.Actions} A new action sequence for this instance.
287
294
  */
288
- actions(options) {} // eslint-disable-line
295
+ actions(options) { } // eslint-disable-line
289
296
 
290
297
  /**
291
298
  * Executes a snippet of JavaScript in the context of the currently selected
@@ -323,7 +330,7 @@ class IWebDriver {
323
330
  * scripts return value.
324
331
  * @template T
325
332
  */
326
- executeScript(script, ...args) {} // eslint-disable-line
333
+ executeScript(script, ...args) { } // eslint-disable-line
327
334
 
328
335
  /**
329
336
  * Executes a snippet of asynchronous JavaScript in the context of the
@@ -400,7 +407,7 @@ class IWebDriver {
400
407
  * value.
401
408
  * @template T
402
409
  */
403
- executeAsyncScript(script, ...args) {} // eslint-disable-line
410
+ executeAsyncScript(script, ...args) { } // eslint-disable-line
404
411
 
405
412
  /**
406
413
  * Waits for a condition to evaluate to a "truthy" value. The condition may be
@@ -460,7 +467,7 @@ class IWebDriver {
460
467
  * @return {!Promise<void>} A promise that will be resolved when the sleep has
461
468
  * finished.
462
469
  */
463
- sleep(ms) {} // eslint-disable-line
470
+ sleep(ms) { } // eslint-disable-line
464
471
 
465
472
  /**
466
473
  * Retrieves the current window handle.
@@ -503,7 +510,7 @@ class IWebDriver {
503
510
  * @return {!Promise<void>} A promise that will be resolved when the document
504
511
  * has finished loading.
505
512
  */
506
- get(url) {} // eslint-disable-line
513
+ get(url) { } // eslint-disable-line
507
514
 
508
515
  /**
509
516
  * Retrieves the URL for the current page.
@@ -559,7 +566,7 @@ class IWebDriver {
559
566
  * commands against the located element. If the element is not found, the
560
567
  * element will be invalidated and all scheduled commands aborted.
561
568
  */
562
- findElement(locator) {} // eslint-disable-line
569
+ findElement(locator) { } // eslint-disable-line
563
570
 
564
571
  /**
565
572
  * Search for multiple elements on the page. Refer to the documentation on
@@ -569,7 +576,7 @@ class IWebDriver {
569
576
  * @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
570
577
  * array of WebElements.
571
578
  */
572
- findElements(locator) {} // eslint-disable-line
579
+ findElements(locator) { } // eslint-disable-line
573
580
 
574
581
  /**
575
582
  * Takes a screenshot of the current page. The driver makes a best effort to
@@ -618,7 +625,7 @@ class IWebDriver {
618
625
  * shrinkToFit: (boolean|undefined)
619
626
  * pageRanges: (<Array>|undefined)}} options.
620
627
  */
621
- printPage(options) {} // eslint-disable-line
628
+ printPage(options) { } // eslint-disable-line
622
629
  }
623
630
 
624
631
  /**
@@ -925,7 +932,7 @@ class WebDriver {
925
932
 
926
933
  /** @override */
927
934
  sleep(ms) {
928
- return new Promise((resolve) => setTimeout(() => resolve(), ms))
935
+ return new Promise((resolve) => setTimeout(resolve, ms))
929
936
  }
930
937
 
931
938
  /** @override */
@@ -968,16 +975,46 @@ class WebDriver {
968
975
  /** @override */
969
976
  findElement(locator) {
970
977
  let id
971
- locator = by.checkedLocator(locator)
978
+ let cmd = null
979
+
980
+ if (locator instanceof RelativeBy) {
981
+ cmd = new command.Command(
982
+ command.Name.FIND_ELEMENTS_RELATIVE
983
+ ).setParameter('args', locator.marshall())
984
+ } else {
985
+ locator = by.checkedLocator(locator)
986
+ }
987
+
972
988
  if (typeof locator === 'function') {
973
989
  id = this.findElementInternal_(locator, this)
974
- } else {
975
- let cmd = new command.Command(command.Name.FIND_ELEMENT)
990
+ return new WebElementPromise(this, id)
991
+ } else if (cmd === null) {
992
+ cmd = new command.Command(command.Name.FIND_ELEMENT)
976
993
  .setParameter('using', locator.using)
977
994
  .setParameter('value', locator.value)
978
- id = this.execute(cmd)
979
995
  }
980
- return new WebElementPromise(this, id)
996
+
997
+ id = this.execute(cmd)
998
+ if (locator instanceof RelativeBy) {
999
+ return this.normalize_(id)
1000
+ } else {
1001
+ return new WebElementPromise(this, id)
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * @param {!Function} webElementPromise The webElement in unresolved state
1007
+ * @return {!Promise<!WebElement>} First single WebElement from array of resolved promises
1008
+ */
1009
+ async normalize_(webElementPromise) {
1010
+ let result = await webElementPromise
1011
+ if (result.length === 0) {
1012
+ throw new NoSuchElementError(
1013
+ 'Cannot locate an element with provided parameters'
1014
+ )
1015
+ } else {
1016
+ return result[0]
1017
+ }
981
1018
  }
982
1019
 
983
1020
  /**
@@ -1150,6 +1187,270 @@ class WebDriver {
1150
1187
  new command.Command(command.Name.PRINT_PAGE).setParameters(resultObj)
1151
1188
  )
1152
1189
  }
1190
+
1191
+ /**
1192
+ * Creates a new WebSocket connection.
1193
+ * @return {!Promise<resolved>} A new CDP instance.
1194
+ */
1195
+ async createCDPConnection(target) {
1196
+ const caps = await this.getCapabilities()
1197
+ const seCdp = caps['map_'].get('se:cdp')
1198
+ const vendorInfo =
1199
+ caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
1200
+ caps['map_'].get(this.VENDOR_CAPABILITY_PREFIX + ':edgeOptions') ||
1201
+ caps['map_'].get('moz:debuggerAddress') ||
1202
+ new Map()
1203
+ const debuggerUrl = seCdp || vendorInfo['debuggerAddress'] || vendorInfo
1204
+ this._wsUrl = await this.getWsUrl(debuggerUrl, target)
1205
+
1206
+ return new Promise((resolve, reject) => {
1207
+ try {
1208
+ this._wsConnection = new WebSocket(this._wsUrl)
1209
+ } catch (err) {
1210
+ reject(err)
1211
+ return
1212
+ }
1213
+
1214
+ this._wsConnection.on('open', () => {
1215
+ this._cdpConnection = new cdp.CdpConnection(this._wsConnection)
1216
+ resolve(this._cdpConnection)
1217
+ })
1218
+
1219
+ this._wsConnection.on('error', (error) => {
1220
+ reject(error)
1221
+ })
1222
+ })
1223
+ }
1224
+
1225
+ /**
1226
+ * Retrieves 'webSocketDebuggerUrl' by sending a http request using debugger address
1227
+ * @param {string} debuggerAddress
1228
+ * @param {string} target
1229
+ * @return {string} Returns parsed webSocketDebuggerUrl obtained from the http request
1230
+ */
1231
+ async getWsUrl(debuggerAddress, target) {
1232
+ if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
1233
+ throw new error.InvalidArgumentError('invalid target value')
1234
+ }
1235
+ let path
1236
+ if (target === 'page') {
1237
+ path = '/json'
1238
+ } else {
1239
+ path = '/json/version'
1240
+ }
1241
+ let request = new http.Request('GET', path)
1242
+ let client = new http.HttpClient('http://' + debuggerAddress)
1243
+ let response = await client.send(request)
1244
+ let url
1245
+ if (target.toLowerCase() === 'page') {
1246
+ url = JSON.parse(response.body)[0]['webSocketDebuggerUrl']
1247
+ } else {
1248
+ url = JSON.parse(response.body)['webSocketDebuggerUrl']
1249
+ }
1250
+
1251
+ return url
1252
+ }
1253
+
1254
+ /**
1255
+ * Sets a listener for Fetch.authRequired event from CDP
1256
+ * If event is triggered, it enter username and password
1257
+ * and allows the test to move forward
1258
+ * @param {string} username
1259
+ * @param {string} password
1260
+ * @param connection CDP Connection
1261
+ */
1262
+ async register(username, password, connection) {
1263
+ await connection.execute(
1264
+ 'Network.setCacheDisabled',
1265
+ this.getRandomNumber(1, 10),
1266
+ {
1267
+ cacheDisabled: true,
1268
+ },
1269
+ null
1270
+ )
1271
+
1272
+ this._wsConnection.on('message', (message) => {
1273
+ const params = JSON.parse(message)
1274
+
1275
+ if (params.method === 'Fetch.authRequired') {
1276
+ const requestParams = params['params']
1277
+ connection.execute(
1278
+ 'Fetch.continueWithAuth',
1279
+ this.getRandomNumber(1, 10),
1280
+ {
1281
+ requestId: requestParams['requestId'],
1282
+ authChallengeResponse: {
1283
+ response: 'ProvideCredentials',
1284
+ username: username,
1285
+ password: password,
1286
+ },
1287
+ }
1288
+ )
1289
+ } else if (params.method === 'Fetch.requestPaused') {
1290
+ const requestPausedParams = params['params']
1291
+ connection.execute(
1292
+ 'Fetch.continueRequest',
1293
+ this.getRandomNumber(1, 10),
1294
+ {
1295
+ requestId: requestPausedParams['requestId'],
1296
+ }
1297
+ )
1298
+ }
1299
+ })
1300
+
1301
+ await connection.execute(
1302
+ 'Fetch.enable',
1303
+ 1,
1304
+ {
1305
+ handleAuthRequests: true,
1306
+ },
1307
+ null
1308
+ )
1309
+ }
1310
+
1311
+ /**
1312
+ *
1313
+ * @param connection
1314
+ * @param callback
1315
+ * @returns {Promise<void>}
1316
+ */
1317
+ async onLogEvent(connection, callback) {
1318
+ await connection.execute(
1319
+ 'Runtime.enable',
1320
+ this.getRandomNumber(1, 10),
1321
+ {},
1322
+ null
1323
+ )
1324
+
1325
+ this._wsConnection.on('message', (message) => {
1326
+ const params = JSON.parse(message)
1327
+
1328
+ if (params.method === 'Runtime.consoleAPICalled') {
1329
+ const consoleEventParams = params['params']
1330
+ let event = {
1331
+ type: consoleEventParams['type'],
1332
+ timestamp: new Date(consoleEventParams['timestamp']),
1333
+ args: consoleEventParams['args'],
1334
+ }
1335
+
1336
+ callback(event)
1337
+ }
1338
+ })
1339
+ }
1340
+
1341
+ /**
1342
+ *
1343
+ * @param connection
1344
+ * @param callback
1345
+ * @returns {Promise<void>}
1346
+ */
1347
+ async onLogException(connection, callback) {
1348
+ await connection.execute(
1349
+ 'Runtime.enable',
1350
+ this.getRandomNumber(1, 10),
1351
+ {},
1352
+ null
1353
+ )
1354
+
1355
+ this._wsConnection.on('message', (message) => {
1356
+ const params = JSON.parse(message)
1357
+
1358
+ if (params.method === 'Runtime.exceptionThrown') {
1359
+ const exceptionEventParams = params['params']
1360
+ let event = {
1361
+ exceptionDetails: exceptionEventParams['exceptionDetails'],
1362
+ timestamp: new Date(exceptionEventParams['timestamp']),
1363
+ }
1364
+
1365
+ callback(event)
1366
+ }
1367
+ })
1368
+ }
1369
+
1370
+ /**
1371
+ * @param connection
1372
+ * @param callback
1373
+ * @returns {Promise<void>}
1374
+ */
1375
+ async logMutationEvents(connection, callback) {
1376
+ await connection.execute(
1377
+ 'Runtime.enable',
1378
+ this.getRandomNumber(1, 10),
1379
+ {},
1380
+ null
1381
+ )
1382
+ await connection.execute(
1383
+ 'Page.enable',
1384
+ this.getRandomNumber(1, 10),
1385
+ {},
1386
+ null
1387
+ )
1388
+
1389
+ await connection.execute(
1390
+ 'Runtime.addBinding',
1391
+ this.getRandomNumber(1, 10),
1392
+ {
1393
+ name: '__webdriver_attribute',
1394
+ },
1395
+ null
1396
+ )
1397
+
1398
+ let mutationListener = ''
1399
+ try {
1400
+ // Depending on what is running the code it could appear in 2 different places which is why we try
1401
+ // here and then the other location
1402
+ mutationListener = fs
1403
+ .readFileSync(
1404
+ './javascript/node/selenium-webdriver/lib/atoms/mutation-listener.js',
1405
+ 'utf-8'
1406
+ )
1407
+ .toString()
1408
+ } catch {
1409
+ mutationListener = fs
1410
+ .readFileSync(
1411
+ path.resolve(__dirname, './atoms/mutation-listener.js'),
1412
+ 'utf-8'
1413
+ )
1414
+ .toString()
1415
+ }
1416
+
1417
+ this.executeScript(mutationListener)
1418
+
1419
+ await connection.execute(
1420
+ 'Page.addScriptToEvaluateOnNewDocument',
1421
+ this.getRandomNumber(1, 10),
1422
+ {
1423
+ source: mutationListener,
1424
+ },
1425
+ null
1426
+ )
1427
+
1428
+ this._wsConnection.on('message', async (message) => {
1429
+ const params = JSON.parse(message)
1430
+ if (params.method === 'Runtime.bindingCalled') {
1431
+ let payload = JSON.parse(params['params']['payload'])
1432
+ let elements = await this.findElements({
1433
+ css: '*[data-__webdriver_id=' + payload['target'],
1434
+ })
1435
+
1436
+ if (elements.length === 0) {
1437
+ return
1438
+ }
1439
+
1440
+ let event = {
1441
+ element: elements[0],
1442
+ attribute_name: payload['name'],
1443
+ current_value: payload['value'],
1444
+ old_value: payload['oldValue'],
1445
+ }
1446
+ callback(event)
1447
+ }
1448
+ })
1449
+ }
1450
+
1451
+ getRandomNumber(min, max) {
1452
+ return Math.floor(Math.random() * (max - min + 1) + min)
1453
+ }
1153
1454
  }
1154
1455
 
1155
1456
  /**
@@ -2324,6 +2625,25 @@ class WebElement {
2324
2625
  return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT))
2325
2626
  }
2326
2627
 
2628
+ /**
2629
+ * Get the computed WAI-ARIA role of element.
2630
+ *
2631
+ * @return {!Promise<string>} A promise that will be
2632
+ * resolved with the element's computed role.
2633
+ */
2634
+ getAriaRole() {
2635
+ return this.execute_(new command.Command(command.Name.GET_COMPUTED_ROLE))
2636
+ }
2637
+
2638
+ /**
2639
+ * Get the computed WAI-ARIA label of element.
2640
+ *
2641
+ * @return {!Promise<string>} A promise that will be
2642
+ * resolved with the element's computed label.
2643
+ */
2644
+ getAccessibleName() {
2645
+ return this.execute_(new command.Command(command.Name.GET_COMPUTED_LABEL))
2646
+ }
2327
2647
  /**
2328
2648
  * Returns an object describing an element's location, in pixels relative to
2329
2649
  * the document element, and the element's size in pixels.
package/net/index.js CHANGED
File without changes
package/net/portprober.js CHANGED
@@ -52,21 +52,21 @@ function isFree(port, opt_host) {
52
52
 
53
53
  function findFreePort(opt_host) {
54
54
  return new Promise((resolve, reject) => {
55
- const server = net.createServer();
56
- server.on('listening', function() {
57
- resolve(server.address().port);
58
- server.close();
59
- });
55
+ const server = net.createServer()
56
+ server.on('listening', function () {
57
+ resolve(server.address().port)
58
+ server.close()
59
+ })
60
60
  server.on('error', (e) => {
61
61
  if (e.code === 'EADDRINUSE' || e.code === 'EACCES') {
62
62
  resolve('Unable to find a free port')
63
63
  } else {
64
64
  reject(e)
65
65
  }
66
- });
66
+ })
67
67
  // By providing 0 we let the operative system find an arbitrary port
68
- server.listen(0, opt_host);
69
- });
68
+ server.listen(0, opt_host)
69
+ })
70
70
  }
71
71
 
72
72
  // PUBLIC API
package/opera.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selenium-webdriver",
3
- "version": "4.0.0-alpha.8",
3
+ "version": "4.0.0-beta.4",
4
4
  "description": "The official WebDriver JavaScript bindings from the Selenium project",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -10,7 +10,7 @@
10
10
  "webdriver",
11
11
  "webdriverjs"
12
12
  ],
13
- "homepage": "https://github.com/SeleniumHQ/selenium",
13
+ "homepage": "https://github.com/SeleniumHQ/selenium/tree/trunk/javascript/node/selenium-webdriver#readme",
14
14
  "bugs": {
15
15
  "url": "https://github.com/SeleniumHQ/selenium/issues"
16
16
  },
@@ -23,27 +23,27 @@
23
23
  "node": ">= 10.15.0"
24
24
  },
25
25
  "dependencies": {
26
- "jszip": "^3.5.0",
27
- "rimraf": "^2.7.1",
28
- "tmp": "^0.1.0",
29
- "ws": "^7.3.1"
26
+ "jszip": "^3.6.0",
27
+ "rimraf": "^3.0.2",
28
+ "tmp": "^0.2.1",
29
+ "ws": ">=7.4.6"
30
30
  },
31
31
  "devDependencies": {
32
- "eslint": "^7.6.0",
33
- "eslint-config-prettier": "^6.11.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.1.4",
36
+ "eslint-plugin-prettier": "^3.4.0",
37
37
  "express": "^4.17.1",
38
38
  "jasmine": "^3.6.1",
39
39
  "mocha": "^8.1.0",
40
40
  "multer": "^1.4.2",
41
- "prettier": "^2.0.5",
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
- "lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --ext js \"**/*.js\"",
46
+ "lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --ext js lib/http.js \"**/*.js\"",
47
47
  "test": "npm run lint && mocha -t 600000 --recursive test",
48
48
  "test-jasmine": "jasmine JASMINE_CONFIG_PATH=jasmine.json"
49
49
  },
package/proxy.js CHANGED
File without changes
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
 
package/safari.js CHANGED
File without changes