selenium-webdriver 3.0.0-beta-1 → 3.0.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.
Files changed (77) hide show
  1. package/CHANGES.md +109 -0
  2. package/LICENSE +1 -1
  3. package/README.md +7 -6
  4. package/chrome.js +31 -128
  5. package/edge.js +13 -91
  6. package/example/chrome_android.js +18 -13
  7. package/example/chrome_mobile_emulation.js +19 -15
  8. package/example/google_search.js +11 -11
  9. package/example/google_search_generator.js +23 -18
  10. package/example/google_search_test.js +31 -17
  11. package/example/logging.js +9 -17
  12. package/example/parallel_flows.js +3 -0
  13. package/firefox/binary.js +17 -0
  14. package/firefox/index.js +198 -101
  15. package/firefox/profile.js +24 -3
  16. package/http/util.js +95 -59
  17. package/ie.js +6 -10
  18. package/index.js +636 -2
  19. package/lib/actions.js +18 -10
  20. package/lib/atoms/getAttribute.js +11 -0
  21. package/lib/atoms/isDisplayed.js +106 -0
  22. package/lib/firefox/amd64/libnoblur64.so +0 -0
  23. package/lib/firefox/i386/libnoblur.so +0 -0
  24. package/lib/firefox/webdriver.xpi +0 -0
  25. package/lib/http.js +159 -46
  26. package/lib/logging.js +7 -1
  27. package/lib/promise.js +602 -358
  28. package/lib/proxy.js +28 -0
  29. package/lib/session.js +1 -1
  30. package/lib/test/build.js +26 -28
  31. package/lib/test/data/formPage.html +1 -0
  32. package/lib/test/fileserver.js +12 -10
  33. package/lib/test/httpserver.js +4 -6
  34. package/lib/test/index.js +18 -11
  35. package/lib/test/promise.js +79 -0
  36. package/lib/webdriver.js +587 -479
  37. package/opera.js +12 -88
  38. package/package.json +13 -14
  39. package/phantomjs.js +7 -14
  40. package/remote/index.js +201 -30
  41. package/safari.js +62 -416
  42. package/test/actions_test.js +9 -11
  43. package/test/chrome/options_test.js +8 -8
  44. package/test/chrome/service_test.js +2 -2
  45. package/test/cookie_test.js +57 -57
  46. package/test/element_finding_test.js +201 -185
  47. package/test/execute_script_test.js +108 -111
  48. package/test/fingerprint_test.js +16 -11
  49. package/test/firefox/firefox_test.js +75 -49
  50. package/test/firefox/profile_test.js +1 -3
  51. package/test/http/util_test.js +21 -27
  52. package/test/io_test.js +2 -5
  53. package/test/lib/http_test.js +8 -6
  54. package/test/lib/promise_aplus_test.js +48 -44
  55. package/test/lib/promise_error_test.js +687 -611
  56. package/test/lib/promise_flow_test.js +1810 -1806
  57. package/test/lib/promise_generator_test.js +227 -225
  58. package/test/lib/promise_test.js +864 -835
  59. package/test/lib/until_test.js +1 -1
  60. package/test/lib/webdriver_test.js +520 -424
  61. package/test/logging_test.js +43 -45
  62. package/test/net/portprober_test.js +23 -24
  63. package/test/page_loading_test.js +84 -78
  64. package/test/phantomjs/execute_phantomjs_test.js +11 -25
  65. package/test/proxy_test.js +23 -23
  66. package/test/remote_test.js +22 -22
  67. package/test/safari_test.js +108 -0
  68. package/test/session_test.js +11 -10
  69. package/test/stale_element_test.js +18 -16
  70. package/test/tag_name_test.js +7 -5
  71. package/test/testing/assert_test.js +2 -3
  72. package/test/testing/index_test.js +163 -117
  73. package/test/upload_test.js +13 -13
  74. package/test/window_test.js +94 -70
  75. package/testing/index.js +176 -37
  76. package/builder.js +0 -553
  77. package/lib/safari/client.js +0 -56
@@ -18,28 +18,33 @@
18
18
  /**
19
19
  * @fileoverview An example WebDriver script using generator functions.
20
20
  *
21
- * Usage: node selenium-webdriver/example/google_search_generator.js
21
+ * Before running this script, ensure that Mozilla's geckodriver is present on
22
+ * your system PATH: <https://github.com/mozilla/geckodriver/releases>
23
+ *
24
+ * Usage:
25
+ *
26
+ * node selenium-webdriver/example/google_search_generator.js
22
27
  */
23
28
 
24
- var webdriver = require('..'),
25
- By = webdriver.By;
29
+ 'use strict';
30
+
31
+ const {Builder, By, promise, until} = require('..');
26
32
 
27
- var driver = new webdriver.Builder()
28
- .forBrowser('firefox')
29
- .build();
33
+ promise.consume(function* () {
34
+ let driver;
35
+ try {
36
+ driver = yield new Builder().forBrowser('firefox').build();
30
37
 
31
- driver.get('http://www.google.com/ncr');
32
- driver.call(function* () {
33
- var query = yield driver.findElement(By.name('q'));
34
- query.sendKeys('webdriver');
38
+ yield driver.get('http://www.google.com/ncr');
35
39
 
36
- var submit = yield driver.findElement(By.name('btnG'));
37
- submit.click();
38
- });
40
+ let q = yield driver.findElement(By.name('q'));
41
+ yield q.sendKeys('webdriver');
39
42
 
40
- driver.wait(function* () {
41
- var title = yield driver.getTitle();
42
- return 'webdriver - Google Search' === title;
43
- }, 1000);
43
+ let btnG = yield driver.findElement(By.name('btnG'));
44
+ yield btnG.click();
44
45
 
45
- driver.quit();
46
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
47
+ } finally {
48
+ yield driver && driver.quit();
49
+ }
50
+ }).then(_ => console.log('SUCCESS'), err => console.error('ERROR: ' + err));
@@ -17,31 +17,45 @@
17
17
 
18
18
  /**
19
19
  * @fileoverview An example test that may be run using Mocha.
20
- * Usage: mocha -t 10000 selenium-webdriver/example/google_search_test.js
20
+ *
21
+ * Usage:
22
+ *
23
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
24
+ *
25
+ * You can change which browser is started with the SELENIUM_BROWSER environment
26
+ * variable:
27
+ *
28
+ * SELENIUM_BROWSER=chrome \
29
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
21
30
  */
22
31
 
23
- var webdriver = require('..'),
24
- By = webdriver.By,
25
- until = webdriver.until,
26
- test = require('../testing');
32
+ const {Builder, By, until} = require('..');
33
+ const test = require('../testing');
27
34
 
28
35
  test.describe('Google Search', function() {
29
- var driver;
36
+ let driver;
30
37
 
31
- test.before(function() {
32
- driver = new webdriver.Builder()
33
- .forBrowser('firefox')
34
- .build();
38
+ test.before(function *() {
39
+ driver = yield new Builder().forBrowser('firefox').build();
35
40
  });
36
41
 
37
- test.it('should append query to title', function() {
38
- driver.get('http://www.google.com');
39
- driver.findElement(By.name('q')).sendKeys('webdriver');
40
- driver.findElement(By.name('btnG')).click();
41
- driver.wait(until.titleIs('webdriver - Google Search'), 1000);
42
+ // You can write tests either using traditional promises.
43
+ it('works with promises', function() {
44
+ return driver.get('http://www.google.com')
45
+ .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
46
+ .then(_ => driver.findElement(By.name('btnG')).click())
47
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000));
42
48
  });
43
49
 
44
- test.after(function() {
45
- driver.quit();
50
+ // Or you can define the test as a generator function. The test will wait for
51
+ // any yielded promises to resolve before invoking the next step in the
52
+ // generator.
53
+ test.it('works with generators', function*() {
54
+ yield driver.get('http://www.google.com/ncr');
55
+ yield driver.findElement(By.name('q')).sendKeys('webdriver');
56
+ yield driver.findElement(By.name('btnG')).click();
57
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
46
58
  });
59
+
60
+ test.after(() => driver.quit());
47
61
  });
@@ -21,23 +21,15 @@
21
21
 
22
22
  'use strict';
23
23
 
24
- var webdriver = require('..'),
25
- By = webdriver.By,
26
- until = webdriver.until;
24
+ const {Builder, By, logging, until} = require('..');
27
25
 
28
- webdriver.logging.installConsoleHandler();
29
- webdriver.logging.getLogger('webdriver.http')
30
- .setLevel(webdriver.logging.Level.ALL);
26
+ logging.installConsoleHandler();
27
+ logging.getLogger('webdriver.http').setLevel(logging.Level.ALL);
31
28
 
32
- var driver = new webdriver.Builder()
33
- .forBrowser('firefox')
34
- .build();
29
+ var driver = new Builder().forBrowser('firefox').build();
35
30
 
36
- driver.get('http://www.google.com/ncr');
37
-
38
- var searchBox = driver.wait(until.elementLocated(By.name('q')), 3000);
39
- searchBox.sendKeys('webdriver');
40
-
41
- driver.findElement(By.name('btnG')).click();
42
- driver.wait(until.titleIs('webdriver - Google Search'), 1000);
43
- driver.quit();
31
+ driver.get('http://www.google.com/ncr')
32
+ .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
33
+ .then(_ => driver.findElement(By.name('btnG')).click())
34
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
35
+ .then(_ => driver.quit());
@@ -18,6 +18,9 @@
18
18
  /**
19
19
  * @fileoverview An example of starting multiple WebDriver clients that run
20
20
  * in parallel in separate control flows.
21
+ *
22
+ * This example will only work when the promise manager is enabled
23
+ * (see <https://github.com/SeleniumHQ/selenium/issues/2969>).
21
24
  */
22
25
 
23
26
  var webdriver = require('..'),
package/firefox/binary.js CHANGED
@@ -181,6 +181,15 @@ class Binary {
181
181
  this.devEdition_ = false;
182
182
  }
183
183
 
184
+ /**
185
+ * @return {(string|undefined)} The path to the Firefox executable to use, or
186
+ * `undefined` if WebDriver should attempt to locate Firefox automatically
187
+ * on the current system.
188
+ */
189
+ getExe() {
190
+ return this.exe_;
191
+ }
192
+
184
193
  /**
185
194
  * Add arguments to the command line used to start Firefox.
186
195
  * @param {...(string|!Array.<string>)} var_args Either the arguments to add
@@ -196,6 +205,14 @@ class Binary {
196
205
  }
197
206
  }
198
207
 
208
+ /**
209
+ * @return {!Array<string>} The command line arguments to use when starting
210
+ * the browser.
211
+ */
212
+ getArguments() {
213
+ return this.args_;
214
+ }
215
+
199
216
  /**
200
217
  * Specifies whether to use Firefox Developer Edition instead of the normal
201
218
  * stable channel. Setting this option has no effect if this instance was
package/firefox/index.js CHANGED
@@ -305,30 +305,6 @@ function findGeckoDriver() {
305
305
  }
306
306
 
307
307
 
308
- /**
309
- * @param {(string|!Binary)} binary .
310
- * @return {!remote.DriverService} .
311
- */
312
- function createGeckoDriverService(binary) {
313
- let exe = typeof binary === 'string' ?
314
- Promise.resolve(binary) : binary.locate();
315
-
316
- let geckoDriver = findGeckoDriver();
317
- let port = portprober.findFreePort();
318
- let marionettePort = portprober.findFreePort();
319
- return new remote.DriverService(geckoDriver, {
320
- loopback: true,
321
- port: port,
322
- args: Promise.all([exe, port, marionettePort]).then(args => {
323
- return ['-b', args[0],
324
- '--port', args[1],
325
- '--marionette-port', args[2]];
326
- })
327
- // ,stdio: 'inherit'
328
- });
329
- }
330
-
331
-
332
308
  /**
333
309
  * @param {(Profile|string)} profile The profile to prepare.
334
310
  * @param {number} port The port the FirefoxDriver should listen on.
@@ -420,29 +396,210 @@ function configureExecutor(executor) {
420
396
  }
421
397
 
422
398
 
399
+ /**
400
+ * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
401
+ * a [geckodriver](https://github.com/mozilla/geckodriver) server in a child
402
+ * process.
403
+ */
404
+ class ServiceBuilder extends remote.DriverService.Builder {
405
+ /**
406
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
407
+ * the builder will attempt to locate the geckodriver on the system PATH.
408
+ */
409
+ constructor(opt_exe) {
410
+ super(opt_exe || findGeckoDriver());
411
+ this.setLoopback(true); // Required.
412
+ }
413
+
414
+ /**
415
+ * Enables verbose logging.
416
+ *
417
+ * @param {boolean=} opt_trace Whether to enable trace-level logging. By
418
+ * default, only debug logging is enabled.
419
+ * @return {!ServiceBuilder} A self reference.
420
+ */
421
+ enableVerboseLogging(opt_trace) {
422
+ return this.addArguments(opt_trace ? '-vv' : '-v');
423
+ }
424
+
425
+ /**
426
+ * Sets the path to the executable Firefox binary that the geckodriver should
427
+ * use. If this method is not called, this builder will attempt to locate
428
+ * Firefox in the default installation location for the current platform.
429
+ *
430
+ * @param {(string|!Binary)} binary Path to the executable Firefox binary to use.
431
+ * @return {!ServiceBuilder} A self reference.
432
+ * @see Binary#locate()
433
+ */
434
+ setFirefoxBinary(binary) {
435
+ let exe = typeof binary === 'string'
436
+ ? Promise.resolve(binary) : binary.locate();
437
+ return this.addArguments('-b', exe);
438
+ }
439
+ }
440
+
441
+
442
+ /**
443
+ * @typedef {{executor: !command.Executor,
444
+ * capabilities: (!capabilities.Capabilities|
445
+ * {desired: (capabilities.Capabilities|undefined),
446
+ * required: (capabilities.Capabilities|undefined)}),
447
+ * onQuit: function(this: void): ?}}
448
+ */
449
+ var DriverSpec;
450
+
451
+
452
+ /**
453
+ * @param {(http.Executor|remote.DriverService|undefined)} executor
454
+ * @param {!capabilities.Capabilities} caps
455
+ * @param {Profile} profile
456
+ * @param {Binary} binary
457
+ * @return {DriverSpec}
458
+ */
459
+ function createGeckoDriver(executor, caps, profile, binary) {
460
+ let firefoxOptions = {};
461
+ caps.set('moz:firefoxOptions', firefoxOptions);
462
+
463
+ if (binary) {
464
+ if (binary.getExe()) {
465
+ firefoxOptions['binary'] = binary.getExe();
466
+ }
467
+
468
+ let args = binary.getArguments();
469
+ if (args.length) {
470
+ firefoxOptions['args'] = args;
471
+ }
472
+ }
473
+
474
+ if (profile) {
475
+ // If the user specified a template directory or any extensions to install,
476
+ // we need to encode the profile as a base64 string (which requires writing
477
+ // it to disk first). Otherwise, if the user just specified some custom
478
+ // preferences, we can send those directly.
479
+ if (profile.getTemplateDir() || profile.getExtensions().length) {
480
+ firefoxOptions['profile'] = profile.encode();
481
+
482
+ } else {
483
+ let prefs = profile.getPreferences();
484
+ if (Object.keys(prefs).length) {
485
+ firefoxOptions['prefs'] = prefs;
486
+ }
487
+ }
488
+ }
489
+
490
+ let sessionCaps = caps;
491
+ if (caps.has(capabilities.Capability.PROXY)) {
492
+ let proxy = normalizeProxyConfiguration(
493
+ caps.get(capabilities.Capability.PROXY));
494
+
495
+ // Marionette requires proxy settings to be specified as required
496
+ // capabilities. See mozilla/geckodriver#97
497
+ let required = new capabilities.Capabilities()
498
+ .set(capabilities.Capability.PROXY, proxy);
499
+
500
+ caps.delete(capabilities.Capability.PROXY);
501
+ sessionCaps = {required, desired: caps};
502
+ }
503
+
504
+ /** @type {!command.Executor} */
505
+ let cmdExecutor;
506
+ let onQuit = function() {};
507
+
508
+ if (executor instanceof http.Executor) {
509
+ configureExecutor(executor);
510
+ cmdExecutor = executor;
511
+ } else if (executor instanceof remote.DriverService) {
512
+ cmdExecutor = createExecutor(executor.start());
513
+ onQuit = () => executor.kill();
514
+ } else {
515
+ let builder = new ServiceBuilder();
516
+ if (binary) {
517
+ builder.setFirefoxBinary(binary);
518
+ }
519
+ let service = builder.build();
520
+ cmdExecutor = createExecutor(service.start());
521
+ onQuit = () => service.kill();
522
+ }
523
+
524
+ return {
525
+ executor: cmdExecutor,
526
+ capabilities: sessionCaps,
527
+ onQuit
528
+ };
529
+ }
530
+
531
+
532
+ /**
533
+ * @param {!capabilities.Capabilities} caps
534
+ * @param {Profile} profile
535
+ * @param {!Binary} binary
536
+ * @return {DriverSpec}
537
+ */
538
+ function createLegacyDriver(caps, profile, binary, flow) {
539
+ profile = profile || new Profile;
540
+
541
+ let freePort = portprober.findFreePort();
542
+ let preparedProfile =
543
+ freePort.then(port => prepareProfile(profile, port));
544
+ let command = preparedProfile.then(dir => binary.launch(dir));
545
+
546
+ let serverUrl = command.then(() => freePort)
547
+ .then(function(/** number */port) {
548
+ let serverUrl = url.format({
549
+ protocol: 'http',
550
+ hostname: net.getLoopbackAddress(),
551
+ port: port + '',
552
+ pathname: '/hub'
553
+ });
554
+ let ready = httpUtil.waitForServer(serverUrl, 45 * 1000);
555
+ return ready.then(() => serverUrl);
556
+ });
557
+
558
+ return {
559
+ executor: createExecutor(serverUrl),
560
+ capabilities: caps,
561
+ onQuit: function() {
562
+ return command.then(command => {
563
+ command.kill();
564
+ return preparedProfile.then(io.rmDir)
565
+ .then(() => command.result(),
566
+ () => command.result());
567
+ });
568
+ }
569
+ };
570
+ }
571
+
572
+
423
573
  /**
424
574
  * A WebDriver client for Firefox.
425
575
  */
426
576
  class Driver extends webdriver.WebDriver {
427
577
  /**
578
+ * Creates a new Firefox session.
579
+ *
428
580
  * @param {(Options|capabilities.Capabilities|Object)=} opt_config The
429
581
  * configuration options for this driver, specified as either an
430
582
  * {@link Options} or {@link capabilities.Capabilities}, or as a raw hash
431
583
  * object.
432
- * @param {promise.ControlFlow=} opt_flow The flow to
433
- * schedule commands through. Defaults to the active flow object.
434
- * @param {http.Executor=} opt_executor A pre-configured command executor to
435
- * use for communicating with an externally managed remoted end (which is
436
- * assumed to already be running). The provided executor should not be
437
- * reused with other clients as its internal command mappings will be
438
- * updated to support Firefox-specific commands.
584
+ * @param {(http.Executor|remote.DriverService)=} opt_executor Either a
585
+ * pre-configured command executor to use for communicating with an
586
+ * externally managed remote end (which is assumed to already be running),
587
+ * or the `DriverService` to use to start the geckodriver in a child
588
+ * process.
439
589
  *
440
- * _This parameter may only be used with Mozilla's GeckoDriver._
590
+ * If an executor is provided, care should e taken not to use reuse it with
591
+ * other clients as its internal command mappings will be updated to support
592
+ * Firefox-specific commands.
441
593
  *
594
+ * _This parameter may only be used with Mozilla's GeckoDriver._
595
+ *
596
+ * @param {promise.ControlFlow=} opt_flow The flow to
597
+ * schedule commands through. Defaults to the active flow object.
442
598
  * @throws {Error} If a custom command executor is provided and the driver is
443
599
  * configured to use the legacy FirefoxDriver from the Selenium project.
600
+ * @return {!Driver} A new driver instance.
444
601
  */
445
- constructor(opt_config, opt_flow, opt_executor) {
602
+ static createSession(opt_config, opt_executor, opt_flow) {
446
603
  let caps;
447
604
  if (opt_config instanceof Options) {
448
605
  caps = opt_config.toCapabilities();
@@ -462,8 +619,6 @@ class Driver extends webdriver.WebDriver {
462
619
  caps.delete(Capability.PROFILE);
463
620
  }
464
621
 
465
- let serverUrl, onQuit;
466
-
467
622
  // Users must now explicitly disable marionette to use the legacy
468
623
  // FirefoxDriver.
469
624
  let noMarionette =
@@ -471,78 +626,19 @@ class Driver extends webdriver.WebDriver {
471
626
  || /^0|false$/i.test(process.env['SELENIUM_MARIONETTE']);
472
627
  let useMarionette = !noMarionette;
473
628
 
629
+ let spec;
474
630
  if (useMarionette) {
475
- if (opt_executor) {
476
- configureExecutor(opt_executor);
477
- serverUrl = Promise.reject(Error('unexpected variable use'));
478
- onQuit = function noop() {};
479
- } else {
480
- let service = createGeckoDriverService(binary);
481
- serverUrl = service.start();
482
- onQuit = () => service.kill();
483
- }
484
-
485
- if (profile) {
486
- caps.set(Capability.PROFILE, profile.encode());
487
- }
488
-
489
- if (caps.has(capabilities.Capability.PROXY)) {
490
- let proxy = normalizeProxyConfiguration(
491
- caps.get(capabilities.Capability.PROXY));
492
-
493
- // Marionette requires proxy settings to be specified as required
494
- // capabilities. See mozilla/geckodriver#97
495
- let required = new capabilities.Capabilities()
496
- .set(capabilities.Capability.PROXY, proxy);
497
-
498
- caps.delete(capabilities.Capability.PROXY);
499
- caps = {required, desired: caps};
500
- }
631
+ spec = createGeckoDriver(opt_executor, caps, profile, binary);
501
632
  } else {
502
633
  if (opt_executor) {
503
634
  throw Error('You may not use a custom command executor with the legacy'
504
635
  + ' FirefoxDriver');
505
636
  }
506
-
507
- profile = profile || new Profile;
508
-
509
- let freePort = portprober.findFreePort();
510
- let preparedProfile =
511
- freePort.then(port => prepareProfile(profile, port));
512
- let command = preparedProfile.then(dir => binary.launch(dir));
513
-
514
- serverUrl = command.then(() => freePort)
515
- .then(function(/** number */port) {
516
- let serverUrl = url.format({
517
- protocol: 'http',
518
- hostname: net.getLoopbackAddress(),
519
- port: port + '',
520
- pathname: '/hub'
521
- });
522
- let ready = httpUtil.waitForServer(serverUrl, 45 * 1000);
523
- return ready.then(() => serverUrl);
524
- });
525
-
526
- onQuit = function() {
527
- return command.then(command => {
528
- command.kill();
529
- return preparedProfile.then(io.rmDir)
530
- .then(() => command.result(),
531
- () => command.result());
532
- });
533
- };
637
+ spec = createLegacyDriver(caps, profile, binary, opt_flow);
534
638
  }
535
639
 
536
- let executor = opt_executor || createExecutor(serverUrl);
537
- let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
538
- super(driver.getSession(), executor, driver.controlFlow());
539
-
540
- let boundQuit = this.quit.bind(this);
541
-
542
- /** @override */
543
- this.quit = function() {
544
- return boundQuit().finally(onQuit);
545
- };
640
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
641
+ spec.executor, spec.capabilities, opt_flow, this, spec.onQuit));
546
642
  }
547
643
 
548
644
  /**
@@ -556,7 +652,7 @@ class Driver extends webdriver.WebDriver {
556
652
  /**
557
653
  * Get the context that is currently in effect.
558
654
  *
559
- * @return {!promise.Promise<Context>} Current context.
655
+ * @return {!promise.Thenable<Context>} Current context.
560
656
  */
561
657
  getContext() {
562
658
  return this.schedule(
@@ -576,7 +672,7 @@ class Driver extends webdriver.WebDriver {
576
672
  *
577
673
  * Use your powers wisely.
578
674
  *
579
- * @param {!promise.Promise<void>} ctx The context to switch to.
675
+ * @param {!promise.Thenable<void>} ctx The context to switch to.
580
676
  */
581
677
  setContext(ctx) {
582
678
  return this.schedule(
@@ -595,3 +691,4 @@ exports.Context = Context;
595
691
  exports.Driver = Driver;
596
692
  exports.Options = Options;
597
693
  exports.Profile = Profile;
694
+ exports.ServiceBuilder = ServiceBuilder;
@@ -201,9 +201,6 @@ class Profile {
201
201
  /** @private {!Object} */
202
202
  this.preferences_ = {};
203
203
 
204
- Object.assign(this.preferences_, getDefaultPreferences()['mutable']);
205
- Object.assign(this.preferences_, getDefaultPreferences()['frozen']);
206
-
207
204
  /** @private {boolean} */
208
205
  this.nativeEventsEnabled_ = true;
209
206
 
@@ -217,6 +214,14 @@ class Profile {
217
214
  this.extensions_ = [];
218
215
  }
219
216
 
217
+ /**
218
+ * @return {(string|undefined)} Path to an existing Firefox profile directory
219
+ * to use as a template when writing this Profile to disk.
220
+ */
221
+ getTemplateDir() {
222
+ return this.template_;
223
+ }
224
+
220
225
  /**
221
226
  * Registers an extension to be included with this profile.
222
227
  * @param {string} extension Path to the extension to include, as either an
@@ -226,6 +231,13 @@ class Profile {
226
231
  this.extensions_.push(extension);
227
232
  }
228
233
 
234
+ /**
235
+ * @return {!Array<string>} A list of extensions to install in this profile.
236
+ */
237
+ getExtensions() {
238
+ return this.extensions_;
239
+ }
240
+
229
241
  /**
230
242
  * Sets a desired preference for this profile.
231
243
  * @param {string} key The preference key.
@@ -254,6 +266,13 @@ class Profile {
254
266
  return this.preferences_[key];
255
267
  }
256
268
 
269
+ /**
270
+ * @return {!Object} A copy of all currently configured preferences.
271
+ */
272
+ getPreferences() {
273
+ return Object.assign({}, this.preferences_);
274
+ }
275
+
257
276
  /**
258
277
  * Specifies which host the driver should listen for commands on. If not
259
278
  * specified, the driver will default to "localhost". This option should be
@@ -353,6 +372,8 @@ class Profile {
353
372
 
354
373
  // Freeze preferences for async operations.
355
374
  var prefs = {};
375
+ Object.assign(prefs, getDefaultPreferences()['mutable']);
376
+ Object.assign(prefs, getDefaultPreferences()['frozen']);
356
377
  Object.assign(prefs, this.preferences_);
357
378
 
358
379
  // Freeze extensions for async operations.