selenium-webdriver 3.1.0 → 3.6.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.
Files changed (65) hide show
  1. package/CHANGES.md +112 -0
  2. package/NOTICE +1 -1
  3. package/README.md +5 -11
  4. package/chrome.js +122 -33
  5. package/edge.js +2 -2
  6. package/example/async_await_test.js +69 -0
  7. package/example/chrome_android.js +2 -3
  8. package/example/chrome_mobile_emulation.js +3 -4
  9. package/example/firefox_channels.js +3 -3
  10. package/example/google_search.js +3 -3
  11. package/example/google_search_generator.js +2 -5
  12. package/example/google_search_test.js +5 -6
  13. package/example/headless.js +55 -0
  14. package/example/logging.js +3 -3
  15. package/example/parallel_flows.js +4 -7
  16. package/firefox/binary.js +3 -0
  17. package/firefox/extension.js +118 -81
  18. package/firefox/index.js +117 -263
  19. package/firefox/profile.js +18 -137
  20. package/ie.js +2 -2
  21. package/index.js +4 -1
  22. package/io/index.js +59 -0
  23. package/io/zip.js +214 -0
  24. package/lib/atoms/get-attribute.js +9 -0
  25. package/lib/atoms/is-displayed.js +73 -78
  26. package/lib/by.js +8 -0
  27. package/lib/capabilities.js +61 -22
  28. package/lib/command.js +2 -0
  29. package/lib/error.js +32 -2
  30. package/lib/http.js +53 -44
  31. package/lib/promise.js +72 -25
  32. package/lib/test/data/click_tests/disabled_element.html +12 -0
  33. package/lib/test/data/content-editable.html +10 -0
  34. package/lib/test/data/firefox/webextension.xpi +0 -0
  35. package/lib/test/data/key_logger.html +34 -0
  36. package/lib/test/data/nestedElements.html +10 -1
  37. package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
  38. package/lib/test/data/single_text_input.html +12 -0
  39. package/lib/test/index.js +4 -36
  40. package/lib/webdriver.js +147 -55
  41. package/net/portprober.js +1 -1
  42. package/opera.js +1 -1
  43. package/package.json +2 -2
  44. package/phantomjs.js +2 -2
  45. package/remote/index.js +21 -14
  46. package/safari.js +2 -2
  47. package/test/firefox/extension_test.js +41 -17
  48. package/test/firefox/firefox_test.js +37 -19
  49. package/test/firefox/profile_test.js +14 -59
  50. package/test/http/http_test.js +18 -0
  51. package/test/{io_test.js → io/io_test.js} +40 -1
  52. package/test/io/zip_test.js +128 -0
  53. package/test/lib/by_test.js +21 -0
  54. package/test/lib/error_test.js +21 -1
  55. package/test/lib/http_test.js +11 -3
  56. package/test/lib/webdriver_test.js +107 -3
  57. package/test/page_loading_test.js +0 -6
  58. package/test/remote_test.js +3 -3
  59. package/test/session_test.js +68 -23
  60. package/testing/index.js +2 -3
  61. package/lib/atoms/getAttribute.js +0 -12
  62. package/lib/firefox/amd64/libnoblur64.so +0 -0
  63. package/lib/firefox/i386/libnoblur.so +0 -0
  64. package/lib/firefox/webdriver.json +0 -69
  65. package/lib/firefox/webdriver.xpi +0 -0
package/firefox/index.js CHANGED
@@ -114,16 +114,6 @@
114
114
  * .setFirefoxOptions(options)
115
115
  * .build();
116
116
  *
117
- * __Testing Older Versions of Firefox__
118
- *
119
- * To test versions of Firefox prior to Firefox 47, you must disable the use of
120
- * the geckodriver using the {@link Options} class.
121
- *
122
- * let options = new firefox.Options().useGeckoDriver(false);
123
- *
124
- * Alternatively, you may disable the geckodriver at runtime by setting the
125
- * environment variable `SELENIUM_MARIONETTE=false`.
126
- *
127
117
  * [geckodriver release]: https://github.com/mozilla/geckodriver/releases/
128
118
  * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
129
119
  */
@@ -134,7 +124,6 @@ const url = require('url');
134
124
 
135
125
  const {Binary, Channel} = require('./binary'),
136
126
  Profile = require('./profile').Profile,
137
- decodeProfile = require('./profile').decode,
138
127
  http = require('../http'),
139
128
  httpUtil = require('../http/util'),
140
129
  io = require('../io'),
@@ -148,35 +137,6 @@ const {Binary, Channel} = require('./binary'),
148
137
  remote = require('../remote');
149
138
 
150
139
 
151
- /**
152
- * Firefox-specific capability keys. Users should use the {@linkplain Options}
153
- * class instead of referencing these keys directly. _These keys are considered
154
- * implementation details and may be removed or changed at any time._
155
- *
156
- * @enum {string}
157
- */
158
- const Capability = {
159
- /**
160
- * Defines the Firefox binary to use. May be set to either a
161
- * {@linkplain Binary} instance, or a string path to the Firefox executable.
162
- */
163
- BINARY: 'firefox_binary',
164
-
165
- /**
166
- * Specifies whether to use Mozilla's Marionette, or the legacy FirefoxDriver
167
- * from the Selenium project. Defaults to false.
168
- */
169
- MARIONETTE: 'marionette',
170
-
171
- /**
172
- * Defines the Firefox profile to use. May be set to either a
173
- * {@linkplain Profile} instance, or to a base-64 encoded zip of a profile
174
- * directory.
175
- */
176
- PROFILE: 'firefox_profile'
177
- };
178
-
179
-
180
140
  /**
181
141
  * Configuration options for the FirefoxDriver.
182
142
  */
@@ -185,17 +145,58 @@ class Options {
185
145
  /** @private {Profile} */
186
146
  this.profile_ = null;
187
147
 
188
- /** @private {Binary} */
148
+ /** @private {(Binary|Channel|string|null)} */
189
149
  this.binary_ = null;
190
150
 
151
+ /** @private {!Array<string>} */
152
+ this.args_ = [];
153
+
191
154
  /** @private {logging.Preferences} */
192
155
  this.logPrefs_ = null;
193
156
 
194
157
  /** @private {?capabilities.ProxyConfig} */
195
158
  this.proxy_ = null;
159
+ }
196
160
 
197
- /** @private {boolean} */
198
- this.marionette_ = true;
161
+ /**
162
+ * Specify additional command line arguments that should be used when starting
163
+ * the Firefox browser.
164
+ *
165
+ * @param {...(string|!Array<string>)} args The arguments to include.
166
+ * @return {!Options} A self reference.
167
+ */
168
+ addArguments(...args) {
169
+ this.args_ = this.args_.concat(...args);
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Configures the geckodriver to start Firefox in headless mode.
175
+ *
176
+ * @return {!Options} A self reference.
177
+ */
178
+ headless() {
179
+ return this.addArguments('-headless');
180
+ }
181
+
182
+ /**
183
+ * Sets the initial window size when running in
184
+ * {@linkplain #headless headless} mode.
185
+ *
186
+ * @param {{width: number, height: number}} size The desired window size.
187
+ * @return {!Options} A self reference.
188
+ * @throws {TypeError} if width or height is unspecified, not a number, or
189
+ * less than or equal to 0.
190
+ */
191
+ windowSize({width, height}) {
192
+ function checkArg(arg) {
193
+ if (typeof arg !== 'number' || arg <= 0) {
194
+ throw TypeError('Arguments must be {width, height} with numbers > 0');
195
+ }
196
+ }
197
+ checkArg(width);
198
+ checkArg(height);
199
+ return this.addArguments(`--window-size=${width},${height}`);
199
200
  }
200
201
 
201
202
  /**
@@ -224,17 +225,14 @@ class Options {
224
225
  * @throws {TypeError} If `binary` is an invalid type.
225
226
  */
226
227
  setBinary(binary) {
227
- if (typeof binary === 'string' || binary instanceof Channel) {
228
- binary = new Binary(binary);
229
- }
230
-
231
- if (!(binary instanceof Binary)) {
232
- throw TypeError(
233
- 'binary must be a string path, Channel, or Binary object');
228
+ if (binary instanceof Binary
229
+ || binary instanceof Channel
230
+ || typeof binary === 'string') {
231
+ this.binary_ = binary;
232
+ return this;
234
233
  }
235
-
236
- this.binary_ = binary;
237
- return this;
234
+ throw TypeError(
235
+ 'binary must be a string path, Channel, or Binary object');
238
236
  }
239
237
 
240
238
  /**
@@ -258,38 +256,69 @@ class Options {
258
256
  return this;
259
257
  }
260
258
 
261
- /**
262
- * Sets whether to use Mozilla's geckodriver to drive the browser. This option
263
- * is enabled by default and required for Firefox 47+.
264
- *
265
- * @param {boolean} enable Whether to enable the geckodriver.
266
- * @see https://github.com/mozilla/geckodriver
267
- */
268
- useGeckoDriver(enable) {
269
- this.marionette_ = enable;
270
- return this;
271
- }
272
-
273
259
  /**
274
260
  * Converts these options to a {@link capabilities.Capabilities} instance.
275
261
  *
276
262
  * @return {!capabilities.Capabilities} A new capabilities object.
277
263
  */
278
264
  toCapabilities() {
279
- var caps = capabilities.Capabilities.firefox();
265
+ let caps = capabilities.Capabilities.firefox();
266
+ let firefoxOptions = {};
267
+ caps.set('moz:firefoxOptions', firefoxOptions);
268
+
280
269
  if (this.logPrefs_) {
281
270
  caps.set(capabilities.Capability.LOGGING_PREFS, this.logPrefs_);
282
271
  }
272
+
283
273
  if (this.proxy_) {
284
274
  caps.set(capabilities.Capability.PROXY, this.proxy_);
285
275
  }
276
+
277
+ if (this.args_.length) {
278
+ firefoxOptions['args'] = this.args_.concat();
279
+ }
280
+
286
281
  if (this.binary_) {
287
- caps.set(Capability.BINARY, this.binary_);
282
+ if (this.binary_ instanceof Binary) {
283
+ let exe = this.binary_.getExe();
284
+ if (exe) {
285
+ firefoxOptions['binary'] = exe;
286
+ }
287
+
288
+ let args = this.binary_.getArguments();
289
+ if (args.length) {
290
+ if (this.args_.length) {
291
+ throw Error(
292
+ 'You may specify browser arguments with Options.addArguments'
293
+ + ' (preferred) or Binary.addArguments, but not both');
294
+ }
295
+ firefoxOptions['args'] = args;
296
+ }
297
+ } else if (this.binary_ instanceof Channel) {
298
+ firefoxOptions['binary'] = this.binary_.locate();
299
+
300
+ } else if (typeof this.binary_ === 'string') {
301
+ firefoxOptions['binary'] = this.binary_;
302
+ }
288
303
  }
304
+
289
305
  if (this.profile_) {
290
- caps.set(Capability.PROFILE, this.profile_);
306
+ // If the user specified a template directory or any extensions to
307
+ // install, we need to encode the profile as a base64 string (which
308
+ // requires writing it to disk first). Otherwise, if the user just
309
+ // specified some custom preferences, we can send those directly.
310
+ let profile = this.profile_;
311
+ if (profile.getTemplateDir() || profile.getExtensions().length) {
312
+ firefoxOptions['profile'] = profile.encode();
313
+
314
+ } else {
315
+ let prefs = profile.getPreferences();
316
+ if (Object.keys(prefs).length) {
317
+ firefoxOptions['prefs'] = prefs;
318
+ }
319
+ }
291
320
  }
292
- caps.set(Capability.MARIONETTE, this.marionette_);
321
+
293
322
  return caps;
294
323
  }
295
324
  }
@@ -325,33 +354,13 @@ function findGeckoDriver() {
325
354
  throw Error(
326
355
  'The ' + GECKO_DRIVER_EXE + ' executable could not be found on the current ' +
327
356
  'PATH. Please download the latest version from ' +
328
- 'https://github.com/mozilla/geckodriver/releases/' +
329
- 'WebDriver and ensure it can be found on your PATH.');
357
+ 'https://github.com/mozilla/geckodriver/releases/ ' +
358
+ 'and ensure it can be found on your PATH.');
330
359
  }
331
360
  return exe;
332
361
  }
333
362
 
334
363
 
335
- /**
336
- * @param {(Profile|string)} profile The profile to prepare.
337
- * @param {number} port The port the FirefoxDriver should listen on.
338
- * @return {!Promise<string>} a promise for the path to the profile directory.
339
- */
340
- function prepareProfile(profile, port) {
341
- if (typeof profile === 'string') {
342
- return decodeProfile(/** @type {string} */(profile)).then(dir => {
343
- profile = new Profile(dir);
344
- profile.setPreference('webdriver_firefox_port', port);
345
- return profile.writeToDisk();
346
- });
347
- }
348
-
349
- profile = profile || new Profile;
350
- profile.setPreference('webdriver_firefox_port', port);
351
- return profile.writeToDisk();
352
- }
353
-
354
-
355
364
  function normalizeProxyConfiguration(config) {
356
365
  if ('manual' === config.proxyType) {
357
366
  if (config.ftpProxy && !config.ftpProxyPort) {
@@ -448,152 +457,6 @@ class ServiceBuilder extends remote.DriverService.Builder {
448
457
  enableVerboseLogging(opt_trace) {
449
458
  return this.addArguments(opt_trace ? '-vv' : '-v');
450
459
  }
451
-
452
- /**
453
- * Sets the path to the executable Firefox binary that the geckodriver should
454
- * use. If this method is not called, this builder will attempt to locate
455
- * Firefox in the default installation location for the current platform.
456
- *
457
- * @param {(string|!Binary)} binary Path to the executable Firefox binary to use.
458
- * @return {!ServiceBuilder} A self reference.
459
- * @see Binary#locate()
460
- */
461
- setFirefoxBinary(binary) {
462
- let exe = typeof binary === 'string'
463
- ? Promise.resolve(binary) : binary.locate();
464
- return this.addArguments('-b', exe);
465
- }
466
- }
467
-
468
-
469
- /**
470
- * @typedef {{executor: !command.Executor,
471
- * capabilities: (!capabilities.Capabilities|
472
- * {desired: (capabilities.Capabilities|undefined),
473
- * required: (capabilities.Capabilities|undefined)}),
474
- * onQuit: function(this: void): ?}}
475
- */
476
- var DriverSpec;
477
-
478
-
479
- /**
480
- * @param {(http.Executor|remote.DriverService|undefined)} executor
481
- * @param {!capabilities.Capabilities} caps
482
- * @param {Profile} profile
483
- * @param {Binary} binary
484
- * @return {DriverSpec}
485
- */
486
- function createGeckoDriver(executor, caps, profile, binary) {
487
- let firefoxOptions = {};
488
- caps.set('moz:firefoxOptions', firefoxOptions);
489
-
490
- if (binary) {
491
- if (binary.getExe()) {
492
- firefoxOptions['binary'] = binary.getExe();
493
- }
494
-
495
- let args = binary.getArguments();
496
- if (args.length) {
497
- firefoxOptions['args'] = args;
498
- }
499
- }
500
-
501
- if (profile) {
502
- // If the user specified a template directory or any extensions to install,
503
- // we need to encode the profile as a base64 string (which requires writing
504
- // it to disk first). Otherwise, if the user just specified some custom
505
- // preferences, we can send those directly.
506
- if (profile.getTemplateDir() || profile.getExtensions().length) {
507
- firefoxOptions['profile'] = profile.encode();
508
-
509
- } else {
510
- let prefs = profile.getPreferences();
511
- if (Object.keys(prefs).length) {
512
- firefoxOptions['prefs'] = prefs;
513
- }
514
- }
515
- }
516
-
517
- let sessionCaps = caps;
518
- if (caps.has(capabilities.Capability.PROXY)) {
519
- let proxy = normalizeProxyConfiguration(
520
- caps.get(capabilities.Capability.PROXY));
521
-
522
- // Marionette requires proxy settings to be specified as required
523
- // capabilities. See mozilla/geckodriver#97
524
- let required = new capabilities.Capabilities()
525
- .set(capabilities.Capability.PROXY, proxy);
526
-
527
- caps.delete(capabilities.Capability.PROXY);
528
- sessionCaps = {required, desired: caps};
529
- }
530
-
531
- /** @type {!command.Executor} */
532
- let cmdExecutor;
533
- let onQuit = function() {};
534
-
535
- if (executor instanceof http.Executor) {
536
- configureExecutor(executor);
537
- cmdExecutor = executor;
538
- } else if (executor instanceof remote.DriverService) {
539
- cmdExecutor = createExecutor(executor.start());
540
- onQuit = () => executor.kill();
541
- } else {
542
- let builder = new ServiceBuilder();
543
- if (binary) {
544
- builder.setFirefoxBinary(binary);
545
- }
546
- let service = builder.build();
547
- cmdExecutor = createExecutor(service.start());
548
- onQuit = () => service.kill();
549
- }
550
-
551
- return {
552
- executor: cmdExecutor,
553
- capabilities: sessionCaps,
554
- onQuit
555
- };
556
- }
557
-
558
-
559
- /**
560
- * @param {!capabilities.Capabilities} caps
561
- * @param {Profile} profile
562
- * @param {!Binary} binary
563
- * @return {DriverSpec}
564
- */
565
- function createLegacyDriver(caps, profile, binary, flow) {
566
- profile = profile || new Profile;
567
-
568
- let freePort = portprober.findFreePort();
569
- let preparedProfile =
570
- freePort.then(port => prepareProfile(profile, port));
571
- let command = preparedProfile.then(dir => binary.launch(dir));
572
-
573
- let serverUrl = command.then(() => freePort)
574
- .then(function(/** number */port) {
575
- let serverUrl = url.format({
576
- protocol: 'http',
577
- hostname: net.getLoopbackAddress(),
578
- port: port + '',
579
- pathname: '/hub'
580
- });
581
- let ready = httpUtil.waitForServer(serverUrl, 45 * 1000);
582
- return ready.then(() => serverUrl);
583
- });
584
-
585
- return {
586
- executor: createExecutor(serverUrl),
587
- capabilities: caps,
588
- onQuit: function() {
589
- return command.then(command => {
590
- command.kill();
591
- return preparedProfile.then(io.rmDir)
592
- .then(() => command.result(),
593
- () => command.result());
594
- });
595
- }
596
- };
597
460
  }
598
461
 
599
462
 
@@ -634,38 +497,29 @@ class Driver extends webdriver.WebDriver {
634
497
  caps = new capabilities.Capabilities(opt_config);
635
498
  }
636
499
 
637
- let binary = caps.get(Capability.BINARY) || new Binary();
638
- caps.delete(Capability.BINARY);
639
- if (typeof binary === 'string') {
640
- binary = new Binary(binary);
500
+ if (caps.has(capabilities.Capability.PROXY)) {
501
+ let proxy =
502
+ normalizeProxyConfiguration(caps.get(capabilities.Capability.PROXY));
503
+ caps.set(capabilities.Capability.PROXY, proxy);
641
504
  }
642
505
 
643
- let profile;
644
- if (caps.has(Capability.PROFILE)) {
645
- profile = caps.get(Capability.PROFILE);
646
- caps.delete(Capability.PROFILE);
647
- }
648
-
649
- // Users must now explicitly disable marionette to use the legacy
650
- // FirefoxDriver.
651
- let noMarionette =
652
- caps.get(Capability.MARIONETTE) === false
653
- || /^0|false$/i.test(process.env['SELENIUM_MARIONETTE']);
654
- let useMarionette = !noMarionette;
506
+ let executor;
507
+ let onQuit;
655
508
 
656
- let spec;
657
- if (useMarionette) {
658
- spec = createGeckoDriver(opt_executor, caps, profile, binary);
509
+ if (opt_executor instanceof http.Executor) {
510
+ executor = opt_executor;
511
+ configureExecutor(executor);
512
+ } else if (opt_executor instanceof remote.DriverService) {
513
+ executor = createExecutor(opt_executor.start());
514
+ onQuit = () => opt_executor.kill();
659
515
  } else {
660
- if (opt_executor) {
661
- throw Error('You may not use a custom command executor with the legacy'
662
- + ' FirefoxDriver');
663
- }
664
- spec = createLegacyDriver(caps, profile, binary, opt_flow);
516
+ let service = new ServiceBuilder().build();
517
+ executor = createExecutor(service.start());
518
+ onQuit = () => service.kill();
665
519
  }
666
520
 
667
- return /** @type {!Driver} */(webdriver.WebDriver.createSession(
668
- spec.executor, spec.capabilities, opt_flow, this, spec.onQuit));
521
+ return /** @type {!Driver} */(super.createSession(
522
+ executor, caps, opt_flow, onQuit));
669
523
  }
670
524
 
671
525
  /**