aboutsummaryrefslogtreecommitdiff
path: root/node_modules/selenium-webdriver
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-11-16 01:59:39 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-11-16 02:00:31 +0100
commitbd65bb67e25a79b019d745b7262b2008ce2adb15 (patch)
tree89e1b032103a63737f1a703e6a943832ef261704 /node_modules/selenium-webdriver
parentf91466595b651721690133f58ab37f977539e95b (diff)
downloadwallet-core-bd65bb67e25a79b019d745b7262b2008ce2adb15.tar.xz
incrementally verify denoms
The denominations are not stored in a separate object store.
Diffstat (limited to 'node_modules/selenium-webdriver')
-rw-r--r--node_modules/selenium-webdriver/CHANGES.md60
-rw-r--r--node_modules/selenium-webdriver/LICENSE2
-rw-r--r--node_modules/selenium-webdriver/README.md13
-rw-r--r--node_modules/selenium-webdriver/chrome.js47
-rw-r--r--node_modules/selenium-webdriver/edge.js16
-rw-r--r--node_modules/selenium-webdriver/example/chrome_android.js31
-rw-r--r--node_modules/selenium-webdriver/example/chrome_mobile_emulation.js34
-rw-r--r--node_modules/selenium-webdriver/example/google_search.js22
-rw-r--r--node_modules/selenium-webdriver/example/google_search_generator.js41
-rw-r--r--node_modules/selenium-webdriver/example/google_search_test.js48
-rw-r--r--node_modules/selenium-webdriver/example/logging.js26
-rw-r--r--node_modules/selenium-webdriver/example/parallel_flows.js3
-rw-r--r--node_modules/selenium-webdriver/firefox/binary.js17
-rw-r--r--node_modules/selenium-webdriver/firefox/index.js103
-rw-r--r--node_modules/selenium-webdriver/firefox/profile.js27
-rw-r--r--node_modules/selenium-webdriver/http/util.js154
-rw-r--r--node_modules/selenium-webdriver/ie.js16
-rw-r--r--node_modules/selenium-webdriver/index.js141
-rw-r--r--node_modules/selenium-webdriver/lib/actions.js28
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.sobin41262 -> 41623 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.sobin30887 -> 36934 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/firefox/webdriver.xpibin706947 -> 707042 bytes
-rw-r--r--node_modules/selenium-webdriver/lib/http.js201
-rw-r--r--node_modules/selenium-webdriver/lib/logging.js8
-rw-r--r--node_modules/selenium-webdriver/lib/promise.js927
-rw-r--r--node_modules/selenium-webdriver/lib/safari/client.js56
-rw-r--r--node_modules/selenium-webdriver/lib/session.js2
-rw-r--r--node_modules/selenium-webdriver/lib/test/build.js54
-rw-r--r--node_modules/selenium-webdriver/lib/test/data/formPage.html1
-rw-r--r--node_modules/selenium-webdriver/lib/test/fileserver.js22
-rw-r--r--node_modules/selenium-webdriver/lib/test/httpserver.js10
-rw-r--r--node_modules/selenium-webdriver/lib/test/index.js43
-rw-r--r--node_modules/selenium-webdriver/lib/webdriver.js1005
-rw-r--r--node_modules/selenium-webdriver/opera.js9
-rw-r--r--node_modules/selenium-webdriver/package.json27
-rw-r--r--node_modules/selenium-webdriver/phantomjs.js21
-rw-r--r--node_modules/selenium-webdriver/remote/index.js28
-rw-r--r--node_modules/selenium-webdriver/safari.js499
-rw-r--r--node_modules/selenium-webdriver/test/actions_test.js20
-rw-r--r--node_modules/selenium-webdriver/test/chrome/options_test.js16
-rw-r--r--node_modules/selenium-webdriver/test/cookie_test.js114
-rw-r--r--node_modules/selenium-webdriver/test/element_finding_test.js386
-rw-r--r--node_modules/selenium-webdriver/test/execute_script_test.js214
-rw-r--r--node_modules/selenium-webdriver/test/fingerprint_test.js27
-rw-r--r--node_modules/selenium-webdriver/test/firefox/firefox_test.js124
-rw-r--r--node_modules/selenium-webdriver/test/firefox/profile_test.js1
-rw-r--r--node_modules/selenium-webdriver/test/http/util_test.js48
-rw-r--r--node_modules/selenium-webdriver/test/io_test.js2
-rw-r--r--node_modules/selenium-webdriver/test/lib/http_test.js14
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_aplus_test.js92
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_error_test.js1344
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_flow_test.js3596
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_generator_test.js452
-rw-r--r--node_modules/selenium-webdriver/test/lib/promise_test.js1705
-rw-r--r--node_modules/selenium-webdriver/test/lib/until_test.js2
-rw-r--r--node_modules/selenium-webdriver/test/lib/webdriver_test.js890
-rw-r--r--node_modules/selenium-webdriver/test/logging_test.js88
-rw-r--r--node_modules/selenium-webdriver/test/net/portprober_test.js47
-rw-r--r--node_modules/selenium-webdriver/test/page_loading_test.js162
-rw-r--r--node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js36
-rw-r--r--node_modules/selenium-webdriver/test/proxy_test.js46
-rw-r--r--node_modules/selenium-webdriver/test/remote_test.js44
-rw-r--r--node_modules/selenium-webdriver/test/safari_test.js12
-rw-r--r--node_modules/selenium-webdriver/test/session_test.js21
-rw-r--r--node_modules/selenium-webdriver/test/stale_element_test.js34
-rw-r--r--node_modules/selenium-webdriver/test/tag_name_test.js12
-rw-r--r--node_modules/selenium-webdriver/test/testing/assert_test.js5
-rw-r--r--node_modules/selenium-webdriver/test/testing/index_test.js280
-rw-r--r--node_modules/selenium-webdriver/test/upload_test.js26
-rw-r--r--node_modules/selenium-webdriver/test/window_test.js154
-rw-r--r--node_modules/selenium-webdriver/testing/index.js213
71 files changed, 7196 insertions, 6773 deletions
diff --git a/node_modules/selenium-webdriver/CHANGES.md b/node_modules/selenium-webdriver/CHANGES.md
index 614f905b4..b9ac5fd22 100644
--- a/node_modules/selenium-webdriver/CHANGES.md
+++ b/node_modules/selenium-webdriver/CHANGES.md
@@ -1,3 +1,63 @@
+## v3.0.1
+
+* More API adjustments to align with native Promises
+ - Deprecated `promise.fulfilled(value)`, use `promise.Promise#resolve(value)`
+ - Deprecated `promise.rejected(reason)`, use `promise.Promise#reject(reason)`
+* When a `wait()` condition times out, the returned promise will now be
+ rejected with an `error.TimeoutError` instead of a generic `Error` object.
+* `WebDriver#wait()` will now throw a TypeError if an invalid wait condition is
+ provided.
+* Properly catch unhandled promise rejections with an action sequence (only
+ impacts when the promise manager is disabled).
+
+
+## v3.0.0
+
+* (__NOTICE__) The minimum supported version of Node is now 6.9.0 LTS
+* Removed support for the SafariDriver browser extension. This has been
+ replaced by Apple's safaridriver, which is included wtih Safari 10
+ (available on OS X El Capitan and macOS Sierra).
+
+ To use Safari 9 or older, users will have to use an older version of Selenium.
+
+* geckodriver v0.11.0 or newer is now required for Firefox.
+* Fixed potential reference errors in `selenium-webdriver/testing` when users
+ create a cycle with mocha by running with mocha's `--hook` flag.
+* Fixed `WebDriver.switchTo().activeElement()` to use the correct HTTP method
+ for compatibility with the W3C spec.
+* Update the `selenium-webdriver/firefox` module to use geckodriver's
+ "moz:firefoxOptions" dictionary for Firefox-specific configuration values.
+* Extending the `selenium-webdriver/testing` module to support tests defined
+ using generator functions.
+* The promise manager can be disabled by setting an enviornment variable:
+ `SELENIUM_PROMISE_MANAGER=0`. This is part of a larger plan to remove the
+ promise manager, as documented at
+ <https://github.com/SeleniumHQ/selenium/issues/2969>
+* When communicating with a W3C-compliant remote end, use the atoms library for
+ the `WebElement.getAttribute()` and `WebElement.isDisplayed()` commands. This
+ behavior is consistent with the java, .net, python, and ruby clients.
+
+
+### API Changes
+
+ * Removed `safari.Options#useLegacyDriver()`
+ * Reduced the API on `promise.Thenable` for compatibility with native promises:
+ - Removed `#isPending()`
+ - Removed `#cancel()`
+ - Removed `#finally()`
+ * Changed all subclasses of `webdriver.WebDriver` to overload the static
+ function `WebDriver.createSession()` instead of doing work in the
+ constructor. All constructors now inherit the base class' function signature.
+ Users are still encouraged to use the `Builder` class instead of creating
+ drivers directly.
+ * `Builder#build()` now returns a "thenable" WebDriver instance, allowing users
+ to immediately schedule commands (as before), or issue them through standard
+ promise callbacks. This is the same pattern already employed for WebElements.
+ * Removed `Builder#buildAsync()` as it was redundant with the new semantics of
+ `build()`.
+
+
+
## v3.0.0-beta-3
* Fixed a bug where the promise manager would silently drop callbacks after
diff --git a/node_modules/selenium-webdriver/LICENSE b/node_modules/selenium-webdriver/LICENSE
index d64569567..d43f2c0a4 100644
--- a/node_modules/selenium-webdriver/LICENSE
+++ b/node_modules/selenium-webdriver/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2016 Software Freedom Conservancy (SFC)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/node_modules/selenium-webdriver/README.md b/node_modules/selenium-webdriver/README.md
index 0b70aeddc..bc281d504 100644
--- a/node_modules/selenium-webdriver/README.md
+++ b/node_modules/selenium-webdriver/README.md
@@ -11,22 +11,15 @@ Selenium may be installed via npm with
npm install selenium-webdriver
You will need to download additional components to work with each of the major
-browsers. The drivers for Chrome, Firefox, Safari, PhantomJS, Opera, and
+browsers. The drivers for Chrome, Firefox, PhantomJS, Opera, and
Microsoft's IE and Edge web browsers are all standalone executables that should
be placed on your system [PATH]. Apple's safaridriver is shipped with
-Safari 10 in macOS Sierra. You will need to enable Remote Automation in
-the Develop menu of Safari 10 before testing.
+Safari 10 for OS X El Capitan and macOS Sierra. You will need to enable Remote
+Automation in the Develop menu of Safari 10 before testing.
> **NOTE:** Mozilla's [geckodriver] is only required for Firefox 47+.
> Everything you need for Firefox 38-46 is included with this package.
-> **NOTE:** Apple's [safaridriver] is preferred for testing Safari 10+.
-> To test versions of Safari prior to Safari 10, The
-> [SafariDriver.safariextz][release] browser extension should be
-> installed in your browser before using Selenium. We recommend
-> disabling the extension when using the browser without Selenium
-> or installing the extension in a profile only used for testing.
-
| Browser | Component |
| ----------------- | ---------------------------------- |
diff --git a/node_modules/selenium-webdriver/chrome.js b/node_modules/selenium-webdriver/chrome.js
index 13d7b7bbf..eb33df9ed 100644
--- a/node_modules/selenium-webdriver/chrome.js
+++ b/node_modules/selenium-webdriver/chrome.js
@@ -119,8 +119,7 @@ const fs = require('fs'),
const http = require('./http'),
io = require('./io'),
- Capabilities = require('./lib/capabilities').Capabilities,
- Capability = require('./lib/capabilities').Capability,
+ {Capabilities, Capability} = require('./lib/capabilities'),
command = require('./lib/command'),
logging = require('./lib/logging'),
promise = require('./lib/promise'),
@@ -678,34 +677,27 @@ class Options {
* Creates a new WebDriver client for Chrome.
*/
class Driver extends webdriver.WebDriver {
+
/**
- * @param {(Capabilities|Options)=} opt_config The configuration
- * options.
- * @param {remote.DriverService=} opt_service The session to use; will use
- * the {@linkplain #getDefaultService default service} by default.
- * @param {promise.ControlFlow=} opt_flow The control flow to use,
- * or {@code null} to use the currently active flow.
- * @param {http.Executor=} opt_executor A pre-configured command executor that
- * should be used to send commands to the remote end. The provided
- * executor should not be reused with other clients as its internal
- * command mappings will be updated to support Chrome-specific commands.
- *
- * You may provide either a custom executor or a driver service, but not both.
+ * Creates a new session with the ChromeDriver.
*
- * @throws {Error} if both `opt_service` and `opt_executor` are provided.
+ * @param {(Capabilities|Options)=} opt_config The configuration options.
+ * @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either
+ * a DriverService to use for the remote end, or a preconfigured executor
+ * for an externally managed endpoint. If neither is provided, the
+ * {@linkplain ##getDefaultService default service} will be used by
+ * default.
+ * @param {promise.ControlFlow=} opt_flow The control flow to use, or `null`
+ * to use the currently active flow.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_service, opt_flow, opt_executor) {
- if (opt_service && opt_executor) {
- throw Error(
- 'Either a DriverService or Executor may be provided, but not both');
- }
-
+ static createSession(opt_config, opt_serviceExecutor, opt_flow) {
let executor;
- if (opt_executor) {
- executor = opt_executor;
+ if (opt_serviceExecutor instanceof http.Executor) {
+ executor = opt_serviceExecutor;
configureExecutor(executor);
} else {
- let service = opt_service || getDefaultService();
+ let service = opt_serviceExecutor || getDefaultService();
executor = createExecutor(service.start());
}
@@ -713,9 +705,8 @@ class Driver extends webdriver.WebDriver {
opt_config instanceof Options ? opt_config.toCapabilities() :
(opt_config || Capabilities.chrome());
- let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
-
- super(driver.getSession(), executor, driver.controlFlow());
+ return /** @type {!Driver} */(
+ webdriver.WebDriver.createSession(executor, caps, opt_flow, this));
}
/**
@@ -728,7 +719,7 @@ class Driver extends webdriver.WebDriver {
/**
* Schedules a command to launch Chrome App with given ID.
* @param {string} id ID of the App to launch.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when app is launched.
*/
launchApp(id) {
diff --git a/node_modules/selenium-webdriver/edge.js b/node_modules/selenium-webdriver/edge.js
index 9685a2c21..ee9d43383 100644
--- a/node_modules/selenium-webdriver/edge.js
+++ b/node_modules/selenium-webdriver/edge.js
@@ -259,14 +259,17 @@ function getDefaultService() {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new browser session for Microsoft's Edge browser.
+ *
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
* options.
* @param {remote.DriverService=} opt_service The session to use; will use
* the {@linkplain #getDefaultService default service} by default.
* @param {promise.ControlFlow=} opt_flow The control flow to use, or
* {@code null} to use the currently active flow.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_service, opt_flow) {
+ static createSession(opt_config, opt_service, opt_flow) {
var service = opt_service || getDefaultService();
var client = service.start().then(url => new http.HttpClient(url));
var executor = new http.Executor(client);
@@ -275,15 +278,8 @@ class Driver extends webdriver.WebDriver {
opt_config instanceof Options ? opt_config.toCapabilities() :
(opt_config || capabilities.Capabilities.edge());
- var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
- super(driver.getSession(), executor, driver.controlFlow());
-
- var boundQuit = this.quit.bind(this);
-
- /** @override */
- this.quit = function() {
- return boundQuit().finally(service.kill.bind(service));
- };
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
+ executor, caps, opt_flow, this, () => service.kill()));
}
/**
diff --git a/node_modules/selenium-webdriver/example/chrome_android.js b/node_modules/selenium-webdriver/example/chrome_android.js
index 990a4c445..bc0701cf9 100644
--- a/node_modules/selenium-webdriver/example/chrome_android.js
+++ b/node_modules/selenium-webdriver/example/chrome_android.js
@@ -21,18 +21,23 @@
* AVD).
*/
-var webdriver = require('..'),
- By = webdriver.By,
- until = webdriver.until,
- chrome = require('../chrome');
+'use strict';
-var driver = new webdriver.Builder()
- .forBrowser('chrome')
- .setChromeOptions(new chrome.Options().androidChrome())
- .build();
+const {Builder, By, promise, until} = require('..');
+const {Options} = require('../chrome');
-driver.get('http://www.google.com/ncr');
-driver.findElement(By.name('q')).sendKeys('webdriver');
-driver.findElement(By.name('btnG')).click();
-driver.wait(until.titleIs('webdriver - Google Search'), 1000);
-driver.quit();
+promise.consume(function* () {
+ let driver;
+ try {
+ driver = yield new Builder()
+ .forBrowser('chrome')
+ .setChromeOptions(new Options().androidChrome())
+ .build();
+ yield driver.get('http://www.google.com/ncr');
+ yield driver.findElement(By.name('q')).sendKeys('webdriver');
+ yield driver.findElement(By.name('btnG')).click();
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ } finally {
+ yield driver && driver.quit();
+ }
+}).then(_ => console.log('SUCCESS'), err => console.error('ERROR: ' + err));
diff --git a/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js b/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
index d3081127d..790be2bcf 100644
--- a/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
+++ b/node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
@@ -20,20 +20,24 @@
* ChromeDriver.
*/
-var webdriver = require('..'),
- By = webdriver.By,
- until = webdriver.until,
- chrome = require('../chrome');
+'use strict';
+const {Builder, By, promise, until} = require('..');
+const {Options} = require('../chrome');
-var driver = new webdriver.Builder()
- .forBrowser('chrome')
- .setChromeOptions(new chrome.Options()
- .setMobileEmulation({deviceName: 'Google Nexus 5'}))
- .build();
-
-driver.get('http://www.google.com/ncr');
-driver.findElement(By.name('q')).sendKeys('webdriver');
-driver.findElement(By.name('btnG')).click();
-driver.wait(until.titleIs('webdriver - Google Search'), 1000);
-driver.quit();
+promise.consume(function* () {
+ let driver;
+ try {
+ driver = yield new Builder()
+ .forBrowser('chrome')
+ .setChromeOptions(
+ new Options().setMobileEmulation({deviceName: 'Google Nexus 5'}))
+ .build();
+ yield driver.get('http://www.google.com/ncr');
+ yield driver.findElement(By.name('q')).sendKeys('webdriver');
+ yield driver.findElement(By.name('btnG')).click();
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ } finally {
+ yield driver && driver.quit();
+ }
+}).then(_ => console.log('SUCCESS'), err => console.error('ERROR: ' + err));
diff --git a/node_modules/selenium-webdriver/example/google_search.js b/node_modules/selenium-webdriver/example/google_search.js
index 22d0d21ce..b9b821328 100644
--- a/node_modules/selenium-webdriver/example/google_search.js
+++ b/node_modules/selenium-webdriver/example/google_search.js
@@ -16,8 +16,10 @@
// under the License.
/**
- * @fileoverview An example WebDriver script. This requires the chromedriver
- * to be present on the system PATH.
+ * @fileoverview An example WebDriver script.
+ *
+ * Before running this script, ensure that Mozilla's geckodriver is present on
+ * your system PATH: <https://github.com/mozilla/geckodriver/releases>
*
* Usage:
* // Default behavior
@@ -35,16 +37,14 @@
* node selenium-webdriver/example/google_search.js
*/
-var webdriver = require('..'),
- By = webdriver.By,
- until = webdriver.until;
+const {Builder, By, until} = require('..');
-var driver = new webdriver.Builder()
+var driver = new Builder()
.forBrowser('firefox')
.build();
-driver.get('http://www.google.com/ncr');
-driver.findElement(By.name('q')).sendKeys('webdriver');
-driver.findElement(By.name('btnG')).click();
-driver.wait(until.titleIs('webdriver - Google Search'), 1000);
-driver.quit(); \ No newline at end of file
+driver.get('http://www.google.com/ncr')
+ .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
+ .then(_ => driver.findElement(By.name('btnG')).click())
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
+ .then(_ => driver.quit());
diff --git a/node_modules/selenium-webdriver/example/google_search_generator.js b/node_modules/selenium-webdriver/example/google_search_generator.js
index 983c8d84f..25df93ab9 100644
--- a/node_modules/selenium-webdriver/example/google_search_generator.js
+++ b/node_modules/selenium-webdriver/example/google_search_generator.js
@@ -18,28 +18,33 @@
/**
* @fileoverview An example WebDriver script using generator functions.
*
- * Usage: node selenium-webdriver/example/google_search_generator.js
+ * Before running this script, ensure that Mozilla's geckodriver is present on
+ * your system PATH: <https://github.com/mozilla/geckodriver/releases>
+ *
+ * Usage:
+ *
+ * node selenium-webdriver/example/google_search_generator.js
*/
-var webdriver = require('..'),
- By = webdriver.By;
+'use strict';
+
+const {Builder, By, promise, until} = require('..');
-var driver = new webdriver.Builder()
- .forBrowser('firefox')
- .build();
+promise.consume(function* () {
+ let driver;
+ try {
+ driver = yield new Builder().forBrowser('firefox').build();
-driver.get('http://www.google.com/ncr');
-driver.call(function* () {
- var query = yield driver.findElement(By.name('q'));
- query.sendKeys('webdriver');
+ yield driver.get('http://www.google.com/ncr');
- var submit = yield driver.findElement(By.name('btnG'));
- submit.click();
-});
+ let q = yield driver.findElement(By.name('q'));
+ yield q.sendKeys('webdriver');
-driver.wait(function* () {
- var title = yield driver.getTitle();
- return 'webdriver - Google Search' === title;
-}, 1000);
+ let btnG = yield driver.findElement(By.name('btnG'));
+ yield btnG.click();
-driver.quit();
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ } finally {
+ yield driver && driver.quit();
+ }
+}).then(_ => console.log('SUCCESS'), err => console.error('ERROR: ' + err));
diff --git a/node_modules/selenium-webdriver/example/google_search_test.js b/node_modules/selenium-webdriver/example/google_search_test.js
index 823e2c578..a29278258 100644
--- a/node_modules/selenium-webdriver/example/google_search_test.js
+++ b/node_modules/selenium-webdriver/example/google_search_test.js
@@ -17,31 +17,45 @@
/**
* @fileoverview An example test that may be run using Mocha.
- * Usage: mocha -t 10000 selenium-webdriver/example/google_search_test.js
+ *
+ * Usage:
+ *
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
+ *
+ * You can change which browser is started with the SELENIUM_BROWSER environment
+ * variable:
+ *
+ * SELENIUM_BROWSER=chrome \
+ * mocha -t 10000 selenium-webdriver/example/google_search_test.js
*/
-var webdriver = require('..'),
- By = webdriver.By,
- until = webdriver.until,
- test = require('../testing');
+const {Builder, By, until} = require('..');
+const test = require('../testing');
test.describe('Google Search', function() {
- var driver;
+ let driver;
- test.before(function() {
- driver = new webdriver.Builder()
- .forBrowser('firefox')
- .build();
+ test.before(function *() {
+ driver = yield new Builder().forBrowser('firefox').build();
});
- test.it('should append query to title', function() {
- driver.get('http://www.google.com');
- driver.findElement(By.name('q')).sendKeys('webdriver');
- driver.findElement(By.name('btnG')).click();
- driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ // You can write tests either using traditional promises.
+ it('works with promises', function() {
+ return driver.get('http://www.google.com')
+ .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
+ .then(_ => driver.findElement(By.name('btnG')).click())
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000));
});
- test.after(function() {
- driver.quit();
+ // Or you can define the test as a generator function. The test will wait for
+ // any yielded promises to resolve before invoking the next step in the
+ // generator.
+ test.it('works with generators', function*() {
+ yield driver.get('http://www.google.com/ncr');
+ yield driver.findElement(By.name('q')).sendKeys('webdriver');
+ yield driver.findElement(By.name('btnG')).click();
+ yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
});
+
+ test.after(() => driver.quit());
});
diff --git a/node_modules/selenium-webdriver/example/logging.js b/node_modules/selenium-webdriver/example/logging.js
index ae1d4cc2a..633ac90c2 100644
--- a/node_modules/selenium-webdriver/example/logging.js
+++ b/node_modules/selenium-webdriver/example/logging.js
@@ -21,23 +21,15 @@
'use strict';
-var webdriver = require('..'),
- By = webdriver.By,
- until = webdriver.until;
+const {Builder, By, logging, until} = require('..');
-webdriver.logging.installConsoleHandler();
-webdriver.logging.getLogger('webdriver.http')
- .setLevel(webdriver.logging.Level.ALL);
+logging.installConsoleHandler();
+logging.getLogger('webdriver.http').setLevel(logging.Level.ALL);
-var driver = new webdriver.Builder()
- .forBrowser('firefox')
- .build();
+var driver = new Builder().forBrowser('firefox').build();
-driver.get('http://www.google.com/ncr');
-
-var searchBox = driver.wait(until.elementLocated(By.name('q')), 3000);
-searchBox.sendKeys('webdriver');
-
-driver.findElement(By.name('btnG')).click();
-driver.wait(until.titleIs('webdriver - Google Search'), 1000);
-driver.quit();
+driver.get('http://www.google.com/ncr')
+ .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
+ .then(_ => driver.findElement(By.name('btnG')).click())
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
+ .then(_ => driver.quit());
diff --git a/node_modules/selenium-webdriver/example/parallel_flows.js b/node_modules/selenium-webdriver/example/parallel_flows.js
index f41692234..59ff103fb 100644
--- a/node_modules/selenium-webdriver/example/parallel_flows.js
+++ b/node_modules/selenium-webdriver/example/parallel_flows.js
@@ -18,6 +18,9 @@
/**
* @fileoverview An example of starting multiple WebDriver clients that run
* in parallel in separate control flows.
+ *
+ * This example will only work when the promise manager is enabled
+ * (see <https://github.com/SeleniumHQ/selenium/issues/2969>).
*/
var webdriver = require('..'),
diff --git a/node_modules/selenium-webdriver/firefox/binary.js b/node_modules/selenium-webdriver/firefox/binary.js
index f31440b4e..b997b480d 100644
--- a/node_modules/selenium-webdriver/firefox/binary.js
+++ b/node_modules/selenium-webdriver/firefox/binary.js
@@ -182,6 +182,15 @@ class Binary {
}
/**
+ * @return {(string|undefined)} The path to the Firefox executable to use, or
+ * `undefined` if WebDriver should attempt to locate Firefox automatically
+ * on the current system.
+ */
+ getExe() {
+ return this.exe_;
+ }
+
+ /**
* Add arguments to the command line used to start Firefox.
* @param {...(string|!Array.<string>)} var_args Either the arguments to add
* as varargs, or the arguments as an array.
@@ -197,6 +206,14 @@ class Binary {
}
/**
+ * @return {!Array<string>} The command line arguments to use when starting
+ * the browser.
+ */
+ getArguments() {
+ return this.args_;
+ }
+
+ /**
* Specifies whether to use Firefox Developer Edition instead of the normal
* stable channel. Setting this option has no effect if this instance was
* created with a path to a specific Firefox binary.
diff --git a/node_modules/selenium-webdriver/firefox/index.js b/node_modules/selenium-webdriver/firefox/index.js
index 0adc97093..4ea1702a9 100644
--- a/node_modules/selenium-webdriver/firefox/index.js
+++ b/node_modules/selenium-webdriver/firefox/index.js
@@ -440,7 +440,11 @@ class ServiceBuilder extends remote.DriverService.Builder {
/**
- * @typedef {{driver: !webdriver.WebDriver, onQuit: function()}}
+ * @typedef {{executor: !command.Executor,
+ * capabilities: (!capabilities.Capabilities|
+ * {desired: (capabilities.Capabilities|undefined),
+ * required: (capabilities.Capabilities|undefined)}),
+ * onQuit: function(this: void): ?}}
*/
var DriverSpec;
@@ -450,13 +454,37 @@ var DriverSpec;
* @param {!capabilities.Capabilities} caps
* @param {Profile} profile
* @param {Binary} binary
- * @param {(promise.ControlFlow|undefined)} flow
* @return {DriverSpec}
*/
-function createGeckoDriver(
- executor, caps, profile, binary, flow) {
+function createGeckoDriver(executor, caps, profile, binary) {
+ let firefoxOptions = {};
+ caps.set('moz:firefoxOptions', firefoxOptions);
+
+ if (binary) {
+ if (binary.getExe()) {
+ firefoxOptions['binary'] = binary.getExe();
+ }
+
+ let args = binary.getArguments();
+ if (args.length) {
+ firefoxOptions['args'] = args;
+ }
+ }
+
if (profile) {
- caps.set(Capability.PROFILE, profile.encode());
+ // If the user specified a template directory or any extensions to install,
+ // we need to encode the profile as a base64 string (which requires writing
+ // it to disk first). Otherwise, if the user just specified some custom
+ // preferences, we can send those directly.
+ if (profile.getTemplateDir() || profile.getExtensions().length) {
+ firefoxOptions['profile'] = profile.encode();
+
+ } else {
+ let prefs = profile.getPreferences();
+ if (Object.keys(prefs).length) {
+ firefoxOptions['prefs'] = prefs;
+ }
+ }
}
let sessionCaps = caps;
@@ -473,7 +501,7 @@ function createGeckoDriver(
sessionCaps = {required, desired: caps};
}
- /** @type {(command.Executor|undefined)} */
+ /** @type {!command.Executor} */
let cmdExecutor;
let onQuit = function() {};
@@ -493,12 +521,11 @@ function createGeckoDriver(
onQuit = () => service.kill();
}
- let driver =
- webdriver.WebDriver.createSession(
- /** @type {!http.Executor} */(cmdExecutor),
- sessionCaps,
- flow);
- return {driver, onQuit};
+ return {
+ executor: cmdExecutor,
+ capabilities: sessionCaps,
+ onQuit
+ };
}
@@ -506,7 +533,6 @@ function createGeckoDriver(
* @param {!capabilities.Capabilities} caps
* @param {Profile} profile
* @param {!Binary} binary
- * @param {(promise.ControlFlow|undefined)} flow
* @return {DriverSpec}
*/
function createLegacyDriver(caps, profile, binary, flow) {
@@ -529,18 +555,18 @@ function createLegacyDriver(caps, profile, binary, flow) {
return ready.then(() => serverUrl);
});
- let onQuit = function() {
- return command.then(command => {
- command.kill();
- return preparedProfile.then(io.rmDir)
- .then(() => command.result(),
- () => command.result());
- });
+ return {
+ executor: createExecutor(serverUrl),
+ capabilities: caps,
+ onQuit: function() {
+ return command.then(command => {
+ command.kill();
+ return preparedProfile.then(io.rmDir)
+ .then(() => command.result(),
+ () => command.result());
+ });
+ }
};
-
- let executor = createExecutor(serverUrl);
- let driver = webdriver.WebDriver.createSession(executor, caps, flow);
- return {driver, onQuit};
}
@@ -549,6 +575,8 @@ function createLegacyDriver(caps, profile, binary, flow) {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new Firefox session.
+ *
* @param {(Options|capabilities.Capabilities|Object)=} opt_config The
* configuration options for this driver, specified as either an
* {@link Options} or {@link capabilities.Capabilities}, or as a raw hash
@@ -569,8 +597,9 @@ class Driver extends webdriver.WebDriver {
* schedule commands through. Defaults to the active flow object.
* @throws {Error} If a custom command executor is provided and the driver is
* configured to use the legacy FirefoxDriver from the Selenium project.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_executor, opt_flow) {
+ static createSession(opt_config, opt_executor, opt_flow) {
let caps;
if (opt_config instanceof Options) {
caps = opt_config.toCapabilities();
@@ -578,7 +607,6 @@ class Driver extends webdriver.WebDriver {
caps = new capabilities.Capabilities(opt_config);
}
- let hasBinary = caps.has(Capability.BINARY);
let binary = caps.get(Capability.BINARY) || new Binary();
caps.delete(Capability.BINARY);
if (typeof binary === 'string') {
@@ -591,8 +619,6 @@ class Driver extends webdriver.WebDriver {
caps.delete(Capability.PROFILE);
}
- let serverUrl, onQuit;
-
// Users must now explicitly disable marionette to use the legacy
// FirefoxDriver.
let noMarionette =
@@ -602,12 +628,7 @@ class Driver extends webdriver.WebDriver {
let spec;
if (useMarionette) {
- spec = createGeckoDriver(
- opt_executor,
- caps,
- profile,
- hasBinary ? binary : null,
- opt_flow);
+ spec = createGeckoDriver(opt_executor, caps, profile, binary);
} else {
if (opt_executor) {
throw Error('You may not use a custom command executor with the legacy'
@@ -616,14 +637,8 @@ class Driver extends webdriver.WebDriver {
spec = createLegacyDriver(caps, profile, binary, opt_flow);
}
- super(spec.driver.getSession(),
- spec.driver.getExecutor(),
- spec.driver.controlFlow());
-
- /** @override */
- this.quit = () => {
- return super.quit().finally(spec.onQuit);
- };
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
+ spec.executor, spec.capabilities, opt_flow, this, spec.onQuit));
}
/**
@@ -637,7 +652,7 @@ class Driver extends webdriver.WebDriver {
/**
* Get the context that is currently in effect.
*
- * @return {!promise.Promise<Context>} Current context.
+ * @return {!promise.Thenable<Context>} Current context.
*/
getContext() {
return this.schedule(
@@ -657,7 +672,7 @@ class Driver extends webdriver.WebDriver {
*
* Use your powers wisely.
*
- * @param {!promise.Promise<void>} ctx The context to switch to.
+ * @param {!promise.Thenable<void>} ctx The context to switch to.
*/
setContext(ctx) {
return this.schedule(
diff --git a/node_modules/selenium-webdriver/firefox/profile.js b/node_modules/selenium-webdriver/firefox/profile.js
index b6b39086c..b7f6363f9 100644
--- a/node_modules/selenium-webdriver/firefox/profile.js
+++ b/node_modules/selenium-webdriver/firefox/profile.js
@@ -201,9 +201,6 @@ class Profile {
/** @private {!Object} */
this.preferences_ = {};
- Object.assign(this.preferences_, getDefaultPreferences()['mutable']);
- Object.assign(this.preferences_, getDefaultPreferences()['frozen']);
-
/** @private {boolean} */
this.nativeEventsEnabled_ = true;
@@ -218,6 +215,14 @@ class Profile {
}
/**
+ * @return {(string|undefined)} Path to an existing Firefox profile directory
+ * to use as a template when writing this Profile to disk.
+ */
+ getTemplateDir() {
+ return this.template_;
+ }
+
+ /**
* Registers an extension to be included with this profile.
* @param {string} extension Path to the extension to include, as either an
* unpacked extension directory or the path to a xpi file.
@@ -227,6 +232,13 @@ class Profile {
}
/**
+ * @return {!Array<string>} A list of extensions to install in this profile.
+ */
+ getExtensions() {
+ return this.extensions_;
+ }
+
+ /**
* Sets a desired preference for this profile.
* @param {string} key The preference key.
* @param {(string|number|boolean)} value The preference value.
@@ -255,6 +267,13 @@ class Profile {
}
/**
+ * @return {!Object} A copy of all currently configured preferences.
+ */
+ getPreferences() {
+ return Object.assign({}, this.preferences_);
+ }
+
+ /**
* Specifies which host the driver should listen for commands on. If not
* specified, the driver will default to "localhost". This option should be
* specified when "localhost" is not mapped to the loopback address
@@ -353,6 +372,8 @@ class Profile {
// Freeze preferences for async operations.
var prefs = {};
+ Object.assign(prefs, getDefaultPreferences()['mutable']);
+ Object.assign(prefs, getDefaultPreferences()['frozen']);
Object.assign(prefs, this.preferences_);
// Freeze extensions for async operations.
diff --git a/node_modules/selenium-webdriver/http/util.js b/node_modules/selenium-webdriver/http/util.js
index 7564ba85e..8662bed28 100644
--- a/node_modules/selenium-webdriver/http/util.js
+++ b/node_modules/selenium-webdriver/http/util.js
@@ -61,38 +61,54 @@ exports.getStatus = getStatus;
* Waits for a WebDriver server to be healthy and accepting requests.
* @param {string} url Base URL of the server to query.
* @param {number} timeout How long to wait for the server.
- * @return {!promise.Promise} A promise that will resolve when the
- * server is ready.
+ * @param {Promise=} opt_cancelToken A promise used as a cancellation signal:
+ * if resolved before the server is ready, the wait will be terminated
+ * early with a {@link promise.CancellationError}.
+ * @return {!Promise} A promise that will resolve when the server is ready, or
+ * if the wait is cancelled.
*/
-exports.waitForServer = function(url, timeout) {
- var ready = promise.defer(),
- start = Date.now();
- checkServerStatus();
- return ready.promise;
-
- function checkServerStatus() {
- return getStatus(url).then(status => ready.fulfill(status), onError);
- }
-
- function onError(e) {
- // Some servers don't support the status command. If they are able to
- // response with an error, then can consider the server ready.
- if (e instanceof error.UnsupportedOperationError) {
- ready.fulfill();
- return;
+exports.waitForServer = function(url, timeout, opt_cancelToken) {
+ return new Promise((onResolve, onReject) => {
+ let start = Date.now();
+
+ let done = false;
+ let resolve = (status) => {
+ done = true;
+ onResolve(status);
+ };
+ let reject = (err) => {
+ done = true;
+ onReject(err);
+ };
+
+ if (opt_cancelToken) {
+ opt_cancelToken.then(_ => reject(new promise.CancellationError));
}
- if (Date.now() - start > timeout) {
- ready.reject(
- Error('Timed out waiting for the WebDriver server at ' + url));
- } else {
- setTimeout(function() {
- if (ready.promise.isPending()) {
- checkServerStatus();
- }
- }, 50);
+ checkServerStatus();
+ function checkServerStatus() {
+ return getStatus(url).then(status => resolve(status), onError);
}
- }
+
+ function onError(e) {
+ // Some servers don't support the status command. If they are able to
+ // response with an error, then can consider the server ready.
+ if (e instanceof error.UnsupportedOperationError) {
+ resolve({});
+ return;
+ }
+
+ if (Date.now() - start > timeout) {
+ reject(Error('Timed out waiting for the WebDriver server at ' + url));
+ } else {
+ setTimeout(function() {
+ if (!done) {
+ checkServerStatus();
+ }
+ }, 50);
+ }
+ }
+ });
};
@@ -101,39 +117,59 @@ exports.waitForServer = function(url, timeout) {
* timeout expires.
* @param {string} url The URL to poll.
* @param {number} timeout How long to wait, in milliseconds.
- * @return {!promise.Promise} A promise that will resolve when the
- * URL responds with 2xx.
+ * @param {Promise=} opt_cancelToken A promise used as a cancellation signal:
+ * if resolved before the a 2xx response is received, the wait will be
+ * terminated early with a {@link promise.CancellationError}.
+ * @return {!Promise} A promise that will resolve when a 2xx is received from
+ * the given URL, or if the wait is cancelled.
*/
-exports.waitForUrl = function(url, timeout) {
- var client = new HttpClient(url),
- request = new HttpRequest('GET', ''),
- ready = promise.defer(),
- start = Date.now();
- testUrl();
- return ready.promise;
-
- function testUrl() {
- client.send(request).then(onResponse, onError);
- }
-
- function onError() {
- if (Date.now() - start > timeout) {
- ready.reject(Error(
- 'Timed out waiting for the URL to return 2xx: ' + url));
- } else {
- setTimeout(function() {
- if (ready.promise.isPending()) {
- testUrl();
- }
- }, 50);
+exports.waitForUrl = function(url, timeout, opt_cancelToken) {
+ return new Promise((onResolve, onReject) => {
+ let client = new HttpClient(url);
+ let request = new HttpRequest('GET', '');
+ let start = Date.now();
+
+ let done = false;
+ let resolve = () => {
+ done = true;
+ onResolve();
+ };
+ let reject = (err) => {
+ done = true;
+ onReject(err);
+ };
+
+ if (opt_cancelToken) {
+ opt_cancelToken.then(_ => reject(new promise.CancellationError));
+ }
+
+ testUrl();
+
+ function testUrl() {
+ client.send(request).then(onResponse, onError);
+ }
+
+ function onError() {
+ if (Date.now() - start > timeout) {
+ reject(Error('Timed out waiting for the URL to return 2xx: ' + url));
+ } else {
+ setTimeout(function() {
+ if (!done) {
+ testUrl();
+ }
+ }, 50);
+ }
}
- }
- function onResponse(response) {
- if (!ready.promise.isPending()) return;
- if (response.status > 199 && response.status < 300) {
- return ready.fulfill();
+ function onResponse(response) {
+ if (done) {
+ return;
+ }
+ if (response.status > 199 && response.status < 300) {
+ resolve();
+ return;
+ }
+ onError();
}
- onError();
- }
+ });
};
diff --git a/node_modules/selenium-webdriver/ie.js b/node_modules/selenium-webdriver/ie.js
index 40095a0bf..5b86fa58e 100644
--- a/node_modules/selenium-webdriver/ie.js
+++ b/node_modules/selenium-webdriver/ie.js
@@ -403,12 +403,15 @@ function createServiceFromCapabilities(capabilities) {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new session for Microsoft's Internet Explorer.
+ *
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
* options.
* @param {promise.ControlFlow=} opt_flow The control flow to use,
* or {@code null} to use the currently active flow.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_flow) {
+ static createSession(opt_config, opt_flow) {
var caps = opt_config instanceof Options ?
opt_config.toCapabilities() :
(opt_config || capabilities.Capabilities.ie());
@@ -416,16 +419,9 @@ class Driver extends webdriver.WebDriver {
var service = createServiceFromCapabilities(caps);
var client = service.start().then(url => new http.HttpClient(url));
var executor = new http.Executor(client);
- var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
-
- super(driver.getSession(), executor, driver.controlFlow());
-
- let boundQuit = this.quit.bind(this);
- /** @override */
- this.quit = function() {
- return boundQuit().finally(service.kill.bind(service));
- };
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
+ executor, caps, opt_flow, this, () => service.kill()));
}
/**
diff --git a/node_modules/selenium-webdriver/index.js b/node_modules/selenium-webdriver/index.js
index 47f825738..3cde7f396 100644
--- a/node_modules/selenium-webdriver/index.js
+++ b/node_modules/selenium-webdriver/index.js
@@ -47,6 +47,7 @@ const safari = require('./safari');
const Browser = capabilities.Browser;
const Capabilities = capabilities.Capabilities;
const Capability = capabilities.Capability;
+const Session = session.Session;
const WebDriver = webdriver.WebDriver;
@@ -94,6 +95,80 @@ function ensureFileDetectorsAreEnabled(ctor) {
/**
+ * A thenable wrapper around a {@linkplain webdriver.IWebDriver IWebDriver}
+ * instance that allows commands to be issued directly instead of having to
+ * repeatedly call `then`:
+ *
+ * let driver = new Builder().build();
+ * driver.then(d => d.get(url)); // You can do this...
+ * driver.get(url); // ...or this
+ *
+ * If the driver instance fails to resolve (e.g. the session cannot be created),
+ * every issued command will fail.
+ *
+ * @extends {webdriver.IWebDriver}
+ * @extends {promise.CancellableThenable<!webdriver.IWebDriver>}
+ * @interface
+ */
+class ThenableWebDriver {
+ /** @param {...?} args */
+ static createSession(...args) {}
+}
+
+
+/**
+ * @const {!Map<function(new: WebDriver, !IThenable<!Session>, ...?),
+ * function(new: ThenableWebDriver, !IThenable<!Session>, ...?)>}
+ */
+const THENABLE_DRIVERS = new Map;
+
+
+/**
+ * @param {function(new: WebDriver, !IThenable<!Session>, ...?)} ctor
+ * @param {...?} args
+ * @return {!ThenableWebDriver}
+ */
+function createDriver(ctor, ...args) {
+ let thenableWebDriverProxy = THENABLE_DRIVERS.get(ctor);
+ if (!thenableWebDriverProxy) {
+ /** @implements {ThenableWebDriver} */
+ thenableWebDriverProxy = class extends ctor {
+ /**
+ * @param {!IThenable<!Session>} session
+ * @param {...?} rest
+ */
+ constructor(session, ...rest) {
+ super(session, ...rest);
+
+ const pd = this.getSession().then(session => {
+ return new ctor(session, ...rest);
+ });
+
+ /**
+ * @param {(string|Error)=} opt_reason
+ * @override
+ */
+ this.cancel = function(opt_reason) {
+ if (promise.CancellableThenable.isImplementation(pd)) {
+ /** @type {!promise.CancellableThenable} */(pd).cancel(opt_reason);
+ }
+ };
+
+ /** @override */
+ this.then = pd.then.bind(pd);
+
+ /** @override */
+ this.catch = pd.then.bind(pd);
+ }
+ }
+ promise.CancellableThenable.addImplementation(thenableWebDriverProxy);
+ THENABLE_DRIVERS.set(ctor, thenableWebDriverProxy);
+ }
+ return thenableWebDriverProxy.createSession(...args);
+}
+
+
+/**
* Creates new {@link webdriver.WebDriver WebDriver} instances. The environment
* variables listed below may be used to override a builder's configuration,
* allowing quick runtime changes.
@@ -134,6 +209,9 @@ function ensureFileDetectorsAreEnabled(ctor) {
*/
class Builder {
constructor() {
+ /** @private @const */
+ this.log_ = logging.getLogger('webdriver.Builder');
+
/** @private {promise.ControlFlow} */
this.flow_ = null;
@@ -465,15 +543,14 @@ class Builder {
* Creates a new WebDriver client based on this builder's current
* configuration.
*
- * While this method will immediately return a new WebDriver instance, any
- * commands issued against it will be deferred until the associated browser
- * has been fully initialized. Users may call {@link #buildAsync()} to obtain
- * a promise that will not be fulfilled until the browser has been created
- * (the difference is purely in style).
+ * This method will return a {@linkplain ThenableWebDriver} instance, allowing
+ * users to issue commands directly without calling `then()`. The returned
+ * thenable wraps a promise that will resolve to a concrete
+ * {@linkplain webdriver.WebDriver WebDriver} instance. The promise will be
+ * rejected if the remote end fails to create a new session.
*
- * @return {!webdriver.WebDriver} A new WebDriver instance.
+ * @return {!ThenableWebDriver} A new WebDriver instance.
* @throws {Error} If the current configuration is invalid.
- * @see #buildAsync()
*/
build() {
// Create a copy for any changes we may need to make based on the current
@@ -482,6 +559,7 @@ class Builder {
var browser;
if (!this.ignoreEnv_ && process.env.SELENIUM_BROWSER) {
+ this.log_.fine(`SELENIUM_BROWSER=${process.env.SELENIUM_BROWSER}`);
browser = process.env.SELENIUM_BROWSER.split(/:/, 3);
capabilities.set(Capability.BROWSER_NAME, browser[0]);
capabilities.set(Capability.VERSION, browser[1] || null);
@@ -524,76 +602,65 @@ class Builder {
let url = this.url_;
if (!this.ignoreEnv_) {
if (process.env.SELENIUM_REMOTE_URL) {
+ this.log_.fine(
+ `SELENIUM_REMOTE_URL=${process.env.SELENIUM_REMOTE_URL}`);
url = process.env.SELENIUM_REMOTE_URL;
} else if (process.env.SELENIUM_SERVER_JAR) {
+ this.log_.fine(
+ `SELENIUM_SERVER_JAR=${process.env.SELENIUM_SERVER_JAR}`);
url = startSeleniumServer(process.env.SELENIUM_SERVER_JAR);
}
}
if (url) {
+ this.log_.fine('Creating session on remote server');
let client = Promise.resolve(url)
.then(url => new _http.HttpClient(url, this.agent_, this.proxy_));
let executor = new _http.Executor(client);
if (browser === Browser.CHROME) {
const driver = ensureFileDetectorsAreEnabled(chrome.Driver);
- return new driver(capabilities, null, this.flow_, executor);
+ return createDriver(
+ driver, capabilities, executor, this.flow_);
}
if (browser === Browser.FIREFOX) {
const driver = ensureFileDetectorsAreEnabled(firefox.Driver);
- return new driver(capabilities, executor, this.flow_);
+ return createDriver(
+ driver, capabilities, executor, this.flow_);
}
-
- return WebDriver.createSession(executor, capabilities, this.flow_);
+ return createDriver(
+ WebDriver, executor, capabilities, this.flow_);
}
// Check for a native browser.
switch (browser) {
case Browser.CHROME:
- return new chrome.Driver(capabilities, null, this.flow_);
+ return createDriver(chrome.Driver, capabilities, null, this.flow_);
case Browser.FIREFOX:
- return new firefox.Driver(capabilities, null, this.flow_);
+ return createDriver(firefox.Driver, capabilities, null, this.flow_);
case Browser.INTERNET_EXPLORER:
- return new ie.Driver(capabilities, this.flow_);
+ return createDriver(ie.Driver, capabilities, this.flow_);
case Browser.EDGE:
- return new edge.Driver(capabilities, null, this.flow_);
+ return createDriver(edge.Driver, capabilities, null, this.flow_);
case Browser.OPERA:
- return new opera.Driver(capabilities, null, this.flow_);
+ return createDriver(opera.Driver, capabilities, null, this.flow_);
case Browser.PHANTOM_JS:
- return new phantomjs.Driver(capabilities, this.flow_);
+ return createDriver(phantomjs.Driver, capabilities, this.flow_);
case Browser.SAFARI:
- return new safari.Driver(capabilities, this.flow_);
+ return createDriver(safari.Driver, capabilities, this.flow_);
default:
throw new Error('Do not know how to build driver: ' + browser
+ '; did you forget to call usingServer(url)?');
}
}
-
- /**
- * Creates a new WebDriver client based on this builder's current
- * configuration. This method returns a promise that will not be fulfilled
- * until the new browser session has been fully initialized.
- *
- * __Note:__ this method is purely a convenience wrapper around
- * {@link #build()}.
- *
- * @return {!promise.Promise<!webdriver.WebDriver>} A promise that will be
- * fulfilled with the newly created WebDriver instance once the browser
- * has been fully initialized.
- * @see #build()
- */
- buildAsync() {
- let driver = this.build();
- return driver.getSession().then(() => driver);
- }
}
@@ -612,6 +679,8 @@ exports.EventEmitter = events.EventEmitter;
exports.FileDetector = input.FileDetector;
exports.Key = input.Key;
exports.Session = session.Session;
+exports.ThenableWebDriver = ThenableWebDriver;
+exports.TouchSequence = actions.TouchSequence;
exports.WebDriver = webdriver.WebDriver;
exports.WebElement = webdriver.WebElement;
exports.WebElementCondition = webdriver.WebElementCondition;
diff --git a/node_modules/selenium-webdriver/lib/actions.js b/node_modules/selenium-webdriver/lib/actions.js
index 7200b08d6..1b059bbbf 100644
--- a/node_modules/selenium-webdriver/lib/actions.js
+++ b/node_modules/selenium-webdriver/lib/actions.js
@@ -65,9 +65,12 @@ function checkModifierKey(key) {
* Class for defining sequences of complex user interactions. Each sequence
* will not be executed until {@link #perform} is called.
*
- * Example:
+ * This class should not be instantiated directly. Instead, obtain an instance
+ * using {@link ./webdriver.WebDriver#actions() WebDriver.actions()}.
*
- * new ActionSequence(driver).
+ * Sample usage:
+ *
+ * driver.actions().
* keyDown(Key.SHIFT).
* click(element1).
* click(element2).
@@ -107,7 +110,7 @@ class ActionSequence {
/**
* Executes this action sequence.
*
- * @return {!./promise.Promise} A promise that will be resolved once
+ * @return {!./promise.Thenable} A promise that will be resolved once
* this sequence has completed.
*/
perform() {
@@ -117,9 +120,10 @@ class ActionSequence {
let actions = this.actions_.concat();
let driver = this.driver_;
return driver.controlFlow().execute(function() {
- actions.forEach(function(action) {
- driver.schedule(action.command, action.description);
+ let results = actions.map(action => {
+ return driver.schedule(action.command, action.description);
});
+ return Promise.all(results);
}, 'ActionSequence.perform');
}
@@ -377,9 +381,12 @@ class ActionSequence {
* Class for defining sequences of user touch interactions. Each sequence
* will not be executed until {@link #perform} is called.
*
- * Example:
+ * This class should not be instantiated directly. Instead, obtain an instance
+ * using {@link ./webdriver.WebDriver#touchActions() WebDriver.touchActions()}.
+ *
+ * Sample usage:
*
- * new TouchSequence(driver).
+ * driver.touchActions().
* tapAndHold({x: 0, y: 0}).
* move({x: 3, y: 4}).
* release({x: 10, y: 10}).
@@ -415,7 +422,7 @@ class TouchSequence {
/**
* Executes this action sequence.
- * @return {!./promise.Promise} A promise that will be resolved once
+ * @return {!./promise.Thenable} A promise that will be resolved once
* this sequence has completed.
*/
perform() {
@@ -425,9 +432,10 @@ class TouchSequence {
let actions = this.actions_.concat();
let driver = this.driver_;
return driver.controlFlow().execute(function() {
- actions.forEach(function(action) {
- driver.schedule(action.command, action.description);
+ let results = actions.map(action => {
+ return driver.schedule(action.command, action.description);
});
+ return Promise.all(results);
}, 'TouchSequence.perform');
}
diff --git a/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so b/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so
index 916e530f3..248c32db5 100644
--- a/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so
+++ b/node_modules/selenium-webdriver/lib/firefox/amd64/libnoblur64.so
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so b/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so
index 8e7db8de3..004062c7b 100644
--- a/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so
+++ b/node_modules/selenium-webdriver/lib/firefox/i386/libnoblur.so
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi b/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi
index 39aca6b62..f9a51cf4f 100644
--- a/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi
+++ b/node_modules/selenium-webdriver/lib/firefox/webdriver.xpi
Binary files differ
diff --git a/node_modules/selenium-webdriver/lib/http.js b/node_modules/selenium-webdriver/lib/http.js
index a5675f81f..68bc43213 100644
--- a/node_modules/selenium-webdriver/lib/http.js
+++ b/node_modules/selenium-webdriver/lib/http.js
@@ -25,7 +25,11 @@
'use strict';
+const fs = require('fs');
+const path = require('path');
+
const cmd = require('./command');
+const devmode = require('./devmode');
const error = require('./error');
const logging = require('./logging');
const promise = require('./promise');
@@ -110,13 +114,77 @@ class Response {
}
+const DEV_ROOT = '../../../../buck-out/gen/javascript/';
+
+/** @enum {string} */
+const Atom = {
+ GET_ATTRIBUTE: devmode
+ ? path.join(__dirname, DEV_ROOT, 'webdriver/atoms/getAttribute.js')
+ : path.join(__dirname, 'atoms/getAttribute.js'),
+ IS_DISPLAYED: devmode
+ ? path.join(__dirname, DEV_ROOT, 'atoms/fragments/is-displayed.js')
+ : path.join(__dirname, 'atoms/isDisplayed.js'),
+};
+
+
+const ATOMS = /** !Map<string, !Promise<string>> */new Map();
+const LOG = logging.getLogger('webdriver.http');
+
+/**
+ * @param {Atom} file The atom file to load.
+ * @return {!Promise<string>} A promise that will resolve to the contents of the
+ * file.
+ */
+function loadAtom(file) {
+ if (ATOMS.has(file)) {
+ return ATOMS.get(file);
+ }
+ let contents = /** !Promise<string> */new Promise((resolve, reject) => {
+ LOG.finest(() => `Loading atom ${file}`);
+ fs.readFile(file, 'utf8', function(err, data) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+ ATOMS.set(file, contents);
+ return contents;
+}
+
+
function post(path) { return resource('POST', path); }
function del(path) { return resource('DELETE', path); }
function get(path) { return resource('GET', path); }
function resource(method, path) { return {method: method, path: path}; }
-/** @const {!Map<string, {method: string, path: string}>} */
+/** @typedef {{method: string, path: string}} */
+var CommandSpec;
+
+
+/** @typedef {function(!cmd.Command): !Promise<!cmd.Command>} */
+var CommandTransformer;
+
+
+/**
+ * @param {!cmd.Command} command The initial command.
+ * @param {Atom} atom The name of the atom to execute.
+ * @return {!Promise<!cmd.Command>} The transformed command to execute.
+ */
+function toExecuteAtomCommand(command, atom, ...params) {
+ return loadAtom(atom).then(atom => {
+ return new cmd.Command(cmd.Name.EXECUTE_SCRIPT)
+ .setParameter('sessionId', command.getParameter('sessionId'))
+ .setParameter('script', `return (${atom}).apply(null, arguments)`)
+ .setParameter('args', params.map(param => command.getParameter(param)));
+ });
+}
+
+
+
+/** @const {!Map<string, CommandSpec>} */
const COMMAND_MAP = new Map([
[cmd.Name.GET_SERVER_STATUS, get('/status')],
[cmd.Name.NEW_SESSION, post('/session')],
@@ -195,8 +263,15 @@ const COMMAND_MAP = new Map([
]);
-/** @const {!Map<string, {method: string, path: string}>} */
+/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
const W3C_COMMAND_MAP = new Map([
+ [cmd.Name.GET_ACTIVE_ELEMENT, get('/session/:sessionId/element/active')],
+ [cmd.Name.GET_ELEMENT_ATTRIBUTE, (cmd) => {
+ return toExecuteAtomCommand(cmd, Atom.GET_ATTRIBUTE, 'id', 'name');
+ }],
+ [cmd.Name.IS_ELEMENT_DISPLAYED, (cmd) => {
+ return toExecuteAtomCommand(cmd, Atom.IS_DISPLAYED, 'id');
+ }],
[cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/maximize')],
[cmd.Name.GET_WINDOW_POSITION, get('/session/:sessionId/window/position')],
[cmd.Name.SET_WINDOW_POSITION, post('/session/:sessionId/window/position')],
@@ -249,6 +324,53 @@ function doSend(executor, request) {
/**
+ * @param {Map<string, CommandSpec>} customCommands
+ * A map of custom command definitions.
+ * @param {boolean} w3c Whether to use W3C command mappings.
+ * @param {!cmd.Command} command The command to resolve.
+ * @return {!Promise<!Request>} A promise that will resolve with the
+ * command to execute.
+ */
+function buildRequest(customCommands, w3c, command) {
+ LOG.finest(() => `Translating command: ${command.getName()}`);
+ let spec = customCommands && customCommands.get(command.getName());
+ if (spec) {
+ return toHttpRequest(spec);
+ }
+
+ if (w3c) {
+ spec = W3C_COMMAND_MAP.get(command.getName());
+ if (typeof spec === 'function') {
+ LOG.finest(() => `Transforming command for W3C: ${command.getName()}`);
+ return spec(command)
+ .then(newCommand => buildRequest(customCommands, w3c, newCommand));
+ } else if (spec) {
+ return toHttpRequest(spec);
+ }
+ }
+
+ spec = COMMAND_MAP.get(command.getName());
+ if (spec) {
+ return toHttpRequest(spec);
+ }
+ return Promise.reject(
+ new error.UnknownCommandError(
+ 'Unrecognized command: ' + command.getName()));
+
+ /**
+ * @param {CommandSpec} resource
+ * @return {!Promise<!Request>}
+ */
+ function toHttpRequest(resource) {
+ LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`);
+ let parameters = command.getParameters();
+ let path = buildPath(resource.path, parameters);
+ return Promise.resolve(new Request(resource.method, path, parameters));
+ }
+}
+
+
+/**
* A command executor that communicates with the server using JSON over HTTP.
*
* By default, each instance of this class will use the legacy wire protocol
@@ -279,7 +401,7 @@ class Executor {
*/
this.w3c = false;
- /** @private {Map<string, {method: string, path: string}>} */
+ /** @private {Map<string, CommandSpec>} */
this.customCommands_ = null;
/** @private {!logging.Logger} */
@@ -308,51 +430,40 @@ class Executor {
/** @override */
execute(command) {
- let resource =
- (this.customCommands_ && this.customCommands_.get(command.getName()))
- || (this.w3c && W3C_COMMAND_MAP.get(command.getName()))
- || COMMAND_MAP.get(command.getName());
- if (!resource) {
- throw new error.UnknownCommandError(
- 'Unrecognized command: ' + command.getName());
- }
-
- let parameters = command.getParameters();
- let path = buildPath(resource.path, parameters);
- let request = new Request(resource.method, path, parameters);
-
- let log = this.log_;
- log.finer(() => '>>>\n' + request);
- return doSend(this, request).then(response => {
- log.finer(() => '<<<\n' + response);
-
- let parsed =
- parseHttpResponse(/** @type {!Response} */ (response), this.w3c);
-
- if (command.getName() === cmd.Name.NEW_SESSION
- || command.getName() === cmd.Name.DESCRIBE_SESSION) {
- if (!parsed || !parsed['sessionId']) {
- throw new error.WebDriverError(
- 'Unable to parse new session response: ' + response.body);
+ let request = buildRequest(this.customCommands_, this.w3c, command);
+ return request.then(request => {
+ this.log_.finer(() => `>>> ${request.method} ${request.path}`);
+ return doSend(this, request).then(response => {
+ this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`);
+
+ let parsed =
+ parseHttpResponse(/** @type {!Response} */ (response), this.w3c);
+
+ if (command.getName() === cmd.Name.NEW_SESSION
+ || command.getName() === cmd.Name.DESCRIBE_SESSION) {
+ if (!parsed || !parsed['sessionId']) {
+ throw new error.WebDriverError(
+ 'Unable to parse new session response: ' + response.body);
+ }
+
+ // The remote end is a W3C compliant server if there is no `status`
+ // field in the response. This is not appliable for the DESCRIBE_SESSION
+ // command, which is not defined in the W3C spec.
+ if (command.getName() === cmd.Name.NEW_SESSION) {
+ this.w3c = this.w3c || !('status' in parsed);
+ }
+
+ return new Session(parsed['sessionId'], parsed['value']);
}
- // The remote end is a W3C compliant server if there is no `status`
- // field in the response. This is not appliable for the DESCRIBE_SESSION
- // command, which is not defined in the W3C spec.
- if (command.getName() === cmd.Name.NEW_SESSION) {
- this.w3c = this.w3c || !('status' in parsed);
+ if (parsed
+ && typeof parsed === 'object'
+ && 'value' in parsed) {
+ let value = parsed['value'];
+ return typeof value === 'undefined' ? null : value;
}
-
- return new Session(parsed['sessionId'], parsed['value']);
- }
-
- if (parsed
- && typeof parsed === 'object'
- && 'value' in parsed) {
- let value = parsed['value'];
- return typeof value === 'undefined' ? null : value;
- }
- return parsed;
+ return parsed;
+ });
});
}
}
diff --git a/node_modules/selenium-webdriver/lib/logging.js b/node_modules/selenium-webdriver/lib/logging.js
index 97f54924c..634a5cbc8 100644
--- a/node_modules/selenium-webdriver/lib/logging.js
+++ b/node_modules/selenium-webdriver/lib/logging.js
@@ -521,8 +521,14 @@ function getLogger(name) {
}
+/**
+ * Pads a number to ensure it has a minimum of two digits.
+ *
+ * @param {number} n the number to be padded.
+ * @return {string} the padded number.
+ */
function pad(n) {
- if (n > 10) {
+ if (n >= 10) {
return '' + n;
} else {
return '0' + n;
diff --git a/node_modules/selenium-webdriver/lib/promise.js b/node_modules/selenium-webdriver/lib/promise.js
index b98e7cf2e..32d0c98e6 100644
--- a/node_modules/selenium-webdriver/lib/promise.js
+++ b/node_modules/selenium-webdriver/lib/promise.js
@@ -17,6 +17,42 @@
/**
* @fileoverview
+ *
+ * > ### IMPORTANT NOTICE
+ * >
+ * > The promise manager contained in this module is in the process of being
+ * > phased out in favor of native JavaScript promises. This will be a long
+ * > process and will not be completed until there have been two major LTS Node
+ * > releases (approx. Node v10.0) that support
+ * > [async functions](https://tc39.github.io/ecmascript-asyncawait/).
+ * >
+ * > At this time, the promise manager can be disabled by setting an environment
+ * > variable, `SELENIUM_PROMISE_MANAGER=0`. In the absence of async functions,
+ * > users may use generators with the
+ * > {@link ./promise.consume promise.consume()} function to write "synchronous"
+ * > style tests:
+ * >
+ * > ```js
+ * > const {Builder, By, promise, until} = require('selenium-webdriver');
+ * >
+ * > let result = promise.consume(function* doGoogleSearch() {
+ * > let driver = new Builder().forBrowser('firefox').build();
+ * > yield driver.get('http://www.google.com/ncr');
+ * > yield driver.findElement(By.name('q')).sendKeys('webdriver');
+ * > yield driver.findElement(By.name('btnG')).click();
+ * > yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
+ * > yield driver.quit();
+ * > });
+ * >
+ * > result.then(_ => console.log('SUCCESS!'),
+ * > e => console.error('FAILURE: ' + e));
+ * > ```
+ * >
+ * > The motiviation behind this change and full deprecation plan are documented
+ * > in [issue 2969](https://github.com/SeleniumHQ/selenium/issues/2969).
+ * >
+ * >
+ *
* The promise module is centered around the {@linkplain ControlFlow}, a class
* that coordinates the execution of asynchronous tasks. The ControlFlow allows
* users to focus on the imperative commands for their script without worrying
@@ -592,6 +628,7 @@
'use strict';
+const error = require('./error');
const events = require('./events');
const logging = require('./logging');
@@ -643,7 +680,6 @@ function asyncRun(fn) {
});
}
-
/**
* @param {number} level What level of verbosity to log with.
* @param {(string|function(this: T): string)} loggable The message to log.
@@ -799,32 +835,56 @@ class MultipleUnhandledRejectionError extends Error {
* @const
*/
const IMPLEMENTED_BY_SYMBOL = Symbol('promise.Thenable');
+const CANCELLABLE_SYMBOL = Symbol('promise.CancellableThenable');
+
+
+/**
+ * @param {function(new: ?)} ctor
+ * @param {!Object} symbol
+ */
+function addMarkerSymbol(ctor, symbol) {
+ try {
+ ctor.prototype[symbol] = true;
+ } catch (ignored) {
+ // Property access denied?
+ }
+}
+
+
+/**
+ * @param {*} object
+ * @param {!Object} symbol
+ * @return {boolean}
+ */
+function hasMarkerSymbol(object, symbol) {
+ if (!object) {
+ return false;
+ }
+ try {
+ return !!object[symbol];
+ } catch (e) {
+ return false; // Property access seems to be forbidden.
+ }
+}
/**
* Thenable is a promise-like object with a {@code then} method which may be
* used to schedule callbacks on a promised value.
*
- * @interface
+ * @record
* @extends {IThenable<T>}
* @template T
*/
class Thenable {
/**
* Adds a property to a class prototype to allow runtime checks of whether
- * instances of that class implement the Thenable interface. This function
- * will also ensure the prototype's {@code then} function is exported from
- * compiled code.
+ * instances of that class implement the Thenable interface.
* @param {function(new: Thenable, ...?)} ctor The
* constructor whose prototype to modify.
*/
static addImplementation(ctor) {
- ctor.prototype['then'] = ctor.prototype.then;
- try {
- ctor.prototype[IMPLEMENTED_BY_SYMBOL] = true;
- } catch (ignored) {
- // Property access denied?
- }
+ addMarkerSymbol(ctor, IMPLEMENTED_BY_SYMBOL);
}
/**
@@ -835,30 +895,10 @@ class Thenable {
* interface.
*/
static isImplementation(object) {
- if (!object) {
- return false;
- }
- try {
- return !!object[IMPLEMENTED_BY_SYMBOL];
- } catch (e) {
- return false; // Property access seems to be forbidden.
- }
+ return hasMarkerSymbol(object, IMPLEMENTED_BY_SYMBOL);
}
/**
- * Cancels the computation of this promise's value, rejecting the promise in
- * the process. This method is a no-op if the promise has already been
- * resolved.
- *
- * @param {(string|Error)=} opt_reason The reason this promise is being
- * cancelled. This value will be wrapped in a {@link CancellationError}.
- */
- cancel(opt_reason) {}
-
- /** @return {boolean} Whether this promise's value is still being computed. */
- isPending() {}
-
- /**
* Registers listeners for when this instance is resolved.
*
* @param {?(function(T): (R|IThenable<R>))=} opt_callback The
@@ -867,8 +907,8 @@ class Thenable {
* @param {?(function(*): (R|IThenable<R>))=} opt_errback
* The function to call if this promise is rejected. The function should
* expect a single argument: the rejection reason.
- * @return {!ManagedPromise<R>} A new promise which will be
- * resolved with the result of the invoked callback.
+ * @return {!Thenable<R>} A new promise which will be resolved with the result
+ * of the invoked callback.
* @template R
*/
then(opt_callback, opt_errback) {}
@@ -892,49 +932,53 @@ class Thenable {
* @param {function(*): (R|IThenable<R>)} errback The
* function to call if this promise is rejected. The function should
* expect a single argument: the rejection reason.
- * @return {!ManagedPromise<R>} A new promise which will be
- * resolved with the result of the invoked callback.
+ * @return {!Thenable<R>} A new promise which will be resolved with the result
+ * of the invoked callback.
* @template R
*/
catch(errback) {}
+}
+
+/**
+ * Marker interface for objects that allow consumers to request the cancellation
+ * of a promies-based operation. A cancelled promise will be rejected with a
+ * {@link CancellationError}.
+ *
+ * This interface is considered package-private and should not be used outside
+ * of selenium-webdriver.
+ *
+ * @interface
+ * @extends {Thenable<T>}
+ * @template T
+ * @package
+ */
+class CancellableThenable {
/**
- * Registers a listener to invoke when this promise is resolved, regardless
- * of whether the promise's value was successfully computed. This function
- * is synonymous with the {@code finally} clause in a synchronous API:
- *
- * // Synchronous API:
- * try {
- * doSynchronousWork();
- * } finally {
- * cleanUp();
- * }
- *
- * // Asynchronous promise API:
- * doAsynchronousWork().finally(cleanUp);
- *
- * __Note:__ similar to the {@code finally} clause, if the registered
- * callback returns a rejected promise or throws an error, it will silently
- * replace the rejection error (if any) from this promise:
- *
- * try {
- * throw Error('one');
- * } finally {
- * throw Error('two'); // Hides Error: one
- * }
- *
- * promise.rejected(Error('one'))
- * .finally(function() {
- * throw Error('two'); // Hides Error: one
- * });
+ * @param {function(new: CancellableThenable, ...?)} ctor
+ */
+ static addImplementation(ctor) {
+ Thenable.addImplementation(ctor);
+ addMarkerSymbol(ctor, CANCELLABLE_SYMBOL);
+ }
+
+ /**
+ * @param {*} object
+ * @return {boolean}
+ */
+ static isImplementation(object) {
+ return hasMarkerSymbol(object, CANCELLABLE_SYMBOL);
+ }
+
+ /**
+ * Requests the cancellation of the computation of this promise's value,
+ * rejecting the promise in the process. This method is a no-op if the promise
+ * has already been resolved.
*
- * @param {function(): (R|IThenable<R>)} callback The function to call when
- * this promise is resolved.
- * @return {!ManagedPromise<R>} A promise that will be fulfilled
- * with the callback result.
- * @template R
+ * @param {(string|Error)=} opt_reason The reason this promise is being
+ * cancelled. This value will be wrapped in a {@link CancellationError}.
*/
- finally(callback) {}
+ cancel(opt_reason) {}
}
@@ -967,7 +1011,7 @@ const ON_CANCEL_HANDLER = new WeakMap;
* fulfilled or rejected state, at which point the promise is considered
* resolved.
*
- * @implements {Thenable<T>}
+ * @implements {CancellableThenable<T>}
* @template T
* @see http://promises-aplus.github.io/promises-spec/
*/
@@ -983,6 +1027,12 @@ class ManagedPromise {
* this instance was created under. Defaults to the currently active flow.
*/
constructor(resolver, opt_flow) {
+ if (!usePromiseManager()) {
+ throw TypeError(
+ 'Unable to create a managed promise instance: the promise manager has'
+ + ' been disabled by the SELENIUM_PROMISE_MANAGER environment'
+ + ' variable: ' + process.env['SELENIUM_PROMISE_MANAGER']);
+ }
getUid(this);
/** @private {!ControlFlow} */
@@ -1024,6 +1074,30 @@ class ManagedPromise {
}
}
+ /**
+ * Creates a promise that is immediately resolved with the given value.
+ *
+ * @param {T=} opt_value The value to resolve.
+ * @return {!ManagedPromise<T>} A promise resolved with the given value.
+ * @template T
+ */
+ static resolve(opt_value) {
+ if (opt_value instanceof ManagedPromise) {
+ return opt_value;
+ }
+ return new ManagedPromise(resolve => resolve(opt_value));
+ }
+
+ /**
+ * Creates a promise that is immediately rejected with the given reason.
+ *
+ * @param {*=} opt_reason The rejection reason.
+ * @return {!ManagedPromise<?>} A new rejected promise.
+ */
+ static reject(opt_reason) {
+ return new ManagedPromise((_, reject) => reject(opt_reason));
+ }
+
/** @override */
toString() {
return 'ManagedPromise::' + getUid(this) +
@@ -1176,7 +1250,7 @@ class ManagedPromise {
}
if (this.parent_ && canCancel(this.parent_)) {
- this.parent_.cancel(opt_reason);
+ /** @type {!CancellableThenable} */(this.parent_).cancel(opt_reason);
} else {
var reason = CancellationError.wrap(opt_reason);
let onCancel = ON_CANCEL_HANDLER.get(this);
@@ -1194,7 +1268,7 @@ class ManagedPromise {
function canCancel(promise) {
if (!(promise instanceof ManagedPromise)) {
- return Thenable.isImplementation(promise);
+ return CancellableThenable.isImplementation(promise);
}
return promise.state_ === PromiseState.PENDING
|| promise.state_ === PromiseState.BLOCKED;
@@ -1202,11 +1276,6 @@ class ManagedPromise {
}
/** @override */
- isPending() {
- return this.state_ === PromiseState.PENDING;
- }
-
- /** @override */
then(opt_callback, opt_errback) {
return this.addCallback_(
opt_callback, opt_errback, 'then', ManagedPromise.prototype.then);
@@ -1218,21 +1287,15 @@ class ManagedPromise {
null, errback, 'catch', ManagedPromise.prototype.catch);
}
- /** @override */
+ /**
+ * @param {function(): (R|IThenable<R>)} callback
+ * @return {!ManagedPromise<R>}
+ * @template R
+ * @see ./promise.finally()
+ */
finally(callback) {
- var error;
- var mustThrow = false;
- return this.then(function() {
- return callback();
- }, function(err) {
- error = err;
- mustThrow = true;
- return callback();
- }).then(function() {
- if (mustThrow) {
- throw error;
- }
- });
+ let result = thenFinally(this, callback);
+ return /** @type {!ManagedPromise} */(result);
}
/**
@@ -1308,7 +1371,16 @@ class ManagedPromise {
}
}
}
-Thenable.addImplementation(ManagedPromise);
+CancellableThenable.addImplementation(ManagedPromise);
+
+
+/**
+ * @param {!ManagedPromise} promise
+ * @return {boolean}
+ */
+function isPending(promise) {
+ return promise.state_ === PromiseState.PENDING;
+}
/**
@@ -1405,19 +1477,11 @@ function isPromise(value) {
* Creates a promise that will be resolved at a set time in the future.
* @param {number} ms The amount of time, in milliseconds, to wait before
* resolving the promise.
- * @return {!ManagedPromise} The promise.
+ * @return {!Thenable} The promise.
*/
function delayed(ms) {
- var key;
- return new ManagedPromise(function(fulfill) {
- key = setTimeout(function() {
- key = null;
- fulfill();
- }, ms);
- }).catch(function(e) {
- clearTimeout(key);
- key = null;
- throw e;
+ return createPromise(resolve => {
+ setTimeout(() => resolve(), ms);
});
}
@@ -1436,15 +1500,11 @@ function defer() {
* Creates a promise that has been resolved with the given value.
* @param {T=} opt_value The resolved value.
* @return {!ManagedPromise<T>} The resolved promise.
+ * @deprecated Use {@link ManagedPromise#resolve Promise.resolve(value)}.
* @template T
*/
function fulfilled(opt_value) {
- if (opt_value instanceof ManagedPromise) {
- return opt_value;
- }
- return new ManagedPromise(function(fulfill) {
- fulfill(opt_value);
- });
+ return ManagedPromise.resolve(opt_value);
}
@@ -1452,16 +1512,11 @@ function fulfilled(opt_value) {
* Creates a promise that has been rejected with the given reason.
* @param {*=} opt_reason The rejection reason; may be any value, but is
* usually an Error or a string.
- * @return {!ManagedPromise<T>} The rejected promise.
- * @template T
+ * @return {!ManagedPromise<?>} The rejected promise.
+ * @deprecated Use {@link ManagedPromise#reject Promise.reject(reason)}.
*/
function rejected(opt_reason) {
- if (opt_reason instanceof ManagedPromise) {
- return opt_reason;
- }
- return new ManagedPromise(function(_, reject) {
- reject(opt_reason);
- });
+ return ManagedPromise.reject(opt_reason);
}
@@ -1474,12 +1529,12 @@ function rejected(opt_reason) {
* @param {!Function} fn The function to wrap.
* @param {...?} var_args The arguments to apply to the function, excluding the
* final callback.
- * @return {!ManagedPromise} A promise that will be resolved with the
+ * @return {!Thenable} A promise that will be resolved with the
* result of the provided function's callback.
*/
function checkedNodeCall(fn, var_args) {
let args = Array.prototype.slice.call(arguments, 1);
- return new ManagedPromise(function(fulfill, reject) {
+ return createPromise(function(fulfill, reject) {
try {
args.push(function(error, value) {
error ? reject(error) : fulfill(value);
@@ -1491,6 +1546,59 @@ function checkedNodeCall(fn, var_args) {
});
}
+/**
+ * Registers a listener to invoke when a promise is resolved, regardless
+ * of whether the promise's value was successfully computed. This function
+ * is synonymous with the {@code finally} clause in a synchronous API:
+ *
+ * // Synchronous API:
+ * try {
+ * doSynchronousWork();
+ * } finally {
+ * cleanUp();
+ * }
+ *
+ * // Asynchronous promise API:
+ * doAsynchronousWork().finally(cleanUp);
+ *
+ * __Note:__ similar to the {@code finally} clause, if the registered
+ * callback returns a rejected promise or throws an error, it will silently
+ * replace the rejection error (if any) from this promise:
+ *
+ * try {
+ * throw Error('one');
+ * } finally {
+ * throw Error('two'); // Hides Error: one
+ * }
+ *
+ * let p = Promise.reject(Error('one'));
+ * promise.finally(p, function() {
+ * throw Error('two'); // Hides Error: one
+ * });
+ *
+ * @param {!IThenable<?>} promise The promise to add the listener to.
+ * @param {function(): (R|IThenable<R>)} callback The function to call when
+ * the promise is resolved.
+ * @return {!IThenable<R>} A promise that will be resolved with the callback
+ * result.
+ * @template R
+ */
+function thenFinally(promise, callback) {
+ let error;
+ let mustThrow = false;
+ return promise.then(function() {
+ return callback();
+ }, function(err) {
+ error = err;
+ mustThrow = true;
+ return callback();
+ }).then(function() {
+ if (mustThrow) {
+ throw error;
+ }
+ });
+}
+
/**
* Registers an observer on a promised {@code value}, returning a new promise
@@ -1501,16 +1609,15 @@ function checkedNodeCall(fn, var_args) {
* resolved successfully.
* @param {Function=} opt_errback The function to call when the value is
* rejected.
- * @return {!ManagedPromise} A new promise.
+ * @return {!Thenable} A new promise.
*/
function when(value, opt_callback, opt_errback) {
if (Thenable.isImplementation(value)) {
return value.then(opt_callback, opt_errback);
}
- return new ManagedPromise(function(fulfill) {
- fulfill(value);
- }).then(opt_callback, opt_errback);
+ return createPromise(resolve => resolve(value))
+ .then(opt_callback, opt_errback);
}
@@ -1542,14 +1649,14 @@ function asap(value, callback, opt_errback) {
*
* @param {!Array<(T|!ManagedPromise<T>)>} arr An array of
* promises to wait on.
- * @return {!ManagedPromise<!Array<T>>} A promise that is
+ * @return {!Thenable<!Array<T>>} A promise that is
* fulfilled with an array containing the fulfilled values of the
* input array, or rejected with the same reason as the first
* rejected value.
* @template T
*/
function all(arr) {
- return new ManagedPromise(function(fulfill, reject) {
+ return createPromise(function(fulfill, reject) {
var n = arr.length;
var values = [];
@@ -1603,12 +1710,12 @@ function all(arr) {
* @template TYPE, SELF
*/
function map(arr, fn, opt_self) {
- return fulfilled(arr).then(function(v) {
+ return createPromise(resolve => resolve(arr)).then(v => {
if (!Array.isArray(v)) {
throw TypeError('not an array');
}
var arr = /** @type {!Array} */(v);
- return new ManagedPromise(function(fulfill, reject) {
+ return createPromise(function(fulfill, reject) {
var n = arr.length;
var values = new Array(n);
(function processNext(i) {
@@ -1661,12 +1768,12 @@ function map(arr, fn, opt_self) {
* @template TYPE, SELF
*/
function filter(arr, fn, opt_self) {
- return fulfilled(arr).then(function(v) {
+ return createPromise(resolve => resolve(arr)).then(v => {
if (!Array.isArray(v)) {
throw TypeError('not an array');
}
var arr = /** @type {!Array} */(v);
- return new ManagedPromise(function(fulfill, reject) {
+ return createPromise(function(fulfill, reject) {
var n = arr.length;
var values = [];
var valuesLength = 0;
@@ -1714,7 +1821,7 @@ function filter(arr, fn, opt_self) {
* promise.fullyResolved(value); // Stack overflow.
*
* @param {*} value The value to fully resolve.
- * @return {!ManagedPromise} A promise for a fully resolved version
+ * @return {!Thenable} A promise for a fully resolved version
* of the input value.
*/
function fullyResolved(value) {
@@ -1728,7 +1835,7 @@ function fullyResolved(value) {
/**
* @param {*} value The value to fully resolve. If a promise, assumed to
* already be resolved.
- * @return {!ManagedPromise} A promise for a fully resolved version
+ * @return {!Thenable} A promise for a fully resolved version
* of the input value.
*/
function fullyResolveValue(value) {
@@ -1755,13 +1862,13 @@ function fullyResolveValue(value) {
return fullyResolveKeys(/** @type {!Object} */ (value));
}
- return fulfilled(value);
+ return createPromise(resolve => resolve(value));
}
/**
* @param {!(Array|Object)} obj the object to resolve.
- * @return {!ManagedPromise} A promise that will be resolved with the
+ * @return {!Thenable} A promise that will be resolved with the
* input object once all of its values have been fully resolved.
*/
function fullyResolveKeys(obj) {
@@ -1773,8 +1880,9 @@ function fullyResolveKeys(obj) {
}
return n;
})();
+
if (!numKeys) {
- return fulfilled(obj);
+ return createPromise(resolve => resolve(obj));
}
function forEachProperty(obj, fn) {
@@ -1788,7 +1896,7 @@ function fullyResolveKeys(obj) {
}
var numResolved = 0;
- return new ManagedPromise(function(fulfill, reject) {
+ return createPromise(function(fulfill, reject) {
var forEachKey = isArray ? forEachElement: forEachProperty;
forEachKey(obj, function(partialValue, key) {
@@ -1822,6 +1930,236 @@ function fullyResolveKeys(obj) {
//////////////////////////////////////////////////////////////////////////////
+/**
+ * Defines methods for coordinating the execution of asynchronous tasks.
+ * @record
+ */
+class Scheduler {
+ /**
+ * Schedules a task for execution. If the task function is a generator, the
+ * task will be executed using {@link ./promise.consume consume()}.
+ *
+ * @param {function(): (T|IThenable<T>)} fn The function to call to start the
+ * task.
+ * @param {string=} opt_description A description of the task for debugging
+ * purposes.
+ * @return {!Thenable<T>} A promise that will be resolved with the task
+ * result.
+ * @template T
+ */
+ execute(fn, opt_description) {}
+
+ /**
+ * Creates a new promise using the given resolver function.
+ *
+ * @param {function(
+ * function((T|IThenable<T>|Thenable|null)=),
+ * function(*=))} resolver
+ * @return {!Thenable<T>}
+ * @template T
+ */
+ promise(resolver) {}
+
+ /**
+ * Schedules a `setTimeout` call.
+ *
+ * @param {number} ms The timeout delay, in milliseconds.
+ * @param {string=} opt_description A description to accompany the timeout.
+ * @return {!Thenable<void>} A promise that will be resolved when the timeout
+ * fires.
+ */
+ timeout(ms, opt_description) {}
+
+ /**
+ * Schedules a task to wait for a condition to hold.
+ *
+ * If the condition is defined as a function, it may return any value. Promies
+ * will be resolved before testing if the condition holds (resolution time
+ * counts towards the timeout). Once resolved, values are always evaluated as
+ * booleans.
+ *
+ * If the condition function throws, or returns a rejected promise, the
+ * wait task will fail.
+ *
+ * If the condition is defined as a promise, the scheduler will wait for it to
+ * settle. If the timeout expires before the promise settles, the promise
+ * returned by this function will be rejected.
+ *
+ * If this function is invoked with `timeout === 0`, or the timeout is
+ * omitted, this scheduler will wait indefinitely for the condition to be
+ * satisfied.
+ *
+ * @param {(!IThenable<T>|function())} condition The condition to poll,
+ * or a promise to wait on.
+ * @param {number=} opt_timeout How long to wait, in milliseconds, for the
+ * condition to hold before timing out. If omitted, the flow will wait
+ * indefinitely.
+ * @param {string=} opt_message An optional error message to include if the
+ * wait times out; defaults to the empty string.
+ * @return {!Thenable<T>} A promise that will be fulfilled
+ * when the condition has been satisified. The promise shall be rejected
+ * if the wait times out waiting for the condition.
+ * @throws {TypeError} If condition is not a function or promise or if timeout
+ * is not a number >= 0.
+ * @template T
+ */
+ wait(condition, opt_timeout, opt_message) {}
+}
+
+
+let USE_PROMISE_MANAGER;
+function usePromiseManager() {
+ if (typeof USE_PROMISE_MANAGER !== 'undefined') {
+ return !!USE_PROMISE_MANAGER;
+ }
+ return process.env['SELENIUM_PROMISE_MANAGER'] === undefined
+ || !/^0|false$/i.test(process.env['SELENIUM_PROMISE_MANAGER']);
+}
+
+
+/**
+ * @param {function(
+ * function((T|IThenable<T>|Thenable|null)=),
+ * function(*=))} resolver
+ * @return {!Thenable<T>}
+ * @template T
+ */
+function createPromise(resolver) {
+ let ctor = usePromiseManager() ? ManagedPromise : NativePromise;
+ return new ctor(resolver);
+}
+
+
+/**
+ * @param {!Scheduler} scheduler The scheduler to use.
+ * @param {(!IThenable<T>|function())} condition The condition to poll,
+ * or a promise to wait on.
+ * @param {number=} opt_timeout How long to wait, in milliseconds, for the
+ * condition to hold before timing out. If omitted, the flow will wait
+ * indefinitely.
+ * @param {string=} opt_message An optional error message to include if the
+ * wait times out; defaults to the empty string.
+ * @return {!Thenable<T>} A promise that will be fulfilled
+ * when the condition has been satisified. The promise shall be rejected
+ * if the wait times out waiting for the condition.
+ * @throws {TypeError} If condition is not a function or promise or if timeout
+ * is not a number >= 0.
+ * @template T
+ */
+function scheduleWait(scheduler, condition, opt_timeout, opt_message) {
+ let timeout = opt_timeout || 0;
+ if (typeof timeout !== 'number' || timeout < 0) {
+ throw TypeError('timeout must be a number >= 0: ' + timeout);
+ }
+
+ if (isPromise(condition)) {
+ return scheduler.execute(function() {
+ if (!timeout) {
+ return condition;
+ }
+ return scheduler.promise(function(fulfill, reject) {
+ let start = Date.now();
+ let timer = setTimeout(function() {
+ timer = null;
+ reject(
+ new error.TimeoutError(
+ (opt_message ? opt_message + '\n' : '')
+ + 'Timed out waiting for promise to resolve after '
+ + (Date.now() - start) + 'ms'));
+ }, timeout);
+
+ /** @type {Thenable} */(condition).then(
+ function(value) {
+ timer && clearTimeout(timer);
+ fulfill(value);
+ },
+ function(error) {
+ timer && clearTimeout(timer);
+ reject(error);
+ });
+ });
+ }, opt_message || '<anonymous wait: promise resolution>');
+ }
+
+ if (typeof condition !== 'function') {
+ throw TypeError('Invalid condition; must be a function or promise: ' +
+ typeof condition);
+ }
+
+ if (isGenerator(condition)) {
+ let original = condition;
+ condition = () => consume(original);
+ }
+
+ return scheduler.execute(function() {
+ var startTime = Date.now();
+ return scheduler.promise(function(fulfill, reject) {
+ pollCondition();
+
+ function pollCondition() {
+ var conditionFn = /** @type {function()} */(condition);
+ scheduler.execute(conditionFn).then(function(value) {
+ var elapsed = Date.now() - startTime;
+ if (!!value) {
+ fulfill(value);
+ } else if (timeout && elapsed >= timeout) {
+ reject(
+ new error.TimeoutError(
+ (opt_message ? opt_message + '\n' : '')
+ + `Wait timed out after ${elapsed}ms`));
+ } else {
+ // Do not use asyncRun here because we need a non-micro yield
+ // here so the UI thread is given a chance when running in a
+ // browser.
+ setTimeout(pollCondition, 0);
+ }
+ }, reject);
+ }
+ });
+ }, opt_message || '<anonymous wait>');
+}
+
+
+/**
+ * A scheduler that executes all tasks immediately, with no coordination. This
+ * class is an event emitter for API compatibility with the {@link ControlFlow},
+ * however, it emits no events.
+ *
+ * @implements {Scheduler}
+ */
+class SimpleScheduler extends events.EventEmitter {
+ /** @override */
+ execute(fn) {
+ return this.promise((resolve, reject) => {
+ try {
+ if (isGenerator(fn)) {
+ consume(fn).then(resolve, reject);
+ } else {
+ resolve(fn.call(undefined));
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ });
+ }
+
+ /** @override */
+ promise(resolver) {
+ return new NativePromise(resolver);
+ }
+
+ /** @override */
+ timeout(ms) {
+ return this.promise(resolve => setTimeout(_ => resolve(), ms));
+ }
+
+ /** @override */
+ wait(condition, opt_timeout, opt_message) {
+ return scheduleWait(this, condition, opt_timeout, opt_message);
+ }
+}
+const SIMPLE_SCHEDULER = new SimpleScheduler;
+
/**
* Handles the execution of scheduled tasks, each of which may be an
@@ -1848,13 +2186,20 @@ function fullyResolveKeys(obj) {
* If there are no listeners registered with the flow, the error will be
* rethrown to the global error handler.
*
- * Refer to the {@link ./promise} module documentation fora detailed
+ * Refer to the {@link ./promise} module documentation for a detailed
* explanation of how the ControlFlow coordinates task execution.
*
+ * @implements {Scheduler}
* @final
*/
class ControlFlow extends events.EventEmitter {
constructor() {
+ if (!usePromiseManager()) {
+ throw TypeError(
+ 'Cannot instantiate control flow when the promise manager has'
+ + ' been disabled');
+ }
+
super();
/** @private {boolean} */
@@ -2024,21 +2369,7 @@ class ControlFlow extends events.EventEmitter {
return this.activeQueue_;
}
- /**
- * Schedules a task for execution. If there is nothing currently in the
- * queue, the task will be executed in the next turn of the event loop. If
- * the task function is a generator, the task will be executed using
- * {@link ./promise.consume consume()}.
- *
- * @param {function(): (T|ManagedPromise<T>)} fn The function to
- * call to start the task. If the function returns a
- * {@link ManagedPromise}, this instance will wait for it to be
- * resolved before starting the next task.
- * @param {string=} opt_description A description of the task.
- * @return {!ManagedPromise<T>} A promise that will be resolved
- * with the result of the action.
- * @template T
- */
+ /** @override */
execute(fn, opt_description) {
if (isGenerator(fn)) {
let original = fn;
@@ -2060,126 +2391,21 @@ class ControlFlow extends events.EventEmitter {
return task.promise;
}
- /**
- * Inserts a {@code setTimeout} into the command queue. This is equivalent to
- * a thread sleep in a synchronous programming language.
- *
- * @param {number} ms The timeout delay, in milliseconds.
- * @param {string=} opt_description A description to accompany the timeout.
- * @return {!ManagedPromise} A promise that will be resolved with
- * the result of the action.
- */
+ /** @override */
+ promise(resolver) {
+ return new ManagedPromise(resolver, this);
+ }
+
+ /** @override */
timeout(ms, opt_description) {
- return this.execute(function() {
- return delayed(ms);
+ return this.execute(() => {
+ return this.promise(resolve => setTimeout(() => resolve(), ms));
}, opt_description);
}
- /**
- * Schedules a task that shall wait for a condition to hold. Each condition
- * function may return any value, but it will always be evaluated as a
- * boolean.
- *
- * Condition functions may schedule sub-tasks with this instance, however,
- * their execution time will be factored into whether a wait has timed out.
- *
- * In the event a condition returns a ManagedPromise, the polling loop will wait for
- * it to be resolved before evaluating whether the condition has been
- * satisfied. The resolution time for a promise is factored into whether a
- * wait has timed out.
- *
- * If the condition function throws, or returns a rejected promise, the
- * wait task will fail.
- *
- * If the condition is defined as a promise, the flow will wait for it to
- * settle. If the timeout expires before the promise settles, the promise
- * returned by this function will be rejected.
- *
- * If this function is invoked with `timeout === 0`, or the timeout is
- * omitted, the flow will wait indefinitely for the condition to be satisfied.
- *
- * @param {(!ManagedPromise<T>|function())} condition The condition to poll,
- * or a promise to wait on.
- * @param {number=} opt_timeout How long to wait, in milliseconds, for the
- * condition to hold before timing out. If omitted, the flow will wait
- * indefinitely.
- * @param {string=} opt_message An optional error message to include if the
- * wait times out; defaults to the empty string.
- * @return {!ManagedPromise<T>} A promise that will be fulfilled
- * when the condition has been satisified. The promise shall be rejected
- * if the wait times out waiting for the condition.
- * @throws {TypeError} If condition is not a function or promise or if timeout
- * is not a number >= 0.
- * @template T
- */
+ /** @override */
wait(condition, opt_timeout, opt_message) {
- var timeout = opt_timeout || 0;
- if (typeof timeout !== 'number' || timeout < 0) {
- throw TypeError('timeout must be a number >= 0: ' + timeout);
- }
-
- if (isPromise(condition)) {
- return this.execute(function() {
- if (!timeout) {
- return condition;
- }
- return new ManagedPromise(function(fulfill, reject) {
- var start = Date.now();
- var timer = setTimeout(function() {
- timer = null;
- reject(Error((opt_message ? opt_message + '\n' : '') +
- 'Timed out waiting for promise to resolve after ' +
- (Date.now() - start) + 'ms'));
- }, timeout);
-
- /** @type {Thenable} */(condition).then(
- function(value) {
- timer && clearTimeout(timer);
- fulfill(value);
- },
- function(error) {
- timer && clearTimeout(timer);
- reject(error);
- });
- });
- }, opt_message || '<anonymous wait: promise resolution>');
- }
-
- if (typeof condition !== 'function') {
- throw TypeError('Invalid condition; must be a function or promise: ' +
- typeof condition);
- }
-
- if (isGenerator(condition)) {
- let original = condition;
- condition = () => consume(original);
- }
-
- var self = this;
- return this.execute(function() {
- var startTime = Date.now();
- return new ManagedPromise(function(fulfill, reject) {
- pollCondition();
-
- function pollCondition() {
- var conditionFn = /** @type {function()} */(condition);
- self.execute(conditionFn).then(function(value) {
- var elapsed = Date.now() - startTime;
- if (!!value) {
- fulfill(value);
- } else if (timeout && elapsed >= timeout) {
- reject(new Error((opt_message ? opt_message + '\n' : '') +
- 'Wait timed out after ' + elapsed + 'ms'));
- } else {
- // Do not use asyncRun here because we need a non-micro yield
- // here so the UI thread is given a chance when running in a
- // browser.
- setTimeout(pollCondition, 0);
- }
- }, reject);
- }
- });
- }, opt_message || '<anonymous wait>');
+ return scheduleWait(this, condition, opt_timeout, opt_message);
}
/**
@@ -2510,6 +2736,9 @@ class TaskQueue extends events.EventEmitter {
/** @private {({task: !Task, q: !TaskQueue}|null)} */
this.pending_ = null;
+ /** @private {TaskQueue} */
+ this.subQ_ = null;
+
/** @private {TaskQueueState} */
this.state_ = TaskQueueState.NEW;
@@ -2690,7 +2919,7 @@ class TaskQueue extends events.EventEmitter {
var task;
do {
task = this.getNextTask_();
- } while (task && !task.promise.isPending());
+ } while (task && !isPending(task.promise));
if (!task) {
this.state_ = TaskQueueState.FINISHED;
@@ -2701,20 +2930,30 @@ class TaskQueue extends events.EventEmitter {
return;
}
- var self = this;
- var subQ = new TaskQueue(this.flow_);
- subQ.once('end', () => self.onTaskComplete_(result))
- .once('error', (e) => self.onTaskFailure_(result, e));
- vlog(2, () => self + ' created ' + subQ + ' for ' + task);
+ let result = undefined;
+ this.subQ_ = new TaskQueue(this.flow_);
+
+ this.subQ_.once('end', () => { // On task completion.
+ this.subQ_ = null;
+ this.pending_ && this.pending_.task.fulfill(result);
+ });
+
+ this.subQ_.once('error', e => { // On task failure.
+ this.subQ_ = null;
+ if (Thenable.isImplementation(result)) {
+ result.cancel(CancellationError.wrap(e));
+ }
+ this.pending_ && this.pending_.task.reject(e);
+ });
+ vlog(2, () => `${this} created ${this.subQ_} for ${task}`);
- var result = undefined;
try {
- this.pending_ = {task: task, q: subQ};
+ this.pending_ = {task: task, q: this.subQ_};
task.promise.queue_ = this;
- result = subQ.execute_(task.execute);
- subQ.start();
+ result = this.subQ_.execute_(task.execute);
+ this.subQ_.start();
} catch (ex) {
- subQ.abort_(ex);
+ this.subQ_.abort_(ex);
}
}
@@ -2805,28 +3044,6 @@ class TaskQueue extends events.EventEmitter {
}
/**
- * @param {*} value the value originally returned by the task function.
- * @private
- */
- onTaskComplete_(value) {
- if (this.pending_) {
- this.pending_.task.fulfill(value);
- }
- }
-
- /**
- * @param {*} taskFnResult the value originally returned by the task function.
- * @param {*} error the error that caused the task function to terminate.
- * @private
- */
- onTaskFailure_(taskFnResult, error) {
- if (Thenable.isImplementation(taskFnResult)) {
- taskFnResult.cancel(CancellationError.wrap(error));
- }
- this.pending_.task.reject(error);
- }
-
- /**
* @return {(Task|undefined)} the next task scheduled within this queue,
* if any.
* @private
@@ -2857,9 +3074,9 @@ class TaskQueue extends events.EventEmitter {
/**
* The default flow to use if no others are active.
- * @type {!ControlFlow}
+ * @type {ControlFlow}
*/
-var defaultFlow = new ControlFlow();
+var defaultFlow;
/**
@@ -2878,6 +3095,11 @@ var activeFlows = [];
* @throws {Error} If the default flow is not currently active.
*/
function setDefaultFlow(flow) {
+ if (!usePromiseManager()) {
+ throw Error(
+ 'You may not change set the control flow when the promise'
+ +' manager is disabled');
+ }
if (activeFlows.length) {
throw Error('You may only change the default flow while it is active');
}
@@ -2887,10 +3109,21 @@ function setDefaultFlow(flow) {
/**
* @return {!ControlFlow} The currently active control flow.
+ * @suppress {checkTypes}
*/
function controlFlow() {
- return /** @type {!ControlFlow} */ (
- activeFlows.length ? activeFlows[activeFlows.length - 1] : defaultFlow);
+ if (!usePromiseManager()) {
+ return SIMPLE_SCHEDULER;
+ }
+
+ if (activeFlows.length) {
+ return activeFlows[activeFlows.length - 1];
+ }
+
+ if (!defaultFlow) {
+ defaultFlow = new ControlFlow;
+ }
+ return defaultFlow;
}
@@ -2900,8 +3133,7 @@ function controlFlow() {
* a promise that resolves to the callback result.
* @param {function(!ControlFlow)} callback The entry point
* to the newly created flow.
- * @return {!ManagedPromise} A promise that resolves to the callback
- * result.
+ * @return {!Thenable} A promise that resolves to the callback result.
*/
function createFlow(callback) {
var flow = new ControlFlow;
@@ -2955,53 +3187,51 @@ function isGenerator(fn) {
* @param {Object=} opt_self The object to use as "this" when invoking the
* initial generator.
* @param {...*} var_args Any arguments to pass to the initial generator.
- * @return {!ManagedPromise<?>} A promise that will resolve to the
+ * @return {!Thenable<?>} A promise that will resolve to the
* generator's final result.
* @throws {TypeError} If the given function is not a generator.
*/
-function consume(generatorFn, opt_self, var_args) {
+function consume(generatorFn, opt_self, ...var_args) {
if (!isGenerator(generatorFn)) {
throw new TypeError('Input is not a GeneratorFunction: ' +
generatorFn.constructor.name);
}
- var deferred = defer();
- var generator = generatorFn.apply(
- opt_self, Array.prototype.slice.call(arguments, 2));
- callNext();
- return deferred.promise;
-
- /** @param {*=} opt_value . */
- function callNext(opt_value) {
- pump(generator.next, opt_value);
- }
-
- /** @param {*=} opt_error . */
- function callThrow(opt_error) {
- // Dictionary lookup required because Closure compiler's built-in
- // externs does not include GeneratorFunction.prototype.throw.
- pump(generator['throw'], opt_error);
- }
+ let ret;
+ return ret = createPromise((resolve, reject) => {
+ let generator = generatorFn.apply(opt_self, var_args);
+ callNext();
- function pump(fn, opt_arg) {
- if (!deferred.promise.isPending()) {
- return; // Defererd was cancelled; silently abort.
+ /** @param {*=} opt_value . */
+ function callNext(opt_value) {
+ pump(generator.next, opt_value);
}
- try {
- var result = fn.call(generator, opt_arg);
- } catch (ex) {
- deferred.reject(ex);
- return;
+ /** @param {*=} opt_error . */
+ function callThrow(opt_error) {
+ pump(generator.throw, opt_error);
}
- if (result.done) {
- deferred.fulfill(result.value);
- return;
- }
+ function pump(fn, opt_arg) {
+ if (ret instanceof ManagedPromise && !isPending(ret)) {
+ return; // Defererd was cancelled; silently abort.
+ }
- asap(result.value, callNext, callThrow);
- }
+ try {
+ var result = fn.call(generator, opt_arg);
+ } catch (ex) {
+ reject(ex);
+ return;
+ }
+
+ if (result.done) {
+ resolve(result.value);
+ return;
+ }
+
+ asap(result.value, callNext, callThrow);
+ }
+ });
}
@@ -3009,12 +3239,14 @@ function consume(generatorFn, opt_self, var_args) {
module.exports = {
+ CancellableThenable: CancellableThenable,
CancellationError: CancellationError,
ControlFlow: ControlFlow,
Deferred: Deferred,
MultipleUnhandledRejectionError: MultipleUnhandledRejectionError,
Thenable: Thenable,
Promise: ManagedPromise,
+ Scheduler: Scheduler,
all: all,
asap: asap,
captureStackTrace: captureStackTrace,
@@ -3025,6 +3257,7 @@ module.exports = {
defer: defer,
delayed: delayed,
filter: filter,
+ finally: thenFinally,
fulfilled: fulfilled,
fullyResolved: fullyResolved,
isGenerator: isGenerator,
@@ -3034,6 +3267,22 @@ module.exports = {
setDefaultFlow: setDefaultFlow,
when: when,
+ /**
+ * Indicates whether the promise manager is currently enabled. When disabled,
+ * attempting to use the {@link ControlFlow} or {@link ManagedPromise Promise}
+ * classes will generate an error.
+ *
+ * The promise manager is currently enabled by default, but may be disabled
+ * by setting the environment variable `SELENIUM_PROMISE_MANAGER=0` or by
+ * setting this property to false. Setting this property will always take
+ * precedence ove the use of the environment variable.
+ *
+ * @return {boolean} Whether the promise manager is enabled.
+ * @see <https://github.com/SeleniumHQ/selenium/issues/2969>
+ */
+ get USE_PROMISE_MANAGER() { return usePromiseManager(); },
+ set USE_PROMISE_MANAGER(/** boolean */value) { USE_PROMISE_MANAGER = value; },
+
get LONG_STACK_TRACES() { return LONG_STACK_TRACES; },
set LONG_STACK_TRACES(v) { LONG_STACK_TRACES = v; },
};
diff --git a/node_modules/selenium-webdriver/lib/safari/client.js b/node_modules/selenium-webdriver/lib/safari/client.js
deleted file mode 100644
index 482c820fc..000000000
--- a/node_modules/selenium-webdriver/lib/safari/client.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Licensed to the Software Freedom Conservancy (SFC) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The SFC licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-'use strict';var n=this;
-function q(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==
-b&&"undefined"==typeof a.call)return"object";return b}function aa(a){var b=q(a);return"array"==b||"object"==b&&"number"==typeof a.length}function r(a){return"string"==typeof a}function ba(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ca(a,b,c){return a.call.apply(a.bind,arguments)}
-function da(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function ea(a,b,c){ea=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ca:da;return ea.apply(null,arguments)}var fa=Date.now||function(){return+new Date};
-function ga(a,b){function c(){}c.prototype=b.prototype;a.B=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.w=function(a,c,g){for(var e=Array(arguments.length-2),l=2;l<arguments.length;l++)e[l-2]=arguments[l];return b.prototype[c].apply(a,e)}};function t(a){if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))}ga(t,Error);t.prototype.name="CustomError";var ia;function ja(a,b){for(var c=a.split("%s"),d="",f=Array.prototype.slice.call(arguments,1);f.length&&1<c.length;)d+=c.shift()+f.shift();return d+c.join("%s")}var ka=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};
-function la(a){if(!ma.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(na,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(oa,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(pa,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(qa,"&quot;"));-1!=a.indexOf("'")&&(a=a.replace(ra,"&#39;"));-1!=a.indexOf("\x00")&&(a=a.replace(sa,"&#0;"));return a}var na=/&/g,oa=/</g,pa=/>/g,qa=/"/g,ra=/'/g,sa=/\x00/g,ma=/[\x00&<>"']/;function ta(a,b){return a<b?-1:a>b?1:0};function ua(a,b){b.unshift(a);t.call(this,ja.apply(null,b));b.shift()}ga(ua,t);ua.prototype.name="AssertionError";function u(a,b){throw new ua("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));};var va=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(r(a))return r(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},wa=Array.prototype.forEach?function(a,b,c){Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,f=r(a)?a.split(""):a,g=0;g<d;g++)g in f&&b.call(c,f[g],g,a)};
-function xa(a){return Array.prototype.concat.apply(Array.prototype,arguments)}function Ba(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}function Ca(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};function Da(a,b){for(var c in a)b.call(void 0,a[c],c,a)};function v(a,b){this.b={};this.a=[];this.f=this.c=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)Ea(this,arguments[d],arguments[d+1])}else if(a){var f;if(a instanceof v)f=a.o(),d=a.l();else{var c=[],g=0;for(f in a)c[g++]=f;f=c;c=[];g=0;for(d in a)c[g++]=a[d];d=c}for(c=0;c<f.length;c++)Ea(this,f[c],d[c])}}v.prototype.l=function(){Fa(this);for(var a=[],b=0;b<this.a.length;b++)a.push(this.b[this.a[b]]);return a};
-v.prototype.o=function(){Fa(this);return this.a.concat()};v.prototype.clear=function(){this.b={};this.f=this.c=this.a.length=0};function Fa(a){if(a.c!=a.a.length){for(var b=0,c=0;b<a.a.length;){var d=a.a[b];y(a.b,d)&&(a.a[c++]=d);b++}a.a.length=c}if(a.c!=a.a.length){for(var f={},c=b=0;b<a.a.length;)d=a.a[b],y(f,d)||(a.a[c++]=d,f[d]=1),b++;a.a.length=c}}function Ga(a,b){return y(a.b,b)?a.b[b]:void 0}function Ea(a,b,c){y(a.b,b)||(a.c++,a.a.push(b),a.f++);a.b[b]=c}
-v.prototype.forEach=function(a,b){for(var c=this.o(),d=0;d<c.length;d++){var f=c[d];a.call(b,Ga(this,f),f,this)}};v.prototype.clone=function(){return new v(this)};function y(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Ha=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/;function Ia(a,b){if(a)for(var c=a.split("&"),d=0;d<c.length;d++){var f=c[d].indexOf("="),g=null,e=null;0<=f?(g=c[d].substring(0,f),e=c[d].substring(f+1)):g=c[d];b(g,e?decodeURIComponent(e.replace(/\+/g," ")):"")}};function z(a,b){this.f=this.u=this.c="";this.s=null;this.g=this.m="";this.a=!1;var c;a instanceof z?(this.a=void 0!==b?b:a.a,Ja(this,a.c),this.u=a.u,this.f=a.f,Ka(this,a.s),this.m=a.m,La(this,a.b.clone()),this.g=a.g):a&&(c=String(a).match(Ha))?(this.a=!!b,Ja(this,c[1]||"",!0),this.u=A(c[2]||""),this.f=A(c[3]||"",!0),Ka(this,c[4]),this.m=A(c[5]||"",!0),La(this,c[6]||"",!0),this.g=A(c[7]||"")):(this.a=!!b,this.b=new B(null,0,this.a))}
-z.prototype.toString=function(){var a=[],b=this.c;b&&a.push(C(b,Ma,!0),":");var c=this.f;if(c||"file"==b)a.push("//"),(b=this.u)&&a.push(C(b,Ma,!0),"@"),a.push(encodeURIComponent(String(c)).replace(/%25([0-9a-fA-F]{2})/g,"%$1")),c=this.s,null!=c&&a.push(":",String(c));if(c=this.m)this.f&&"/"!=c.charAt(0)&&a.push("/"),a.push(C(c,"/"==c.charAt(0)?Na:Oa,!0));(c=this.b.toString())&&a.push("?",c);(c=this.g)&&a.push("#",C(c,Pa));return a.join("")};z.prototype.clone=function(){return new z(this)};
-function Ja(a,b,c){a.c=c?A(b,!0):b;a.c&&(a.c=a.c.replace(/:$/,""))}function Ka(a,b){if(b){b=Number(b);if(isNaN(b)||0>b)throw Error("Bad port number "+b);a.s=b}else a.s=null}function La(a,b,c){b instanceof B?(a.b=b,Qa(a.b,a.a)):(c||(b=C(b,Ra)),a.b=new B(b,0,a.a))}function A(a,b){return a?b?decodeURI(a.replace(/%25/g,"%2525")):decodeURIComponent(a):""}function C(a,b,c){return r(a)?(a=encodeURI(a).replace(b,Sa),c&&(a=a.replace(/%25([0-9a-fA-F]{2})/g,"%$1")),a):null}
-function Sa(a){a=a.charCodeAt(0);return"%"+(a>>4&15).toString(16)+(a&15).toString(16)}var Ma=/[#\/\?@]/g,Oa=/[\#\?:]/g,Na=/[\#\?]/g,Ra=/[\#\?@]/g,Pa=/#/g;function B(a,b,c){this.c=this.a=null;this.b=a||null;this.f=!!c}function D(a){a.a||(a.a=new v,a.c=0,a.b&&Ia(a.b,function(b,c){var d=decodeURIComponent(b.replace(/\+/g," "));D(a);a.b=null;var d=E(a,d),f=Ga(a.a,d);f||Ea(a.a,d,f=[]);f.push(c);a.c=a.c+1}))}
-function Ta(a,b){D(a);b=E(a,b);if(y(a.a.b,b)){a.b=null;a.c=a.c-Ga(a.a,b).length;var c=a.a;y(c.b,b)&&(delete c.b[b],c.c--,c.f++,c.a.length>2*c.c&&Fa(c))}}B.prototype.clear=function(){this.a=this.b=null;this.c=0};B.prototype.o=function(){D(this);for(var a=this.a.l(),b=this.a.o(),c=[],d=0;d<b.length;d++)for(var f=a[d],g=0;g<f.length;g++)c.push(b[d]);return c};
-B.prototype.l=function(a){D(this);var b=[];if(r(a)){var c=a;D(this);c=E(this,c);y(this.a.b,c)&&(b=xa(b,Ga(this.a,E(this,a))))}else for(a=this.a.l(),c=0;c<a.length;c++)b=xa(b,a[c]);return b};function Ua(){var a=(new z(window.location)).b.l("url");return 0<a.length?String(a[0]):void 0}
-B.prototype.toString=function(){if(this.b)return this.b;if(!this.a)return"";for(var a=[],b=this.a.o(),c=0;c<b.length;c++)for(var d=b[c],f=encodeURIComponent(String(d)),d=this.l(d),g=0;g<d.length;g++){var e=f;""!==d[g]&&(e+="="+encodeURIComponent(String(d[g])));a.push(e)}return this.b=a.join("&")};B.prototype.clone=function(){var a=new B;a.b=this.b;this.a&&(a.a=this.a.clone(),a.c=this.c);return a};function E(a,b){var c=String(b);a.f&&(c=c.toLowerCase());return c}
-function Qa(a,b){b&&!a.f&&(D(a),a.b=null,a.a.forEach(function(a,b){var f=b.toLowerCase();b!=f&&(Ta(this,b),Ta(this,f),0<a.length&&(this.b=null,Ea(this.a,E(this,f),Ba(a)),this.c=this.c+a.length))},a));a.f=b};var Va={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0};function F(){this.a="";this.b=Wa}F.prototype.i=!0;F.prototype.h=function(){return this.a};F.prototype.toString=function(){return"Const{"+this.a+"}"};function Xa(a){if(a instanceof F&&a.constructor===F&&a.b===Wa)return a.a;u("expected object of type Const, got '"+a+"'");return"type_error:Const"}var Wa={};function Ya(a){var b=new F;b.a=a;return b};function G(){this.a="";this.b=Za}G.prototype.i=!0;var Za={};G.prototype.h=function(){return this.a};G.prototype.toString=function(){return"SafeStyle{"+this.a+"}"};function $a(a){var b=new G;b.a=a;return b}var ab=$a(""),bb=/^([-,."'%_!# a-zA-Z0-9]+|(?:rgb|hsl)a?\([0-9.%, ]+\))$/;function H(){this.a="";this.b=cb}H.prototype.i=!0;H.prototype.h=function(){return this.a};H.prototype.v=!0;H.prototype.j=function(){return 1};H.prototype.toString=function(){return"SafeUrl{"+this.a+"}"};function db(a){if(a instanceof H&&a.constructor===H&&a.b===cb)return a.a;u("expected object of type SafeUrl, got '"+a+"' of type "+q(a));return"type_error:SafeUrl"}var eb=/^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i;
-function fb(a){if(a instanceof H)return a;a=a.i?a.h():String(a);eb.test(a)||(a="about:invalid#zClosurez");return gb(a)}var cb={};function gb(a){var b=new H;b.a=a;return b}gb("about:blank");function I(){this.a=hb}I.prototype.i=!0;I.prototype.h=function(){return""};I.prototype.v=!0;I.prototype.j=function(){return 1};I.prototype.toString=function(){return"TrustedResourceUrl{}"};var hb={};var J;a:{var ib=n.navigator;if(ib){var jb=ib.userAgent;if(jb){J=jb;break a}}J=""};function L(){this.a="";this.c=kb;this.b=null}L.prototype.v=!0;L.prototype.j=function(){return this.b};L.prototype.i=!0;L.prototype.h=function(){return this.a};L.prototype.toString=function(){return"SafeHtml{"+this.a+"}"};function M(a){if(a instanceof L&&a.constructor===L&&a.c===kb)return a.a;u("expected object of type SafeHtml, got '"+a+"' of type "+q(a));return"type_error:SafeHtml"}function lb(a){if(a instanceof L)return a;var b=null;a.v&&(b=a.j());a=la(a.i?a.h():String(a));return N(a,b)}
-function O(a){if(a instanceof L)return a;a=lb(a);var b;b=M(a).replace(/ /g," &#160;").replace(/(\r\n|\r|\n)/g,"<br>");return N(b,a.j())}var mb=/^[a-zA-Z0-9-]+$/,nb={action:!0,cite:!0,data:!0,formaction:!0,href:!0,manifest:!0,poster:!0,src:!0},ob={APPLET:!0,BASE:!0,EMBED:!0,IFRAME:!0,LINK:!0,MATH:!0,META:!0,OBJECT:!0,SCRIPT:!0,STYLE:!0,SVG:!0,TEMPLATE:!0};
-function pb(a,b,c){if(!mb.test(a))throw Error("Invalid tag name <"+a+">.");if(a.toUpperCase()in ob)throw Error("Tag name <"+a+"> is not allowed for SafeHtml.");var d=null,f="<"+a;if(b)for(var g in b){if(!mb.test(g))throw Error('Invalid attribute name "'+g+'".');var e=b[g];if(null!=e){var l,m=a;l=g;if(e instanceof F)e=Xa(e);else if("style"==l.toLowerCase()){if(!ba(e))throw Error('The "style" attribute requires goog.html.SafeStyle or map of style properties, '+typeof e+" given: "+e);if(!(e instanceof
-G)){var m="",w=void 0;for(w in e){if(!/^[-_a-zA-Z0-9]+$/.test(w))throw Error("Name allows only [-_a-zA-Z0-9], got: "+w);var k=e[w];if(null!=k){if(k instanceof F)k=Xa(k);else if(bb.test(k)){for(var h=!0,x=!0,p=0;p<k.length;p++){var K=k.charAt(p);"'"==K&&x?h=!h:'"'==K&&h&&(x=!x)}h&&x||(u("String value requires balanced quotes, got: "+k),k="zClosurez")}else u("String value allows only [-,.\"'%_!# a-zA-Z0-9], rgb() and rgba(), got: "+k),k="zClosurez";m+=w+":"+k+";"}}e=m?$a(m):ab}m=void 0;e instanceof
-G&&e.constructor===G&&e.b===Za?m=e.a:(u("expected object of type SafeStyle, got '"+e+"' of type "+q(e)),m="type_error:SafeStyle");e=m}else{if(/^on/i.test(l))throw Error('Attribute "'+l+'" requires goog.string.Const value, "'+e+'" given.');if(l.toLowerCase()in nb)if(e instanceof I)e instanceof I&&e.constructor===I&&e.a===hb?e="":(u("expected object of type TrustedResourceUrl, got '"+e+"' of type "+q(e)),e="type_error:TrustedResourceUrl");else if(e instanceof H)e=db(e);else if(r(e))e=fb(e).h();else throw Error('Attribute "'+
-l+'" on tag "'+m+'" requires goog.html.SafeUrl, goog.string.Const, or string, value "'+e+'" given.');}e.i&&(e=e.h());l=l+'="'+la(String(e))+'"';f=f+(" "+l)}}null!=c?"array"==q(c)||(c=[c]):c=[];!0===Va[a.toLowerCase()]?f+=">":(d=P(c),f+=">"+M(d)+"</"+a+">",d=d.j());(a=b&&b.dir)&&(/^(ltr|rtl|auto)$/i.test(a)?d=0:d=null);return N(f,d)}function P(a){function b(a){"array"==q(a)?wa(a,b):(a=lb(a),d+=M(a),a=a.j(),0==c?c=a:0!=a&&c!=a&&(c=null))}var c=0,d="";wa(arguments,b);return N(d,c)}var kb={};
-function N(a,b){var c=new L;c.a=a;c.b=b;return c}N("<!DOCTYPE html>",0);var qb=N("",0),rb=N("<br>",0);function sb(a){var b;b=Error();if(Error.captureStackTrace)Error.captureStackTrace(b,a||sb),b=String(b.stack);else{try{throw b;}catch(c){b=c}b=(b=b.stack)?String(b):null}b||(b=tb(a||arguments.callee.caller,[]));return b}
-function tb(a,b){var c=[];if(0<=va(b,a))c.push("[...circular reference...]");else if(a&&50>b.length){c.push(ub(a)+"(");for(var d=a.arguments,f=0;d&&f<d.length;f++){0<f&&c.push(", ");var g;g=d[f];switch(typeof g){case "object":g=g?"object":"null";break;case "string":break;case "number":g=String(g);break;case "boolean":g=g?"true":"false";break;case "function":g=(g=ub(g))?g:"[fn]";break;default:g=typeof g}40<g.length&&(g=g.substr(0,40)+"...");c.push(g)}b.push(a);c.push(")\n");try{c.push(tb(a.caller,
-b))}catch(e){c.push("[exception trying to get caller]\n")}}else a?c.push("[...long stack...]"):c.push("[end]");return c.join("")}function ub(a){if(Q[a])return Q[a];a=String(a);if(!Q[a]){var b=/function ([^\(]+)/.exec(a);Q[a]=b?b[1]:"[Anonymous]"}return Q[a]}var Q={};function vb(a,b,c,d,f){"number"==typeof f||wb++;this.c=d||fa();this.f=a;this.b=b;this.g=c;delete this.a}vb.prototype.a=null;var wb=0;function xb(a){this.g=a;this.a=this.c=this.f=this.b=null}function R(a,b){this.name=a;this.value=b}R.prototype.toString=function(){return this.name};var yb=new R("SHOUT",1200),zb=new R("SEVERE",1E3),Ab=new R("WARNING",900),Bb=new R("INFO",800),Cb=new R("CONFIG",700);function Db(a){if(a.f)return a.f;if(a.b)return Db(a.b);u("Root logger has no level set.");return null}
-xb.prototype.log=function(a,b,c){if(a.value>=Db(this).value)for("function"==q(b)&&(b=b()),a=new vb(a,String(b),this.g),c&&(a.a=c),c="log:"+a.b,n.console&&(n.console.timeStamp?n.console.timeStamp(c):n.console.markTimeline&&n.console.markTimeline(c)),n.msWriteProfilerMark&&n.msWriteProfilerMark(c),c=this;c;){b=c;var d=a;if(b.a)for(var f=0,g=void 0;g=b.a[f];f++)g(d);c=c.b}};var Eb={},S=null;function Fb(){S||(S=new xb(""),Eb[""]=S,S.f=Cb)}
-function Gb(a){Fb();var b;if(!(b=Eb[a])){b=new xb(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Gb(a.substr(0,c));c.c||(c.c={});c.c[d]=b;b.b=c;Eb[a]=b}return b};var Hb=new function(){this.a=fa()};function Ib(a){this.c=a||"";this.f=Hb}Ib.prototype.a=!0;Ib.prototype.b=!1;function T(a){return 10>a?"0"+a:String(a)}function Jb(a){Ib.call(this,a)}ga(Jb,Ib);Jb.prototype.b=!0;function Kb(a,b){Da(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:Lb.hasOwnProperty(d)?a.setAttribute(Lb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})}var Lb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"};
-function Mb(a,b,c){function d(c){c&&b.appendChild(r(c)?a.createTextNode(c):c)}for(var f=2;f<c.length;f++){var g=c[f];!aa(g)||ba(g)&&0<g.nodeType?d(g):wa(Nb(g)?Ba(g):g,d)}}function Nb(a){if(a&&"number"==typeof a.length){if(ba(a))return"function"==typeof a.item||"string"==typeof a.item;if("function"==q(a))return"function"==typeof a.item}return!1}function Ob(a){this.b=a||n.document||document}
-function Pb(a,b){var c;c=a.b;var d=b&&"*"!=b?b.toUpperCase():"";c.querySelectorAll&&c.querySelector&&d?c=c.querySelectorAll(d+""):c=c.getElementsByTagName(d||"*");return c}Ob.prototype.a=function(a,b,c){var d=this.b,f=arguments,g=f[1],e=d.createElement(f[0]);g&&(r(g)?e.className=g:"array"==q(g)?e.className=g.join(" "):Kb(e,g));2<f.length&&Mb(d,e,f);return e};
-Ob.prototype.contains=function(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a};function Qb(a){this.m=ea(this.f,this);this.b=new Jb;this.c=this.b.a=!1;this.a=a;this.g=this.a.ownerDocument||this.a.document;a=(a=this.a)?new Ob(9==a.nodeType?a:a.ownerDocument||a.document):ia||(ia=new Ob);var b=null,c=Pb(a,"HEAD")[0];c||(b=Pb(a,"BODY")[0],c=a.a("HEAD"),b.parentNode.insertBefore(c,b));b=a.a("STYLE");b.innerHTML=".dbg-sev{color:#F00}.dbg-w{color:#C40}.dbg-sh{font-weight:bold;color:#000}.dbg-i{color:#444}.dbg-f{color:#999}.dbg-ev{color:#0A0}.dbg-m{color:#990}.logmsg{border-bottom:1px solid #CCC;padding:2px}.logsep{background-color: #8C8;}.logdiv{border:1px solid #CCC;background-color:#FCFCFC;font:medium monospace}";
-c.appendChild(b);this.a.className+=" logdiv"}function Rb(a){if(1!=a.c){var b;Fb();b=S;var c=a.m;b.a||(b.a=[]);b.a.push(c);a.c=!0}}
-Qb.prototype.f=function(a){if(a){var b=100>=this.a.scrollHeight-this.a.scrollTop-this.a.clientHeight,c=this.g.createElement("DIV");c.className="logmsg";var d;var f=this.b;if(a){switch(a.f.value){case yb.value:d="dbg-sh";break;case zb.value:d="dbg-sev";break;case Ab.value:d="dbg-w";break;case Bb.value:d="dbg-i";break;default:d="dbg-f"}var g=[];g.push(f.c," ");if(f.a){var e=new Date(a.c);g.push("[",T(e.getFullYear()-2E3)+T(e.getMonth()+1)+T(e.getDate())+" "+T(e.getHours())+":"+T(e.getMinutes())+":"+
-T(e.getSeconds())+"."+T(Math.floor(e.getMilliseconds()/10)),"] ")}var e=(a.c-f.f.a)/1E3,l=e.toFixed(3),m=0;if(1>e)m=2;else for(;100>e;)m++,e*=10;for(;0<m--;)l=" "+l;g.push("[",l,"s] ");g.push("[",a.g,"] ");g=O(g.join(""));e=qb;if(f.b&&a.a){var w;try{var k;var h=a.a,x;d:{for(var f=["window","location","href"],e=n,p;p=f.shift();)if(null!=e[p])e=e[p];else{x=null;break d}x=e}if(r(h))k={message:h,name:"Unknown error",lineNumber:"Not available",fileName:x,stack:"Not available"};else{var K,ya;p=!1;try{K=
-h.lineNumber||h.A||"Not available"}catch(za){K="Not available",p=!0}try{ya=h.fileName||h.filename||h.sourceURL||n.$googDebugFname||x}catch(za){ya="Not available",p=!0}k=!p&&h.lineNumber&&h.fileName&&h.stack&&h.message&&h.name?h:{message:h.message||"Not available",name:h.name||"UnknownError",lineNumber:K,fileName:ya,stack:h.stack||"Not available"}}var Aa;var ha=k.fileName;null!=ha||(ha="");if(/^https?:\/\//i.test(ha)){var cc=fb(ha);Ya("view-source scheme plus HTTP/HTTPS URL");var dc="view-source:"+
-db(cc);Aa=gb(dc)}else{var ec=Ya("sanitizedviewsrc");Aa=gb(Xa(ec))}w=P(O("Message: "+k.message+"\nUrl: "),pb("a",{href:Aa,target:"_new"},k.fileName),O("\nLine: "+k.lineNumber+"\n\nBrowser stack:\n"+k.stack+"-> [end]\n\nJS stack traversal:\n"+sb(void 0)+"-> "))}catch(za){w=O("Exception trying to expose exception! You win, we lose. "+za)}e=P(rb,w)}a=O(a.b);d=pb("span",{"class":d},P(a,e));d=P(g,d,rb)}else d=qb;c.innerHTML=M(d);this.a.appendChild(c);b&&(this.a.scrollTop=this.a.scrollHeight)}};
-Qb.prototype.clear=function(){this.a&&(this.a.innerHTML=M(qb))};function U(a,b){a&&a.log(Bb,b,void 0)};var Sb;if(-1!=J.indexOf("iPhone")&&-1==J.indexOf("iPod")&&-1==J.indexOf("iPad")||-1!=J.indexOf("iPad")||-1!=J.indexOf("iPod"))Sb="";else{var Tb=/Version\/([0-9.]+)/.exec(J);Sb=Tb?Tb[1]:""};for(var Ub=0,Vb=ka(String(Sb)).split("."),Wb=ka("6").split("."),Xb=Math.max(Vb.length,Wb.length),Yb=0;0==Ub&&Yb<Xb;Yb++){var Zb=Vb[Yb]||"",$b=Wb[Yb]||"",ac=/(\d*)(\D*)/g,bc=/(\d*)(\D*)/g;do{var V=ac.exec(Zb)||["","",""],W=bc.exec($b)||["","",""];if(0==V[0].length&&0==W[0].length)break;Ub=ta(0==V[1].length?0:parseInt(V[1],10),0==W[1].length?0:parseInt(W[1],10))||ta(0==V[2].length,0==W[2].length)||ta(V[2],W[2])}while(0==Ub)};var fc=JSON.stringify;function X(a,b,c){var d=a.constructor;if(a.window===a)try{var f=a.constructor;delete a.constructor;d=a.constructor;a.constructor=f}catch(g){}return d.prototype[b].apply(a,Ca(arguments,2))};Gb("safaridriver.message");function gc(a){this.a={};this.a[hc]="webdriver";this.a[ic]=a}var hc="origin",ic="type";function jc(a){var b=window;a.a[hc]="webdriver";if(b.postMessage){var c,b=function(a){c=a.data};X(window,"addEventListener","safaridriver.message.response",b,!1);kc(a.a);X(window,"removeEventListener","safaridriver.message.response",b,!1);return c}var d=X(document,"createEvent","Events");d.initEvent("beforeload",!1,!1);return b.canLoad(d,a.a)}
-function kc(a){var b=X(document,"createEvent","MessageEvent");b.initMessageEvent("safaridriver.message",!1,!1,a,window.location.origin,"0",window,null);X(window,"dispatchEvent",b)}gc.prototype.toJSON=function(){return this.a};gc.prototype.toString=function(){return fc(this.toJSON())};function lc(a){gc.call(this,"connect");this.a.url=a}ga(lc,gc);function mc(){function a(){f+=1;jc(g)?(U(c,"Connected to extension"),U(c,"Requesting extension connect to client at "+d)):5>f?setTimeout(a,250*f):c&&c.log(zb,"Unable to establish a connection with the SafariDriver extension",void 0)}var b=document.createElement("h2");b.innerHTML="SafariDriver Launcher";document.body.appendChild(b);b=document.createElement("div");document.body.appendChild(b);Rb(new Qb(b));var c=Gb("safaridriver.client"),d=Ua();if(d){d=new z(d);U(c,"Connecting to SafariDriver browser extension...");
-U(c,"This will fail if you have not installed the latest SafariDriver extension from\nhttp://selenium-release.storage.googleapis.com/index.html");U(c,"Extension logs may be viewed by clicking the Selenium [\u2713] button on the Safari toolbar");var f=0,g=new lc(d.toString());a()}else c&&c.log(zb,"No url specified. Please reload this page with the url parameter set",void 0)}var Y=["init"],Z=n;Y[0]in Z||!Z.execScript||Z.execScript("var "+Y[0]);
-for(var nc;Y.length&&(nc=Y.shift());)Y.length||void 0===mc?Z[nc]?Z=Z[nc]:Z=Z[nc]={}:Z[nc]=mc;;window.onload = init;
diff --git a/node_modules/selenium-webdriver/lib/session.js b/node_modules/selenium-webdriver/lib/session.js
index 296291d4c..64ff6be76 100644
--- a/node_modules/selenium-webdriver/lib/session.js
+++ b/node_modules/selenium-webdriver/lib/session.js
@@ -17,7 +17,7 @@
'use strict';
-const Capabilities = require('./capabilities').Capabilities;
+const {Capabilities} = require('./capabilities');
/**
diff --git a/node_modules/selenium-webdriver/lib/test/build.js b/node_modules/selenium-webdriver/lib/test/build.js
index 59f0b1357..53b284ffb 100644
--- a/node_modules/selenium-webdriver/lib/test/build.js
+++ b/node_modules/selenium-webdriver/lib/test/build.js
@@ -21,8 +21,7 @@ const spawn = require('child_process').spawn,
fs = require('fs'),
path = require('path');
-const isDevMode = require('../devmode'),
- promise = require('../promise');
+const isDevMode = require('../devmode');
var projectRoot = path.normalize(path.join(__dirname, '../../../../..'));
@@ -69,7 +68,7 @@ Build.prototype.onlyOnce = function() {
/**
* Executes the build.
- * @return {!webdriver.promise.Promise} A promise that will be resolved when
+ * @return {!Promise} A promise that will be resolved when
* the build has completed.
* @throws {Error} If no targets were specified.
*/
@@ -86,7 +85,7 @@ Build.prototype.go = function() {
});
if (!targets.length) {
- return promise.fulfilled();
+ return Promise.resolve();
}
}
@@ -100,31 +99,30 @@ Build.prototype.go = function() {
cmd = path.join(projectRoot, 'go');
}
- var result = promise.defer();
- spawn(cmd, args, {
- cwd: projectRoot,
- env: process.env,
- stdio: ['ignore', process.stdout, process.stderr]
- }).on('exit', function(code, signal) {
- if (code === 0) {
- targets.forEach(function(target) {
- builtTargets[target] = 1;
- });
- return result.fulfill();
- }
-
- var msg = 'Unable to build artifacts';
- if (code) { // May be null.
- msg += '; code=' + code;
- }
- if (signal) {
- msg += '; signal=' + signal;
- }
-
- result.reject(Error(msg));
+ return new Promise((resolve, reject) => {
+ spawn(cmd, args, {
+ cwd: projectRoot,
+ env: process.env,
+ stdio: ['ignore', process.stdout, process.stderr]
+ }).on('exit', function(code, signal) {
+ if (code === 0) {
+ targets.forEach(function(target) {
+ builtTargets[target] = 1;
+ });
+ return resolve();
+ }
+
+ var msg = 'Unable to build artifacts';
+ if (code) { // May be null.
+ msg += '; code=' + code;
+ }
+ if (signal) {
+ msg += '; signal=' + signal;
+ }
+
+ reject(Error(msg));
+ });
});
-
- return result.promise;
};
diff --git a/node_modules/selenium-webdriver/lib/test/data/formPage.html b/node_modules/selenium-webdriver/lib/test/data/formPage.html
index 7bcfea00f..45ae2b7d6 100644
--- a/node_modules/selenium-webdriver/lib/test/data/formPage.html
+++ b/node_modules/selenium-webdriver/lib/test/data/formPage.html
@@ -45,6 +45,7 @@ There should be a form here:
<select name="no-select" disabled="disabled">
<option value="foo">Foo</option>
+ <option value="bar">Bar</option>
</select>
<select name="select_empty_multiple" multiple>
diff --git a/node_modules/selenium-webdriver/lib/test/fileserver.js b/node_modules/selenium-webdriver/lib/test/fileserver.js
index 448b0c9a2..8194778ea 100644
--- a/node_modules/selenium-webdriver/lib/test/fileserver.js
+++ b/node_modules/selenium-webdriver/lib/test/fileserver.js
@@ -28,8 +28,7 @@ var serveIndex = require('serve-index');
var Server = require('./httpserver').Server,
resources = require('./resources'),
- isDevMode = require('../devmode'),
- promise = require('../promise');
+ isDevMode = require('../devmode');
var WEB_ROOT = '/common';
var JS_ROOT = '/javascript';
@@ -194,15 +193,18 @@ function sendDelayedResponse(request, response) {
}
-function handleUpload(request, response, next) {
- multer({
- inMemory: true,
- onFileUploadComplete: function(file) {
+function handleUpload(request, response) {
+ let upload = multer({storage: multer.memoryStorage()}).any();
+ upload(request, response, function(err) {
+ if (err) {
+ response.writeHead(500);
+ response.end(err + '');
+ } else {
response.writeHead(200);
- response.write(file.buffer);
+ response.write(request.files[0].buffer);
response.end('<script>window.top.window.onUploadDone();</script>');
}
- })(request, response, function() {});
+ });
}
@@ -269,7 +271,7 @@ function sendIndex(request, response) {
/**
* Starts the server on the specified port.
* @param {number=} opt_port The port to use, or 0 for any free port.
- * @return {!webdriver.promise.Promise.<Host>} A promise that will resolve
+ * @return {!Promise<Host>} A promise that will resolve
* with the server host when it has fully started.
*/
exports.start = server.start.bind(server);
@@ -277,7 +279,7 @@ exports.start = server.start.bind(server);
/**
* Stops the server.
- * @return {!webdriver.promise.Promise} A promise that will resolve when the
+ * @return {!Promise} A promise that will resolve when the
* server has closed all connections.
*/
exports.stop = server.stop.bind(server);
diff --git a/node_modules/selenium-webdriver/lib/test/httpserver.js b/node_modules/selenium-webdriver/lib/test/httpserver.js
index 55b12551f..f7847f734 100644
--- a/node_modules/selenium-webdriver/lib/test/httpserver.js
+++ b/node_modules/selenium-webdriver/lib/test/httpserver.js
@@ -50,14 +50,14 @@ var Server = function(requestHandler) {
* Starts the server on the given port. If no port, or 0, is provided,
* the server will be started on a random port.
* @param {number=} opt_port The port to start on.
- * @return {!webdriver.promise.Promise.<Host>} A promise that will resolve
+ * @return {!Promise<Host>} A promise that will resolve
* with the server host when it has fully started.
*/
this.start = function(opt_port) {
assert(typeof opt_port !== 'function',
"start invoked with function, not port (mocha callback)?");
var port = opt_port || portprober.findFreePort('localhost');
- return promise.when(port, function(port) {
+ return Promise.resolve(port).then(port => {
return promise.checkedNodeCall(
server.listen.bind(server, port, 'localhost'));
}).then(function() {
@@ -67,13 +67,11 @@ var Server = function(requestHandler) {
/**
* Stops the server.
- * @return {!webdriver.promise.Promise} A promise that will resolve when the
+ * @return {!Promise} A promise that will resolve when the
* server has closed all connections.
*/
this.stop = function() {
- var d = promise.defer();
- server.close(d.fulfill);
- return d.promise;
+ return new Promise(resolve => server.close(resolve));
};
/**
diff --git a/node_modules/selenium-webdriver/lib/test/index.js b/node_modules/selenium-webdriver/lib/test/index.js
index d702c5fb2..ba34ddab4 100644
--- a/node_modules/selenium-webdriver/lib/test/index.js
+++ b/node_modules/selenium-webdriver/lib/test/index.js
@@ -31,7 +31,6 @@ var build = require('./build'),
const LEGACY_FIREFOX = 'legacy-' + webdriver.Browser.FIREFOX;
-const LEGACY_SAFARI = 'legacy-' + webdriver.Browser.SAFARI;
/**
@@ -46,8 +45,7 @@ var NATIVE_BROWSERS = [
webdriver.Browser.IE,
webdriver.Browser.OPERA,
webdriver.Browser.PHANTOM_JS,
- webdriver.Browser.SAFARI,
- LEGACY_SAFARI
+ webdriver.Browser.SAFARI
];
@@ -83,8 +81,7 @@ var browsersToTest = (function() {
parts[0] = webdriver.Browser.IE;
}
- if (parts[0] === LEGACY_FIREFOX ||
- parts[0] === LEGACY_SAFARI) {
+ if (parts[0] === LEGACY_FIREFOX) {
return;
}
@@ -117,6 +114,8 @@ var browsersToTest = (function() {
console.log('Running tests using loopback address')
}
}
+ console.log(
+ 'Promise manager is enabled? ' + webdriver.promise.USE_PROMISE_MANAGER);
return browsers;
})();
@@ -175,14 +174,6 @@ function TestEnvironment(browserName, server) {
parts[0] = webdriver.Browser.FIREFOX;
}
- if (parts[0] === LEGACY_SAFARI) {
- var options = builder.getSafariOptions() || new safari.Options();
- options.useLegacyDriver(true);
- builder.setSafariOptions(options);
-
- parts[0] = webdriver.Browser.SAFARI;
- }
-
builder.forBrowser(parts[0], parts[1], parts[2]);
if (server) {
builder.usingServer(server.address());
@@ -227,27 +218,31 @@ function suite(fn, opt_options) {
try {
+ before(function() {
+ if (isDevMode) {
+ return build.of(
+ '//javascript/atoms/fragments:is-displayed',
+ '//javascript/webdriver/atoms:getAttribute')
+ .onlyOnce().go();
+ }
+ });
+
// Server is only started if required for a specific config.
- testing.after(function() {
+ after(function() {
if (seleniumServer) {
return seleniumServer.stop();
}
});
browsers.forEach(function(browser) {
- testing.describe('[' + browser + ']', function() {
+ describe('[' + browser + ']', function() {
if (isDevMode && nativeRun) {
if (browser === LEGACY_FIREFOX) {
- testing.before(function() {
+ before(function() {
return build.of('//javascript/firefox-driver:webdriver')
.onlyOnce().go();
});
- } else if (browser === LEGACY_SAFARI) {
- testing.before(function() {
- return build.of('//javascript/safari-driver:client')
- .onlyOnce().go();
- });
}
}
@@ -259,7 +254,7 @@ function suite(fn, opt_options) {
serverJar, {loopback: useLoopback});
}
- testing.before(function() {
+ before(function() {
this.timeout(0);
return seleniumServer.start(60 * 1000);
});
@@ -275,14 +270,14 @@ function suite(fn, opt_options) {
// GLOBAL TEST SETUP
-testing.before(function() {
+before(function() {
// Do not pass register fileserver.start directly with testing.before,
// as start takes an optional port, which before assumes is an async
// callback.
return fileserver.start();
});
-testing.after(function() {
+after(function() {
return fileserver.stop();
});
diff --git a/node_modules/selenium-webdriver/lib/webdriver.js b/node_modules/selenium-webdriver/lib/webdriver.js
index 13077b54e..081d77bda 100644
--- a/node_modules/selenium-webdriver/lib/webdriver.js
+++ b/node_modules/selenium-webdriver/lib/webdriver.js
@@ -28,7 +28,7 @@ const command = require('./command');
const error = require('./error');
const input = require('./input');
const logging = require('./logging');
-const Session = require('./session').Session;
+const {Session} = require('./session');
const Symbols = require('./symbols');
const promise = require('./promise');
@@ -64,13 +64,13 @@ class Condition {
/**
* Defines a condition that will result in a {@link WebElement}.
*
- * @extends {Condition<!(WebElement|promise.Promise<!WebElement>)>}
+ * @extends {Condition<!(WebElement|IThenable<!WebElement>)>}
*/
class WebElementCondition extends Condition {
/**
* @param {string} message A descriptive error message. Should complete the
* sentence "Waiting [...]"
- * @param {function(!WebDriver): !(WebElement|promise.Promise<!WebElement>)}
+ * @param {function(!WebDriver): !(WebElement|IThenable<!WebElement>)}
* fn The condition function to evaluate on each iteration of the wait
* loop.
*/
@@ -237,145 +237,14 @@ function fromWireValue(driver, value) {
/**
- * Creates a new WebDriver client, which provides control over a browser.
- *
- * Every command.Command returns a {@link promise.Promise} that
- * represents the result of that command. Callbacks may be registered on this
- * object to manipulate the command result or catch an expected error. Any
- * commands scheduled with a callback are considered sub-commands and will
- * execute before the next command in the current frame. For example:
- *
- * var message = [];
- * driver.call(message.push, message, 'a').then(function() {
- * driver.call(message.push, message, 'b');
- * });
- * driver.call(message.push, message, 'c');
- * driver.call(function() {
- * alert('message is abc? ' + (message.join('') == 'abc'));
- * });
+ * Structural interface for a WebDriver client.
*
+ * @record
*/
-class WebDriver {
- /**
- * @param {!(Session|promise.Promise<!Session>)} session Either a
- * known session or a promise that will be resolved to a session.
- * @param {!command.Executor} executor The executor to use when sending
- * commands to the browser.
- * @param {promise.ControlFlow=} opt_flow The flow to
- * schedule commands through. Defaults to the active flow object.
- */
- constructor(session, executor, opt_flow) {
- /** @private {!promise.Promise<!Session>} */
- this.session_ = promise.fulfilled(session);
-
- /** @private {!command.Executor} */
- this.executor_ = executor;
-
- /** @private {!promise.ControlFlow} */
- this.flow_ = opt_flow || promise.controlFlow();
-
- /** @private {input.FileDetector} */
- this.fileDetector_ = null;
- }
-
- /**
- * Creates a new WebDriver client for an existing session.
- * @param {!command.Executor} executor Command executor to use when querying
- * for session details.
- * @param {string} sessionId ID of the session to attach to.
- * @param {promise.ControlFlow=} opt_flow The control flow all
- * driver commands should execute under. Defaults to the
- * {@link promise.controlFlow() currently active} control flow.
- * @return {!WebDriver} A new client for the specified session.
- */
- static attachToSession(executor, sessionId, opt_flow) {
- let flow = opt_flow || promise.controlFlow();
- let cmd = new command.Command(command.Name.DESCRIBE_SESSION)
- .setParameter('sessionId', sessionId);
- let session = flow.execute(
- () => executeCommand(executor, cmd).catch(err => {
- // The DESCRIBE_SESSION command is not supported by the W3C spec, so
- // if we get back an unknown command, just return a session with
- // unknown capabilities.
- if (err instanceof error.UnknownCommandError) {
- return new Session(sessionId, new Capabilities);
- }
- throw err;
- }),
- 'WebDriver.attachToSession()');
- return new WebDriver(session, executor, flow);
- }
-
- /**
- * Creates a new WebDriver session.
- *
- * By default, the requested session `capabilities` are merely "desired" and
- * the remote end will still create a new session even if it cannot satisfy
- * all of the requested capabilities. You can query which capabilities a
- * session actually has using the
- * {@linkplain #getCapabilities() getCapabilities()} method on the returned
- * WebDriver instance.
- *
- * To define _required capabilities_, provide the `capabilities` as an object
- * literal with `required` and `desired` keys. The `desired` key may be
- * omitted if all capabilities are required, and vice versa. If the server
- * cannot create a session with all of the required capabilities, it will
- * return an {@linkplain error.SessionNotCreatedError}.
- *
- * let required = new Capabilities().set('browserName', 'firefox');
- * let desired = new Capabilities().set('version', '45');
- * let driver = WebDriver.createSession(executor, {required, desired});
- *
- * This function will always return a WebDriver instance. If there is an error
- * creating the session, such as the aforementioned SessionNotCreatedError,
- * the driver will have a rejected {@linkplain #getSession session} promise.
- * It is recommended that this promise is left _unhandled_ so it will
- * propagate through the {@linkplain promise.ControlFlow control flow} and
- * cause subsequent commands to fail.
- *
- * let required = Capabilities.firefox();
- * let driver = WebDriver.createSession(executor, {required});
- *
- * // If the createSession operation failed, then this command will also
- * // also fail, propagating the creation failure.
- * driver.get('http://www.google.com').catch(e => console.log(e));
- *
- * @param {!command.Executor} executor The executor to create the new session
- * with.
- * @param {(!Capabilities|
- * {desired: (Capabilities|undefined),
- * required: (Capabilities|undefined)})} capabilities The desired
- * capabilities for the new session.
- * @param {promise.ControlFlow=} opt_flow The control flow all driver
- * commands should execute under, including the initial session creation.
- * Defaults to the {@link promise.controlFlow() currently active}
- * control flow.
- * @return {!WebDriver} The driver for the newly created session.
- */
- static createSession(executor, capabilities, opt_flow) {
- let flow = opt_flow || promise.controlFlow();
- let cmd = new command.Command(command.Name.NEW_SESSION);
-
- if (capabilities && (capabilities.desired || capabilities.required)) {
- cmd.setParameter('desiredCapabilities', capabilities.desired);
- cmd.setParameter('requiredCapabilities', capabilities.required);
- } else {
- cmd.setParameter('desiredCapabilities', capabilities);
- }
+class IWebDriver {
- let session = flow.execute(
- () => executeCommand(executor, cmd),
- 'WebDriver.createSession()');
- return new WebDriver(session, executor, flow);
- }
-
- /**
- * @return {!promise.ControlFlow} The control flow used by this
- * instance.
- */
- controlFlow() {
- return this.flow_;
- }
+ /** @return {!promise.ControlFlow} The control flow used by this instance. */
+ controlFlow() {}
/**
* Schedules a {@link command.Command} to be executed by this driver's
@@ -383,107 +252,44 @@ class WebDriver {
*
* @param {!command.Command} command The command to schedule.
* @param {string} description A description of the command for debugging.
- * @return {!promise.Promise<T>} A promise that will be resolved
+ * @return {!promise.Thenable<T>} A promise that will be resolved
* with the command result.
* @template T
*/
- schedule(command, description) {
- var self = this;
-
- checkHasNotQuit();
- command.setParameter('sessionId', this.session_);
-
- // If any of the command parameters are rejected promises, those
- // rejections may be reported as unhandled before the control flow
- // attempts to execute the command. To ensure parameters errors
- // propagate through the command itself, we resolve all of the
- // command parameters now, but suppress any errors until the ControlFlow
- // actually executes the command. This addresses scenarios like catching
- // an element not found error in:
- //
- // driver.findElement(By.id('foo')).click().catch(function(e) {
- // if (e instanceof NoSuchElementError) {
- // // Do something.
- // }
- // });
- var prepCommand = toWireValue(command.getParameters());
- prepCommand.catch(function() {});
-
- var flow = this.flow_;
- var executor = this.executor_;
- return flow.execute(function() {
- // A call to WebDriver.quit() may have been scheduled in the same event
- // loop as this |command|, which would prevent us from detecting that the
- // driver has quit above. Therefore, we need to make another quick check.
- // We still check above so we can fail as early as possible.
- checkHasNotQuit();
-
- // Retrieve resolved command parameters; any previously suppressed errors
- // will now propagate up through the control flow as part of the command
- // execution.
- return prepCommand.then(function(parameters) {
- command.setParameters(parameters);
- return executor.execute(command);
- }).then(value => fromWireValue(self, value));
- }, description);
-
- function checkHasNotQuit() {
- if (!self.session_) {
- throw new error.NoSuchSessionError(
- 'This driver instance does not have a valid session ID ' +
- '(did you call WebDriver.quit()?) and may no longer be ' +
- 'used.');
- }
- }
- }
+ schedule(command, description) {}
/**
* Sets the {@linkplain input.FileDetector file detector} that should be
* used with this instance.
* @param {input.FileDetector} detector The detector to use or {@code null}.
*/
- setFileDetector(detector) {
- this.fileDetector_ = detector;
- }
+ setFileDetector(detector) {}
/**
* @return {!command.Executor} The command executor used by this instance.
*/
- getExecutor() {
- return this.executor_;
- }
+ getExecutor() {}
/**
- * @return {!promise.Promise<!Session>} A promise for this client's
- * session.
+ * @return {!promise.Thenable<!Session>} A promise for this client's session.
*/
- getSession() {
- return this.session_;
- }
+ getSession() {}
/**
- * @return {!promise.Promise<!Capabilities>} A promise
- * that will resolve with the this instance's capabilities.
+ * @return {!promise.Thenable<!Capabilities>} A promise that will resolve with
+ * the this instance's capabilities.
*/
- getCapabilities() {
- return this.session_.then(session => session.getCapabilities());
- }
+ getCapabilities() {}
/**
- * Schedules a command to quit the current session. After calling quit, this
- * instance will be invalidated and may no longer be used to issue commands
- * against the browser.
- * @return {!promise.Promise<void>} A promise that will be resolved
- * when the command has completed.
+ * Terminates the browser session. After calling quit, this instance will be
+ * invalidated and may no longer be used to issue commands against the
+ * browser.
+ *
+ * @return {!promise.Thenable<void>} A promise that will be resolved when the
+ * command has completed.
*/
- quit() {
- var result = this.schedule(
- new command.Command(command.Name.QUIT),
- 'WebDriver.quit()');
- // Delete our session ID when the quit command finishes; this will allow us
- // to throw an error when attemnpting to use a driver post-quit.
- return result.finally(() => delete this.session_);
- }
+ quit() {}
/**
* Creates a new action sequence using this driver. The sequence will not be
@@ -498,9 +304,7 @@ class WebDriver {
*
* @return {!actions.ActionSequence} A new action sequence for this instance.
*/
- actions() {
- return new actions.ActionSequence(this);
- }
+ actions() {}
/**
* Creates a new touch sequence using this driver. The sequence will not be
@@ -514,9 +318,7 @@ class WebDriver {
*
* @return {!actions.TouchSequence} A new touch sequence for this instance.
*/
- touchActions() {
- return new actions.TouchSequence(this);
- }
+ touchActions() {}
/**
* Schedules a command to execute JavaScript in the context of the currently
@@ -550,22 +352,11 @@ class WebDriver {
*
* @param {!(string|Function)} script The script to execute.
* @param {...*} var_args The arguments to pass to the script.
- * @return {!promise.Promise<T>} A promise that will resolve to the
+ * @return {!promise.Thenable<T>} A promise that will resolve to the
* scripts return value.
* @template T
*/
- executeScript(script, var_args) {
- if (typeof script === 'function') {
- script = 'return (' + script + ').apply(null, arguments);';
- }
- let args =
- arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
- return this.schedule(
- new command.Command(command.Name.EXECUTE_SCRIPT).
- setParameter('script', script).
- setParameter('args', args),
- 'WebDriver.executeScript()');
- }
+ executeScript(script, var_args) {}
/**
* Schedules a command to execute asynchronous JavaScript in the context of the
@@ -639,45 +430,22 @@ class WebDriver {
*
* @param {!(string|Function)} script The script to execute.
* @param {...*} var_args The arguments to pass to the script.
- * @return {!promise.Promise<T>} A promise that will resolve to the
+ * @return {!promise.Thenable<T>} A promise that will resolve to the
* scripts return value.
* @template T
*/
- executeAsyncScript(script, var_args) {
- if (typeof script === 'function') {
- script = 'return (' + script + ').apply(null, arguments);';
- }
- let args = Array.prototype.slice.call(arguments, 1);
- return this.schedule(
- new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
- setParameter('script', script).
- setParameter('args', args),
- 'WebDriver.executeScript()');
- }
+ executeAsyncScript(script, var_args) {}
/**
* Schedules a command to execute a custom function.
- * @param {function(...): (T|promise.Promise<T>)} fn The function to
- * execute.
+ * @param {function(...): (T|IThenable<T>)} fn The function to execute.
* @param {Object=} opt_scope The object in whose scope to execute the function.
* @param {...*} var_args Any arguments to pass to the function.
- * @return {!promise.Promise<T>} A promise that will be resolved'
+ * @return {!promise.Thenable<T>} A promise that will be resolved'
* with the function's result.
* @template T
*/
- call(fn, opt_scope, var_args) {
- let args = Array.prototype.slice.call(arguments, 2);
- let flow = this.flow_;
- return flow.execute(function() {
- return promise.fullyResolved(args).then(function(args) {
- if (promise.isGenerator(fn)) {
- args.unshift(fn, opt_scope);
- return promise.consume.apply(null, args);
- }
- return fn.apply(opt_scope, args);
- });
- }, 'WebDriver.call(' + (fn.name || 'function') + ')');
- }
+ call(fn, opt_scope, var_args) {}
/**
* Schedules a command to wait for a condition to hold. The condition may be
@@ -716,7 +484,7 @@ class WebDriver {
* driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
* driver.get(getServerUrl());
*
- * @param {!(promise.Promise<T>|
+ * @param {!(IThenable<T>|
* Condition<T>|
* function(!WebDriver): T)} condition The condition to
* wait on, defined as a promise, condition object, or a function to
@@ -724,134 +492,76 @@ class WebDriver {
* @param {number=} opt_timeout How long to wait for the condition to be true.
* @param {string=} opt_message An optional message to use if the wait times
* out.
- * @return {!(promise.Promise<T>|WebElementPromise)} A promise that will be
+ * @return {!(promise.Thenable<T>|WebElementPromise)} A promise that will be
* resolved with the first truthy value returned by the condition
* function, or rejected if the condition times out. If the input
* input condition is an instance of a {@link WebElementCondition},
* the returned value will be a {@link WebElementPromise}.
+ * @throws {TypeError} if the provided `condition` is not a valid type.
* @template T
*/
- wait(condition, opt_timeout, opt_message) {
- if (promise.isPromise(condition)) {
- return this.flow_.wait(
- /** @type {!promise.Promise} */(condition),
- opt_timeout, opt_message);
- }
-
- var message = opt_message;
- var fn = /** @type {!Function} */(condition);
- if (condition instanceof Condition) {
- message = message || condition.description();
- fn = condition.fn;
- }
-
- var driver = this;
- var result = this.flow_.wait(function() {
- if (promise.isGenerator(fn)) {
- return promise.consume(fn, null, [driver]);
- }
- return fn(driver);
- }, opt_timeout, message);
-
- if (condition instanceof WebElementCondition) {
- result = new WebElementPromise(this, result.then(function(value) {
- if (!(value instanceof WebElement)) {
- throw TypeError(
- 'WebElementCondition did not resolve to a WebElement: '
- + Object.prototype.toString.call(value));
- }
- return value;
- }));
- }
- return result;
- }
+ wait(condition, opt_timeout, opt_message) {}
/**
* Schedules a command to make the driver sleep for the given amount of time.
* @param {number} ms The amount of time, in milliseconds, to sleep.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the sleep has finished.
*/
- sleep(ms) {
- return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
- }
+ sleep(ms) {}
/**
* Schedules a command to retrieve the current window handle.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the current window handle.
*/
- getWindowHandle() {
- return this.schedule(
- new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE),
- 'WebDriver.getWindowHandle()');
- }
+ getWindowHandle() {}
/**
* Schedules a command to retrieve the current list of available window handles.
- * @return {!promise.Promise.<!Array<string>>} A promise that will
+ * @return {!promise.Thenable<!Array<string>>} A promise that will
* be resolved with an array of window handles.
*/
- getAllWindowHandles() {
- return this.schedule(
- new command.Command(command.Name.GET_WINDOW_HANDLES),
- 'WebDriver.getAllWindowHandles()');
- }
+ getAllWindowHandles() {}
/**
* Schedules a command to retrieve the current page's source. The page source
* returned is a representation of the underlying DOM: do not expect it to be
* formatted or escaped in the same way as the response sent from the web
* server.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the current page source.
*/
- getPageSource() {
- return this.schedule(
- new command.Command(command.Name.GET_PAGE_SOURCE),
- 'WebDriver.getPageSource()');
- }
+ getPageSource() {}
/**
* Schedules a command to close the current window.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when this command has completed.
*/
- close() {
- return this.schedule(new command.Command(command.Name.CLOSE),
- 'WebDriver.close()');
- }
+ close() {}
/**
* Schedules a command to navigate to the given URL.
* @param {string} url The fully qualified URL to open.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the document has finished loading.
*/
- get(url) {
- return this.navigate().to(url);
- }
+ get(url) {}
/**
* Schedules a command to retrieve the URL of the current page.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the current URL.
*/
- getCurrentUrl() {
- return this.schedule(
- new command.Command(command.Name.GET_CURRENT_URL),
- 'WebDriver.getCurrentUrl()');
- }
+ getCurrentUrl() {}
/**
* Schedules a command to retrieve the current page's title.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the current page's title.
*/
- getTitle() {
- return this.schedule(new command.Command(command.Name.GET_TITLE),
- 'WebDriver.getTitle()');
- }
+ getTitle() {}
/**
* Schedule a command to find an element on the page. If the element cannot be
@@ -859,7 +569,10 @@ class WebDriver {
* by the driver. Unlike other commands, this error cannot be suppressed. In
* other words, scheduling a command to find an element doubles as an assert
* that the element is present on the page. To test whether an element is
- * present on the page, use {@link #isElementPresent} instead.
+ * present on the page, use {@link #findElements}:
+ *
+ * driver.findElements(By.id('foo'))
+ * .then(found => console.log('Element found? %s', !!found.length));
*
* The search criteria for an element may be defined using one of the
* factories in the {@link webdriver.By} namespace, or as a short-hand
@@ -889,6 +602,415 @@ class WebDriver {
* commands against the located element. If the element is not found, the
* element will be invalidated and all scheduled commands aborted.
*/
+ findElement(locator) {}
+
+ /**
+ * Schedule a command to search for multiple elements on the page.
+ *
+ * @param {!(by.By|Function)} locator The locator to use.
+ * @return {!promise.Thenable<!Array<!WebElement>>} A
+ * promise that will resolve to an array of WebElements.
+ */
+ findElements(locator) {}
+
+ /**
+ * Schedule a command to take a screenshot. The driver makes a best effort to
+ * return a screenshot of the following, in order of preference:
+ *
+ * 1. Entire page
+ * 2. Current window
+ * 3. Visible portion of the current frame
+ * 4. The entire display containing the browser
+ *
+ * @return {!promise.Thenable<string>} A promise that will be
+ * resolved to the screenshot as a base-64 encoded PNG.
+ */
+ takeScreenshot() {}
+
+ /**
+ * @return {!Options} The options interface for this instance.
+ */
+ manage() {}
+
+ /**
+ * @return {!Navigation} The navigation interface for this instance.
+ */
+ navigate() {}
+
+ /**
+ * @return {!TargetLocator} The target locator interface for this
+ * instance.
+ */
+ switchTo() {}
+}
+
+
+/**
+ * Each WebDriver instance provides automated control over a browser session.
+ *
+ * @implements {IWebDriver}
+ */
+class WebDriver {
+ /**
+ * @param {!(Session|IThenable<!Session>)} session Either a known session or a
+ * promise that will be resolved to a session.
+ * @param {!command.Executor} executor The executor to use when sending
+ * commands to the browser.
+ * @param {promise.ControlFlow=} opt_flow The flow to
+ * schedule commands through. Defaults to the active flow object.
+ * @param {(function(this: void): ?)=} opt_onQuit A function to call, if any,
+ * when the session is terminated.
+ */
+ constructor(session, executor, opt_flow, opt_onQuit) {
+ /** @private {!promise.ControlFlow} */
+ this.flow_ = opt_flow || promise.controlFlow();
+
+ /** @private {!promise.Thenable<!Session>} */
+ this.session_ = this.flow_.promise(resolve => resolve(session));
+
+ /** @private {!command.Executor} */
+ this.executor_ = executor;
+
+ /** @private {input.FileDetector} */
+ this.fileDetector_ = null;
+
+ /** @private @const {(function(this: void): ?|undefined)} */
+ this.onQuit_ = opt_onQuit;
+ }
+
+ /**
+ * Creates a new WebDriver client for an existing session.
+ * @param {!command.Executor} executor Command executor to use when querying
+ * for session details.
+ * @param {string} sessionId ID of the session to attach to.
+ * @param {promise.ControlFlow=} opt_flow The control flow all
+ * driver commands should execute under. Defaults to the
+ * {@link promise.controlFlow() currently active} control flow.
+ * @return {!WebDriver} A new client for the specified session.
+ */
+ static attachToSession(executor, sessionId, opt_flow) {
+ let flow = opt_flow || promise.controlFlow();
+ let cmd = new command.Command(command.Name.DESCRIBE_SESSION)
+ .setParameter('sessionId', sessionId);
+ let session = flow.execute(
+ () => executeCommand(executor, cmd).catch(err => {
+ // The DESCRIBE_SESSION command is not supported by the W3C spec, so
+ // if we get back an unknown command, just return a session with
+ // unknown capabilities.
+ if (err instanceof error.UnknownCommandError) {
+ return new Session(sessionId, new Capabilities);
+ }
+ throw err;
+ }),
+ 'WebDriver.attachToSession()');
+ return new WebDriver(session, executor, flow);
+ }
+
+ /**
+ * Creates a new WebDriver session.
+ *
+ * By default, the requested session `capabilities` are merely "desired" and
+ * the remote end will still create a new session even if it cannot satisfy
+ * all of the requested capabilities. You can query which capabilities a
+ * session actually has using the
+ * {@linkplain #getCapabilities() getCapabilities()} method on the returned
+ * WebDriver instance.
+ *
+ * To define _required capabilities_, provide the `capabilities` as an object
+ * literal with `required` and `desired` keys. The `desired` key may be
+ * omitted if all capabilities are required, and vice versa. If the server
+ * cannot create a session with all of the required capabilities, it will
+ * return an {@linkplain error.SessionNotCreatedError}.
+ *
+ * let required = new Capabilities().set('browserName', 'firefox');
+ * let desired = new Capabilities().set('version', '45');
+ * let driver = WebDriver.createSession(executor, {required, desired});
+ *
+ * This function will always return a WebDriver instance. If there is an error
+ * creating the session, such as the aforementioned SessionNotCreatedError,
+ * the driver will have a rejected {@linkplain #getSession session} promise.
+ * It is recommended that this promise is left _unhandled_ so it will
+ * propagate through the {@linkplain promise.ControlFlow control flow} and
+ * cause subsequent commands to fail.
+ *
+ * let required = Capabilities.firefox();
+ * let driver = WebDriver.createSession(executor, {required});
+ *
+ * // If the createSession operation failed, then this command will also
+ * // also fail, propagating the creation failure.
+ * driver.get('http://www.google.com').catch(e => console.log(e));
+ *
+ * @param {!command.Executor} executor The executor to create the new session
+ * with.
+ * @param {(!Capabilities|
+ * {desired: (Capabilities|undefined),
+ * required: (Capabilities|undefined)})} capabilities The desired
+ * capabilities for the new session.
+ * @param {promise.ControlFlow=} opt_flow The control flow all driver
+ * commands should execute under, including the initial session creation.
+ * Defaults to the {@link promise.controlFlow() currently active}
+ * control flow.
+ * @param {(function(new: WebDriver,
+ * !IThenable<!Session>,
+ * !command.Executor,
+ * promise.ControlFlow=))=} opt_ctor
+ * A reference to the constructor of the specific type of WebDriver client
+ * to instantiate. Will create a vanilla {@linkplain WebDriver} instance
+ * if a constructor is not provided.
+ * @param {(function(this: void): ?)=} opt_onQuit A callback to invoke when
+ * the newly created session is terminated. This should be used to clean
+ * up any resources associated with the session.
+ * @return {!WebDriver} The driver for the newly created session.
+ */
+ static createSession(
+ executor, capabilities, opt_flow, opt_ctor, opt_onQuit) {
+ let flow = opt_flow || promise.controlFlow();
+ let cmd = new command.Command(command.Name.NEW_SESSION);
+
+ if (capabilities && (capabilities.desired || capabilities.required)) {
+ cmd.setParameter('desiredCapabilities', capabilities.desired);
+ cmd.setParameter('requiredCapabilities', capabilities.required);
+ } else {
+ cmd.setParameter('desiredCapabilities', capabilities);
+ }
+
+ let session = flow.execute(
+ () => executeCommand(executor, cmd),
+ 'WebDriver.createSession()');
+ if (typeof opt_onQuit === 'function') {
+ session = session.catch(err => {
+ return Promise.resolve(opt_onQuit.call(void 0)).then(_ => {throw err});
+ });
+ }
+ const ctor = opt_ctor || WebDriver;
+ return new ctor(session, executor, flow, opt_onQuit);
+ }
+
+ /** @override */
+ controlFlow() {
+ return this.flow_;
+ }
+
+ /** @override */
+ schedule(command, description) {
+ command.setParameter('sessionId', this.session_);
+
+ // If any of the command parameters are rejected promises, those
+ // rejections may be reported as unhandled before the control flow
+ // attempts to execute the command. To ensure parameters errors
+ // propagate through the command itself, we resolve all of the
+ // command parameters now, but suppress any errors until the ControlFlow
+ // actually executes the command. This addresses scenarios like catching
+ // an element not found error in:
+ //
+ // driver.findElement(By.id('foo')).click().catch(function(e) {
+ // if (e instanceof NoSuchElementError) {
+ // // Do something.
+ // }
+ // });
+ var prepCommand = toWireValue(command.getParameters());
+ prepCommand.catch(function() {});
+
+ var flow = this.flow_;
+ var executor = this.executor_;
+ return flow.execute(() => {
+ // Retrieve resolved command parameters; any previously suppressed errors
+ // will now propagate up through the control flow as part of the command
+ // execution.
+ return prepCommand.then(function(parameters) {
+ command.setParameters(parameters);
+ return executor.execute(command);
+ }).then(value => fromWireValue(this, value));
+ }, description);
+ }
+
+ /** @override */
+ setFileDetector(detector) {
+ this.fileDetector_ = detector;
+ }
+
+ /** @override */
+ getExecutor() {
+ return this.executor_;
+ }
+
+ /** @override */
+ getSession() {
+ return this.session_;
+ }
+
+ /** @override */
+ getCapabilities() {
+ return this.session_.then(s => s.getCapabilities());
+ }
+
+ /** @override */
+ quit() {
+ var result = this.schedule(
+ new command.Command(command.Name.QUIT),
+ 'WebDriver.quit()');
+ // Delete our session ID when the quit command finishes; this will allow us
+ // to throw an error when attemnpting to use a driver post-quit.
+ return /** @type {!promise.Thenable} */(promise.finally(result, () => {
+ this.session_ = this.flow_.promise((_, reject) => {
+ reject(new error.NoSuchSessionError(
+ 'This driver instance does not have a valid session ID ' +
+ '(did you call WebDriver.quit()?) and may no longer be used.'));
+ });
+
+ // Only want the session rejection to bubble if accessed.
+ this.session_.catch(function() {});
+
+ if (this.onQuit_) {
+ return this.onQuit_.call(void 0);
+ }
+ }));
+ }
+
+ /** @override */
+ actions() {
+ return new actions.ActionSequence(this);
+ }
+
+ /** @override */
+ touchActions() {
+ return new actions.TouchSequence(this);
+ }
+
+ /** @override */
+ executeScript(script, var_args) {
+ if (typeof script === 'function') {
+ script = 'return (' + script + ').apply(null, arguments);';
+ }
+ let args =
+ arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
+ return this.schedule(
+ new command.Command(command.Name.EXECUTE_SCRIPT).
+ setParameter('script', script).
+ setParameter('args', args),
+ 'WebDriver.executeScript()');
+ }
+
+ /** @override */
+ executeAsyncScript(script, var_args) {
+ if (typeof script === 'function') {
+ script = 'return (' + script + ').apply(null, arguments);';
+ }
+ let args = Array.prototype.slice.call(arguments, 1);
+ return this.schedule(
+ new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
+ setParameter('script', script).
+ setParameter('args', args),
+ 'WebDriver.executeScript()');
+ }
+
+ /** @override */
+ call(fn, opt_scope, var_args) {
+ let args = Array.prototype.slice.call(arguments, 2);
+ return this.flow_.execute(function() {
+ return promise.fullyResolved(args).then(function(args) {
+ if (promise.isGenerator(fn)) {
+ args.unshift(fn, opt_scope);
+ return promise.consume.apply(null, args);
+ }
+ return fn.apply(opt_scope, args);
+ });
+ }, 'WebDriver.call(' + (fn.name || 'function') + ')');
+ }
+
+ /** @override */
+ wait(condition, opt_timeout, opt_message) {
+ if (promise.isPromise(condition)) {
+ return this.flow_.wait(
+ /** @type {!IThenable} */(condition),
+ opt_timeout, opt_message);
+ }
+
+ var message = opt_message;
+ var fn = /** @type {!Function} */(condition);
+ if (condition instanceof Condition) {
+ message = message || condition.description();
+ fn = condition.fn;
+ }
+
+ if (typeof fn !== 'function') {
+ throw TypeError(
+ 'Wait condition must be a promise-like object, function, or a '
+ + 'Condition object');
+ }
+
+ var driver = this;
+ var result = this.flow_.wait(function() {
+ if (promise.isGenerator(fn)) {
+ return promise.consume(fn, null, [driver]);
+ }
+ return fn(driver);
+ }, opt_timeout, message);
+
+ if (condition instanceof WebElementCondition) {
+ result = new WebElementPromise(this, result.then(function(value) {
+ if (!(value instanceof WebElement)) {
+ throw TypeError(
+ 'WebElementCondition did not resolve to a WebElement: '
+ + Object.prototype.toString.call(value));
+ }
+ return value;
+ }));
+ }
+ return result;
+ }
+
+ /** @override */
+ sleep(ms) {
+ return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
+ }
+
+ /** @override */
+ getWindowHandle() {
+ return this.schedule(
+ new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE),
+ 'WebDriver.getWindowHandle()');
+ }
+
+ /** @override */
+ getAllWindowHandles() {
+ return this.schedule(
+ new command.Command(command.Name.GET_WINDOW_HANDLES),
+ 'WebDriver.getAllWindowHandles()');
+ }
+
+ /** @override */
+ getPageSource() {
+ return this.schedule(
+ new command.Command(command.Name.GET_PAGE_SOURCE),
+ 'WebDriver.getPageSource()');
+ }
+
+ /** @override */
+ close() {
+ return this.schedule(new command.Command(command.Name.CLOSE),
+ 'WebDriver.close()');
+ }
+
+ /** @override */
+ get(url) {
+ return this.navigate().to(url);
+ }
+
+ /** @override */
+ getCurrentUrl() {
+ return this.schedule(
+ new command.Command(command.Name.GET_CURRENT_URL),
+ 'WebDriver.getCurrentUrl()');
+ }
+
+ /** @override */
+ getTitle() {
+ return this.schedule(new command.Command(command.Name.GET_TITLE),
+ 'WebDriver.getTitle()');
+ }
+
+ /** @override */
findElement(locator) {
let id;
locator = by.checkedLocator(locator);
@@ -907,7 +1029,7 @@ class WebDriver {
* @param {!Function} locatorFn The locator function to use.
* @param {!(WebDriver|WebElement)} context The search
* context.
- * @return {!promise.Promise.<!WebElement>} A
+ * @return {!promise.Thenable<!WebElement>} A
* promise that will resolve to a list of WebElements.
* @private
*/
@@ -923,13 +1045,7 @@ class WebDriver {
});
}
- /**
- * Schedule a command to search for multiple elements on the page.
- *
- * @param {!(by.By|Function)} locator The locator to use.
- * @return {!promise.Promise.<!Array.<!WebElement>>} A
- * promise that will resolve to an array of WebElements.
- */
+ /** @override */
findElements(locator) {
locator = by.checkedLocator(locator);
if (typeof locator === 'function') {
@@ -951,7 +1067,7 @@ class WebDriver {
/**
* @param {!Function} locatorFn The locator function to use.
* @param {!(WebDriver|WebElement)} context The search context.
- * @return {!promise.Promise<!Array<!WebElement>>} A promise that
+ * @return {!promise.Thenable<!Array<!WebElement>>} A promise that
* will resolve to an array of WebElements.
* @private
*/
@@ -971,41 +1087,23 @@ class WebDriver {
});
}
- /**
- * Schedule a command to take a screenshot. The driver makes a best effort to
- * return a screenshot of the following, in order of preference:
- *
- * 1. Entire page
- * 2. Current window
- * 3. Visible portion of the current frame
- * 4. The entire display containing the browser
- *
- * @return {!promise.Promise<string>} A promise that will be
- * resolved to the screenshot as a base-64 encoded PNG.
- */
+ /** @override */
takeScreenshot() {
return this.schedule(new command.Command(command.Name.SCREENSHOT),
'WebDriver.takeScreenshot()');
}
- /**
- * @return {!Options} The options interface for this instance.
- */
+ /** @override */
manage() {
return new Options(this);
}
- /**
- * @return {!Navigation} The navigation interface for this instance.
- */
+ /** @override */
navigate() {
return new Navigation(this);
}
- /**
- * @return {!TargetLocator} The target locator interface for this
- * instance.
- */
+ /** @override */
switchTo() {
return new TargetLocator(this);
}
@@ -1015,7 +1113,7 @@ class WebDriver {
/**
* Interface for navigating back and forth in the browser history.
*
- * This class should never be instantiated directly. Insead, obtain an instance
+ * This class should never be instantiated directly. Instead, obtain an instance
* with
*
* webdriver.navigate()
@@ -1035,7 +1133,7 @@ class Navigation {
/**
* Schedules a command to navigate to a new URL.
* @param {string} url The URL to navigate to.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the URL has been loaded.
*/
to(url) {
@@ -1047,7 +1145,7 @@ class Navigation {
/**
* Schedules a command to move backwards in the browser history.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the navigation event has completed.
*/
back() {
@@ -1058,7 +1156,7 @@ class Navigation {
/**
* Schedules a command to move forwards in the browser history.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the navigation event has completed.
*/
forward() {
@@ -1069,7 +1167,7 @@ class Navigation {
/**
* Schedules a command to refresh the current page.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the navigation event has completed.
*/
refresh() {
@@ -1116,7 +1214,7 @@ class Options {
* });
*
* @param {!Options.Cookie} spec Defines the cookie to add.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the cookie has been added to the page.
* @throws {error.InvalidArgumentError} if any of the cookie parameters are
* invalid.
@@ -1171,7 +1269,7 @@ class Options {
/**
* Schedules a command to delete all cookies visible to the current page.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when all cookies have been deleted.
*/
deleteAllCookies() {
@@ -1185,7 +1283,7 @@ class Options {
* is a no-op if there is no cookie with the given name visible to the current
* page.
* @param {string} name The name of the cookie to delete.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the cookie has been deleted.
*/
deleteCookie(name) {
@@ -1199,7 +1297,7 @@ class Options {
* Schedules a command to retrieve all cookies visible to the current page.
* Each cookie will be returned as a JSON object as described by the WebDriver
* wire protocol.
- * @return {!promise.Promise<!Array<!Options.Cookie>>} A promise that will be
+ * @return {!promise.Thenable<!Array<!Options.Cookie>>} A promise that will be
* resolved with the cookies visible to the current browsing context.
*/
getCookies() {
@@ -1214,7 +1312,7 @@ class Options {
* described by the WebDriver wire protocol.
*
* @param {string} name The name of the cookie to retrieve.
- * @return {!promise.Promise<?Options.Cookie>} A promise that will be resolved
+ * @return {!promise.Thenable<?Options.Cookie>} A promise that will be resolved
* with the named cookie, or `null` if there is no such cookie.
*/
getCookie(name) {
@@ -1365,7 +1463,7 @@ class Timeouts {
* slower location strategies like XPath.
*
* @param {number} ms The amount of time to wait, in milliseconds.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the implicit wait timeout has been set.
*/
implicitlyWait(ms) {
@@ -1378,7 +1476,7 @@ class Timeouts {
* less than or equal to 0, the script will be allowed to run indefinitely.
*
* @param {number} ms The amount of time to wait, in milliseconds.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the script timeout has been set.
*/
setScriptTimeout(ms) {
@@ -1391,7 +1489,7 @@ class Timeouts {
* indefinite.
*
* @param {number} ms The amount of time to wait, in milliseconds.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the timeout has been set.
*/
pageLoadTimeout(ms) {
@@ -1411,7 +1509,7 @@ class Timeouts {
/**
* An interface for managing the current window.
*
- * This class should never be instantiated directly. Insead, obtain an instance
+ * This class should never be instantiated directly. Instead, obtain an instance
* with
*
* webdriver.manage().window()
@@ -1432,7 +1530,7 @@ class Window {
/**
* Retrieves the window's current position, relative to the top left corner of
* the screen.
- * @return {!promise.Promise.<{x: number, y: number}>} A promise
+ * @return {!promise.Thenable<{x: number, y: number}>} A promise
* that will be resolved with the window's position in the form of a
* {x:number, y:number} object literal.
*/
@@ -1449,7 +1547,7 @@ class Window {
* side of the screen.
* @param {number} y The desired vertical position, relative to the top of the
* of the screen.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the command has completed.
*/
setPosition(x, y) {
@@ -1463,7 +1561,7 @@ class Window {
/**
* Retrieves the window's current size.
- * @return {!promise.Promise<{width: number, height: number}>} A
+ * @return {!promise.Thenable<{width: number, height: number}>} A
* promise that will be resolved with the window's size in the form of a
* {width:number, height:number} object literal.
*/
@@ -1478,7 +1576,7 @@ class Window {
* Resizes the current window.
* @param {number} width The desired window width.
* @param {number} height The desired window height.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the command has completed.
*/
setSize(width, height) {
@@ -1492,7 +1590,7 @@ class Window {
/**
* Maximizes the current window.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the command has completed.
*/
maximize() {
@@ -1534,7 +1632,7 @@ class Logs {
* entries since the last call, or from the start of the session.
*
* @param {!logging.Type} type The desired log type.
- * @return {!promise.Promise.<!Array.<!logging.Entry>>} A
+ * @return {!promise.Thenable<!Array.<!logging.Entry>>} A
* promise that will resolve to a list of log entries for the specified
* type.
*/
@@ -1557,7 +1655,7 @@ class Logs {
/**
* Retrieves the log types available to this driver.
- * @return {!promise.Promise<!Array<!logging.Type>>} A
+ * @return {!promise.Thenable<!Array<!logging.Type>>} A
* promise that will resolve to a list of available log types.
*/
getAvailableLogTypes() {
@@ -1604,7 +1702,7 @@ class TargetLocator {
/**
* Schedules a command to switch focus of all future commands to the topmost
* frame on the page.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the driver has changed focus to the default content.
*/
defaultContent() {
@@ -1630,7 +1728,7 @@ class TargetLocator {
* rejected with a {@linkplain error.NoSuchFrameError}.
*
* @param {(number|WebElement|null)} id The frame locator.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the driver has changed focus to the specified frame.
*/
frame(id) {
@@ -1650,7 +1748,7 @@ class TargetLocator {
*
* @param {string} nameOrHandle The name or window handle of the window to
* switch focus to.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the driver has changed focus to the specified window.
*/
window(nameOrHandle) {
@@ -1714,8 +1812,8 @@ class WebElement {
/** @private {!WebDriver} */
this.driver_ = driver;
- /** @private {!promise.Promise<string>} */
- this.id_ = promise.fulfilled(id);
+ /** @private {!promise.Thenable<string>} */
+ this.id_ = driver.controlFlow().promise(resolve => resolve(id));
}
/**
@@ -1762,12 +1860,12 @@ class WebElement {
*
* @param {!WebElement} a A WebElement.
* @param {!WebElement} b A WebElement.
- * @return {!promise.Promise<boolean>} A promise that will be
+ * @return {!promise.Thenable<boolean>} A promise that will be
* resolved to whether the two WebElements are equal.
*/
static equals(a, b) {
if (a === b) {
- return promise.fulfilled(true);
+ return a.driver_.controlFlow().promise(resolve => resolve(true));
}
let ids = [a.getId(), b.getId()];
return promise.all(ids).then(function(ids) {
@@ -1791,7 +1889,7 @@ class WebElement {
}
/**
- * @return {!promise.Promise<string>} A promise that resolves to
+ * @return {!promise.Thenable<string>} A promise that resolves to
* the server-assigned opaque ID assigned to this element.
*/
getId() {
@@ -1812,14 +1910,14 @@ class WebElement {
*
* @param {!command.Command} command The command to schedule.
* @param {string} description A description of the command for debugging.
- * @return {!promise.Promise<T>} A promise that will be resolved
+ * @return {!promise.Thenable<T>} A promise that will be resolved
* with the command result.
* @template T
* @see WebDriver#schedule
* @private
*/
schedule_(command, description) {
- command.setParameter('id', this.getId());
+ command.setParameter('id', this);
return this.driver_.schedule(command, description);
}
@@ -1878,7 +1976,7 @@ class WebElement {
*
* @param {!(by.By|Function)} locator The locator strategy to use when
* searching for the element.
- * @return {!promise.Promise<!Array<!WebElement>>} A
+ * @return {!promise.Thenable<!Array<!WebElement>>} A
* promise that will resolve to an array of WebElements.
*/
findElements(locator) {
@@ -1897,7 +1995,7 @@ class WebElement {
/**
* Schedules a command to click on this element.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the click command has completed.
*/
click() {
@@ -1959,7 +2057,7 @@ class WebElement {
* sequence of keys to type. Number keys may be referenced numerically or
* by string (1 or '1'). All arguments will be joined into a single
* sequence.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when all keys have been typed.
*/
sendKeys(var_args) {
@@ -1993,7 +2091,7 @@ class WebElement {
keys.catch(function() {});
var element = this;
- return this.driver_.flow_.execute(function() {
+ return this.getDriver().controlFlow().execute(function() {
return keys.then(function(keys) {
return element.driver_.fileDetector_
.handleFile(element.driver_, keys.join(''));
@@ -2008,7 +2106,7 @@ class WebElement {
/**
* Schedules a command to query for the tag/node name of this element.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the element's tag name.
*/
getTagName() {
@@ -2029,7 +2127,7 @@ class WebElement {
*
* @param {string} cssStyleProperty The name of the CSS style property to look
* up.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the requested CSS value.
*/
getCssValue(cssStyleProperty) {
@@ -2065,7 +2163,7 @@ class WebElement {
* - "readonly"
*
* @param {string} attributeName The name of the attribute to query.
- * @return {!promise.Promise<?string>} A promise that will be
+ * @return {!promise.Thenable<?string>} A promise that will be
* resolved with the attribute's value. The returned value will always be
* either a string or null.
*/
@@ -2080,7 +2178,7 @@ class WebElement {
* Get the visible (i.e. not hidden by CSS) innerText of this element,
* including sub-elements, without any leading or trailing whitespace.
*
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved with the element's visible text.
*/
getText() {
@@ -2092,7 +2190,7 @@ class WebElement {
/**
* Schedules a command to compute the size of this element's bounding box, in
* pixels.
- * @return {!promise.Promise.<{width: number, height: number}>} A
+ * @return {!promise.Thenable<{width: number, height: number}>} A
* promise that will be resolved with the element's size as a
* {@code {width:number, height:number}} object.
*/
@@ -2104,7 +2202,7 @@ class WebElement {
/**
* Schedules a command to compute the location of this element in page space.
- * @return {!promise.Promise.<{x: number, y: number}>} A promise that
+ * @return {!promise.Thenable<{x: number, y: number}>} A promise that
* will be resolved to the element's location as a
* {@code {x:number, y:number}} object.
*/
@@ -2117,7 +2215,7 @@ class WebElement {
/**
* Schedules a command to query whether the DOM element represented by this
* instance is enabled, as dicted by the {@code disabled} attribute.
- * @return {!promise.Promise<boolean>} A promise that will be
+ * @return {!promise.Thenable<boolean>} A promise that will be
* resolved with whether this element is currently enabled.
*/
isEnabled() {
@@ -2128,7 +2226,7 @@ class WebElement {
/**
* Schedules a command to query whether this element is selected.
- * @return {!promise.Promise<boolean>} A promise that will be
+ * @return {!promise.Thenable<boolean>} A promise that will be
* resolved with whether this element is currently selected.
*/
isSelected() {
@@ -2141,7 +2239,7 @@ class WebElement {
* Schedules a command to submit the form containing this element (or this
* element if it is a FORM element). This command is a no-op if the element is
* not contained in a form.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the form has been submitted.
*/
submit() {
@@ -2154,7 +2252,7 @@ class WebElement {
* Schedules a command to clear the `value` of this element. This command has
* no effect if the underlying DOM element is neither a text INPUT element
* nor a TEXTAREA element.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when the element has been cleared.
*/
clear() {
@@ -2165,7 +2263,7 @@ class WebElement {
/**
* Schedules a command to test whether this element is currently displayed.
- * @return {!promise.Promise<boolean>} A promise that will be
+ * @return {!promise.Thenable<boolean>} A promise that will be
* resolved with whether this element is currently visible on the page.
*/
isDisplayed() {
@@ -2181,7 +2279,7 @@ class WebElement {
* @param {boolean=} opt_scroll Optional argument that indicates whether the
* element should be scrolled into view before taking a screenshot.
* Defaults to false.
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved to the screenshot as a base-64 encoded PNG.
*/
takeScreenshot(opt_scroll) {
@@ -2206,24 +2304,30 @@ class WebElement {
* return el.click();
* });
*
- * @implements {promise.Thenable<!WebElement>}
+ * @implements {promise.CancellableThenable<!WebElement>}
* @final
*/
class WebElementPromise extends WebElement {
/**
* @param {!WebDriver} driver The parent WebDriver instance for this
* element.
- * @param {!promise.Promise<!WebElement>} el A promise
+ * @param {!promise.Thenable<!WebElement>} el A promise
* that will resolve to the promised element.
*/
constructor(driver, el) {
super(driver, 'unused');
- /** @override */
- this.cancel = el.cancel.bind(el);
-
- /** @override */
- this.isPending = el.isPending.bind(el);
+ /**
+ * Cancel operation is only supported if the wrapped thenable is also
+ * cancellable.
+ * @param {(string|Error)=} opt_reason
+ * @override
+ */
+ this.cancel = function(opt_reason) {
+ if (promise.CancellableThenable.isImplementation(el)) {
+ /** @type {!promise.CancellableThenable} */(el).cancel(opt_reason);
+ }
+ }
/** @override */
this.then = el.then.bind(el);
@@ -2231,9 +2335,6 @@ class WebElementPromise extends WebElement {
/** @override */
this.catch = el.catch.bind(el);
- /** @override */
- this.finally = el.finally.bind(el);
-
/**
* Defers returning the element ID until the wrapped WebElement has been
* resolved.
@@ -2246,7 +2347,7 @@ class WebElementPromise extends WebElement {
};
}
}
-promise.Thenable.addImplementation(WebElementPromise);
+promise.CancellableThenable.addImplementation(WebElementPromise);
//////////////////////////////////////////////////////////////////////////////
@@ -2272,15 +2373,15 @@ class Alert {
/** @private {!WebDriver} */
this.driver_ = driver;
- /** @private {!promise.Promise<string>} */
- this.text_ = promise.fulfilled(text);
+ /** @private {!promise.Thenable<string>} */
+ this.text_ = driver.controlFlow().promise(resolve => resolve(text));
}
/**
* Retrieves the message text displayed with this alert. For instance, if the
* alert were opened with alert("hello"), then this would return "hello".
*
- * @return {!promise.Promise<string>} A promise that will be
+ * @return {!promise.Thenable<string>} A promise that will be
* resolved to the text displayed with this alert.
*/
getText() {
@@ -2294,7 +2395,7 @@ class Alert {
*
* @param {string} username The username to send.
* @param {string} password The password to send.
- * @return {!promise.Promise<void>} A promise that will be resolved when this
+ * @return {!promise.Thenable<void>} A promise that will be resolved when this
* command has completed.
*/
authenticateAs(username, password) {
@@ -2307,7 +2408,7 @@ class Alert {
/**
* Accepts this alert.
*
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when this command has completed.
*/
accept() {
@@ -2319,7 +2420,7 @@ class Alert {
/**
* Dismisses this alert.
*
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when this command has completed.
*/
dismiss() {
@@ -2334,7 +2435,7 @@ class Alert {
* window.confirm).
*
* @param {string} text The text to set.
- * @return {!promise.Promise<void>} A promise that will be resolved
+ * @return {!promise.Thenable<void>} A promise that will be resolved
* when this command has completed.
*/
sendKeys(text) {
@@ -2357,7 +2458,7 @@ class Alert {
* return alert.dismiss();
* });
*
- * @implements {promise.Thenable.<!webdriver.Alert>}
+ * @implements {promise.CancellableThenable<!webdriver.Alert>}
* @final
*/
class AlertPromise extends Alert {
@@ -2370,11 +2471,17 @@ class AlertPromise extends Alert {
constructor(driver, alert) {
super(driver, 'unused');
- /** @override */
- this.cancel = alert.cancel.bind(alert);
-
- /** @override */
- this.isPending = alert.isPending.bind(alert);
+ /**
+ * Cancel operation is only supported if the wrapped thenable is also
+ * cancellable.
+ * @param {(string|Error)=} opt_reason
+ * @override
+ */
+ this.cancel = function(opt_reason) {
+ if (promise.CancellableThenable.isImplementation(alert)) {
+ /** @type {!promise.CancellableThenable} */(alert).cancel(opt_reason);
+ }
+ };
/** @override */
this.then = alert.then.bind(alert);
@@ -2382,9 +2489,6 @@ class AlertPromise extends Alert {
/** @override */
this.catch = alert.catch.bind(alert);
- /** @override */
- this.finally = alert.finally.bind(alert);
-
/**
* Defer returning text until the promised alert has been resolved.
* @override
@@ -2436,7 +2540,7 @@ class AlertPromise extends Alert {
};
}
}
-promise.Thenable.addImplementation(AlertPromise);
+promise.CancellableThenable.addImplementation(AlertPromise);
// PUBLIC API
@@ -2451,6 +2555,7 @@ module.exports = {
Options: Options,
TargetLocator: TargetLocator,
Timeouts: Timeouts,
+ IWebDriver: IWebDriver,
WebDriver: WebDriver,
WebElement: WebElement,
WebElementCondition: WebElementCondition,
diff --git a/node_modules/selenium-webdriver/opera.js b/node_modules/selenium-webdriver/opera.js
index 7106511f7..cc84f2af5 100644
--- a/node_modules/selenium-webdriver/opera.js
+++ b/node_modules/selenium-webdriver/opera.js
@@ -348,14 +348,17 @@ class Options {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new session for Opera.
+ *
* @param {(capabilities.Capabilities|Options)=} opt_config The configuration
* options.
* @param {remote.DriverService=} opt_service The session to use; will use
* the {@link getDefaultService default service} by default.
* @param {promise.ControlFlow=} opt_flow The control flow to use,
* or {@code null} to use the currently active flow.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_service, opt_flow) {
+ static createSession(opt_config, opt_service, opt_flow) {
var service = opt_service || getDefaultService();
var client = service.start().then(url => new http.HttpClient(url));
var executor = new http.Executor(client);
@@ -379,8 +382,8 @@ class Driver extends webdriver.WebDriver {
caps = options.toCapabilities(caps);
}
- var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
- super(driver.getSession(), executor, driver.controlFlow());
+ return /** @type {!Driver} */(
+ webdriver.WebDriver.createSession(executor, caps, opt_flow, this));
}
/**
diff --git a/node_modules/selenium-webdriver/package.json b/node_modules/selenium-webdriver/package.json
index 8f94d5300..5c3935833 100644
--- a/node_modules/selenium-webdriver/package.json
+++ b/node_modules/selenium-webdriver/package.json
@@ -1,6 +1,6 @@
{
"name": "selenium-webdriver",
- "version": "3.0.0-beta-3",
+ "version": "3.0.1",
"description": "The official WebDriver JavaScript bindings from the Selenium project",
"license": "Apache-2.0",
"keywords": [
@@ -20,24 +20,23 @@
"url": "https://github.com/SeleniumHQ/selenium.git"
},
"engines": {
- "node": ">= 4.2.x"
+ "node": ">= 6.9.0"
},
"dependencies": {
- "adm-zip": "0.4.4",
- "rimraf": "^2.2.8",
- "tmp": "0.0.24",
- "ws": "^1.0.1",
- "xml2js": "0.4.4"
+ "adm-zip": "^0.4.7",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
},
"devDependencies": {
- "express": "^4.11.2",
- "mocha": ">= 1.21.x",
- "multer": "^0.1.7",
- "promises-aplus-tests": "^2.1.0",
- "serve-index": "^1.6.1",
- "sinon": "^1.17.2"
+ "express": "^4.14.0",
+ "mocha": "^3.1.2",
+ "multer": "^1.2.0",
+ "promises-aplus-tests": "^2.1.2",
+ "serve-index": "^1.8.0",
+ "sinon": "^1.17.6"
},
"scripts": {
- "test": "mocha --harmony -t 600000 --recursive test"
+ "test": "mocha -t 600000 --recursive test"
}
}
diff --git a/node_modules/selenium-webdriver/phantomjs.js b/node_modules/selenium-webdriver/phantomjs.js
index 2de5364de..baa7cb378 100644
--- a/node_modules/selenium-webdriver/phantomjs.js
+++ b/node_modules/selenium-webdriver/phantomjs.js
@@ -148,6 +148,8 @@ function createExecutor(url) {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new PhantomJS session.
+ *
* @param {capabilities.Capabilities=} opt_capabilities The desired
* capabilities.
* @param {promise.ControlFlow=} opt_flow The control flow to use,
@@ -155,8 +157,9 @@ class Driver extends webdriver.WebDriver {
* @param {string=} opt_logFile Path to the log file for the phantomjs
* executable's output. For convenience, this may be set at runtime with
* the `SELENIUM_PHANTOMJS_LOG` environment variable.
+ * @return {!Driver} A new driver reference.
*/
- constructor(opt_capabilities, opt_flow, opt_logFile) {
+ static createSession(opt_capabilities, opt_flow, opt_logFile) {
// TODO: add an Options class for consistency with the other driver types.
var caps = opt_capabilities || capabilities.Capabilities.phantomjs();
@@ -207,7 +210,6 @@ class Driver extends webdriver.WebDriver {
var port = portprober.findFreePort();
var service = new remote.DriverService(exe, {
port: port,
- stdio: 'inherit',
args: Promise.resolve(port).then(function(port) {
args.push('--webdriver=' + port);
return args;
@@ -215,17 +217,8 @@ class Driver extends webdriver.WebDriver {
});
var executor = createExecutor(service.start());
- var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
-
- super(driver.getSession(), executor, driver.controlFlow());
-
- var boundQuit = this.quit.bind(this);
-
- /** @override */
- this.quit = function() {
- let killService = () => service.kill();
- return boundQuit().then(killService, killService);
- };
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
+ executor, caps, opt_flow, this, () => service.kill()));
}
/**
@@ -265,7 +258,7 @@ class Driver extends webdriver.WebDriver {
*
* @param {(string|!Function)} script The script to execute.
* @param {...*} var_args The arguments to pass to the script.
- * @return {!promise.Promise<T>} A promise that resolve to the
+ * @return {!promise.Thenable<T>} A promise that resolve to the
* script's return value.
* @template T
*/
diff --git a/node_modules/selenium-webdriver/remote/index.js b/node_modules/selenium-webdriver/remote/index.js
index ab76b4476..87dee7e8d 100644
--- a/node_modules/selenium-webdriver/remote/index.js
+++ b/node_modules/selenium-webdriver/remote/index.js
@@ -247,15 +247,18 @@ class DriverService {
pathname: self.path_
});
- return new Promise(function(fulfill, reject) {
- var ready = httpUtil.waitForServer(serverUrl, timeout)
- .then(fulfill, reject);
- earlyTermination.catch(function(e) {
- ready.cancel(/** @type {Error} */(e));
- reject(Error(e.message));
- });
- }).then(function() {
- return serverUrl;
+ return new Promise((fulfill, reject) => {
+ let cancelToken =
+ earlyTermination.catch(e => reject(Error(e.message)));
+
+ httpUtil.waitForServer(serverUrl, timeout, cancelToken)
+ .then(_ => fulfill(serverUrl), err => {
+ if (err instanceof promise.CancellationError) {
+ fulfill(serverUrl);
+ } else {
+ reject(err);
+ }
+ });
});
});
}));
@@ -283,7 +286,7 @@ class DriverService {
/**
* Schedules a task in the current control flow to stop the server if it is
* currently running.
- * @return {!promise.Promise} A promise that will be resolved when
+ * @return {!promise.Thenable} A promise that will be resolved when
* the server has been stopped.
*/
stop() {
@@ -297,8 +300,9 @@ class DriverService {
* @return {!Promise<!Array<string>>}
*/
function resolveCommandLineFlags(args) {
- return Promise.resolve(args) // Resolve the outer array.
- .then(args => Promise.all(args)); // Then resolve the individual flags.
+ // Resolve the outer array, then the individual flags.
+ return Promise.resolve(args)
+ .then(/** !Array<CommandLineFlag> */args => Promise.all(args));
}
diff --git a/node_modules/selenium-webdriver/safari.js b/node_modules/selenium-webdriver/safari.js
index bbfff06e8..97d512bc7 100644
--- a/node_modules/selenium-webdriver/safari.js
+++ b/node_modules/selenium-webdriver/safari.js
@@ -17,452 +17,21 @@
/**
* @fileoverview Defines a WebDriver client for Safari.
- *
- *
- * __Testing Older Versions of Safari__
- *
- * To test versions of Safari prior to Safari 10.0, you must install the
- * [latest version](http://selenium-release.storage.googleapis.com/index.html)
- * of the SafariDriver browser extension; using Safari for normal browsing is
- * not recommended once the extension has been installed. You can, and should,
- * disable the extension when the browser is not being used with WebDriver.
- *
- * You must also enable the use of legacy driver using the {@link Options} class.
- *
- * let options = new safari.Options()
- * .useLegacyDriver(true);
- *
- * let driver = new (require('selenium-webdriver')).Builder()
- * .forBrowser('safari')
- * .setSafariOptions(options)
- * .build();
*/
'use strict';
-const events = require('events');
-const fs = require('fs');
-const http = require('http');
-const path = require('path');
-const url = require('url');
-const util = require('util');
-const ws = require('ws');
-
+const http = require('./http');
const io = require('./io');
-const exec = require('./io/exec');
-const isDevMode = require('./lib/devmode');
-const Capabilities = require('./lib/capabilities').Capabilities;
-const Capability = require('./lib/capabilities').Capability;
+const {Capabilities, Capability} = require('./lib/capabilities');
const command = require('./lib/command');
const error = require('./lib/error');
const logging = require('./lib/logging');
const promise = require('./lib/promise');
-const Session = require('./lib/session').Session;
const Symbols = require('./lib/symbols');
const webdriver = require('./lib/webdriver');
const portprober = require('./net/portprober');
const remote = require('./remote');
-const http_ = require('./http');
-
-
-/** @const */
-const CLIENT_PATH = isDevMode
- ? path.join(__dirname,
- '../../../buck-out/gen/javascript/safari-driver/client.js')
- : path.join(__dirname, 'lib/safari/client.js');
-
-
-/** @const */
-const LIBRARY_DIR = (function() {
- if (process.platform === 'darwin') {
- return path.join('/Users', process.env['USER'], 'Library/Safari');
- } else if (process.platform === 'win32') {
- return path.join(process.env['APPDATA'], 'Apple Computer', 'Safari');
- } else {
- return '/dev/null';
- }
-})();
-
-
-/** @const */
-const SESSION_DATA_FILES = (function() {
- if (process.platform === 'darwin') {
- var libraryDir = path.join('/Users', process.env['USER'], 'Library');
- return [
- path.join(libraryDir, 'Caches/com.apple.Safari/Cache.db'),
- path.join(libraryDir, 'Cookies/Cookies.binarycookies'),
- path.join(libraryDir, 'Cookies/Cookies.plist'),
- path.join(libraryDir, 'Safari/History.plist'),
- path.join(libraryDir, 'Safari/LastSession.plist'),
- path.join(libraryDir, 'Safari/LocalStorage'),
- path.join(libraryDir, 'Safari/Databases')
- ];
- } else if (process.platform === 'win32') {
- var appDataDir = path.join(process.env['APPDATA'],
- 'Apple Computer', 'Safari');
- var localDataDir = path.join(process.env['LOCALAPPDATA'],
- 'Apple Computer', 'Safari');
- return [
- path.join(appDataDir, 'History.plist'),
- path.join(appDataDir, 'LastSession.plist'),
- path.join(appDataDir, 'Cookies/Cookies.plist'),
- path.join(appDataDir, 'Cookies/Cookies.binarycookies'),
- path.join(localDataDir, 'Cache.db'),
- path.join(localDataDir, 'Databases'),
- path.join(localDataDir, 'LocalStorage')
- ];
- } else {
- return [];
- }
-})();
-
-
-/** @typedef {{port: number, address: string, family: string}} */
-var Host;
-
-
-/**
- * A basic HTTP/WebSocket server used to communicate with the legacy SafariDriver
- * browser extension.
- */
-class Server extends events.EventEmitter {
- constructor() {
- super();
- var server = http.createServer(function(req, res) {
- if (req.url === '/favicon.ico') {
- res.writeHead(204);
- res.end();
- return;
- }
-
- var query = url.parse(/** @type {string} */(req.url)).query || '';
- if (query.indexOf('url=') == -1) {
- var address = server.address()
- var host = address.address + ':' + address.port;
- res.writeHead(
- 302, {'Location': 'http://' + host + '?url=ws://' + host});
- res.end();
- }
-
- fs.readFile(CLIENT_PATH, 'utf8', function(err, data) {
- if (err) {
- res.writeHead(500, {'Content-Type': 'text/plain'});
- res.end(err.stack);
- return;
- }
- var content = '<!DOCTYPE html><body><script>' + data + '</script>';
- res.writeHead(200, {
- 'Content-Type': 'text/html; charset=utf-8',
- 'Content-Length': Buffer.byteLength(content, 'utf8'),
- });
- res.end(content);
- });
- });
-
- var wss = new ws.Server({server: server});
- wss.on('connection', this.emit.bind(this, 'connection'));
-
- /**
- * Starts the server on a random port.
- * @return {!Promise<Host>} A promise that will resolve with the server host
- * when it has fully started.
- */
- this.start = function() {
- if (server.address()) {
- return Promise.resolve(server.address());
- }
- return portprober.findFreePort('localhost').then(function(port) {
- return promise.checkedNodeCall(
- server.listen.bind(server, port, 'localhost'));
- }).then(function() {
- return server.address();
- });
- };
-
- /**
- * Stops the server.
- * @return {!Promise} A promise that will resolve when the server has closed
- * all connections.
- */
- this.stop = function() {
- return new Promise(fulfill => server.close(fulfill));
- };
-
- /**
- * @return {Host} This server's host info.
- * @throws {Error} If the server is not running.
- */
- this.address = function() {
- var addr = server.address();
- if (!addr) {
- throw Error('There server is not running!');
- }
- return addr;
- };
- }
-}
-
-
-/**
- * @return {!Promise<string>} A promise that will resolve with the path
- * to Safari on the current system.
- */
-function findSafariExecutable() {
- switch (process.platform) {
- case 'darwin':
- return Promise.resolve('/Applications/Safari.app/Contents/MacOS/Safari');
-
- case 'win32':
- var files = [
- process.env['PROGRAMFILES'] || '\\Program Files',
- process.env['PROGRAMFILES(X86)'] || '\\Program Files (x86)'
- ].map(function(prefix) {
- return path.join(prefix, 'Safari\\Safari.exe');
- });
- return io.exists(files[0]).then(function(exists) {
- return exists ? files[0] : io.exists(files[1]).then(function(exists) {
- if (exists) {
- return files[1];
- }
- throw Error('Unable to find Safari on the current system');
- });
- });
-
- default:
- return Promise.reject(
- Error('Safari is not supported on the current platform: ' +
- process.platform));
- }
-}
-
-
-/**
- * @param {string} serverUrl The URL to connect to.
- * @return {!Promise<string>} A promise for the path to a file that Safari can
- * open on start-up to trigger a new connection to the WebSocket server.
- */
-function createConnectFile(serverUrl) {
- return io.tmpFile({postfix: '.html'}).then(function(f) {
- let contents =
- `<!DOCTYPE html><script>window.location = "${serverUrl}";</script>`;
- return io.write(f, contents).then(() => f);
- });
-}
-
-
-/**
- * Deletes all session data files if so desired.
- * @param {!Object} desiredCapabilities .
- * @return {!Array<!Promise>} A list of promises for the deleted files.
- */
-function cleanSession(desiredCapabilities) {
- if (!desiredCapabilities) {
- return [];
- }
- var options = desiredCapabilities[OPTIONS_CAPABILITY_KEY];
- if (!options) {
- return [];
- }
- if (!options['cleanSession']) {
- return [];
- }
- return SESSION_DATA_FILES.map(function(file) {
- return io.unlink(file);
- });
-}
-
-
-/** @return {string} . */
-function getRandomString() {
- let seed = Date.now();
- return Math.floor(Math.random() * seed).toString(36)
- + Math.abs(Math.floor(Math.random() * seed) ^ Date.now()).toString(36);
-}
-
-
-/**
- * @implements {command.Executor}
- */
-class CommandExecutor {
- constructor() {
- this.server_ = null;
-
- /** @private {ws.WebSocket} */
- this.socket_ = null;
-
- /** @private {?string} 8*/
- this.sessionId_ = null;
-
- /** @private {Promise<!exec.Command>} */
- this.safari_ = null;
-
- /** @private {!logging.Logger} */
- this.log_ = logging.getLogger('webdriver.safari');
- }
-
- /** @override */
- execute(cmd) {
- var self = this;
- return new promise.Promise(function(fulfill, reject) {
- var safariCommand = JSON.stringify({
- 'origin': 'webdriver',
- 'type': 'command',
- 'command': {
- 'id': getRandomString(),
- 'name': cmd.getName(),
- 'parameters': cmd.getParameters()
- }
- });
-
- switch (cmd.getName()) {
- case command.Name.NEW_SESSION:
- self.startSafari_(cmd)
- .then(() => self.sendCommand_(safariCommand))
- .then(caps => new Session(self.sessionId(), caps))
- .then(fulfill, reject);
- break;
-
- case command.Name.DESCRIBE_SESSION:
- self.sendCommand_(safariCommand)
- .then(caps => new Session(self.sessionId(), caps))
- .then(fulfill, reject);
- break;
-
- case command.Name.QUIT:
- self.destroySession_().then(() => fulfill(null), reject);
- break;
-
- default:
- self.sendCommand_(safariCommand).then(fulfill, reject);
- break;
- }
- });
- }
-
- /**
- * @return {string} The static session ID for this executor's current
- * connection.
- */
- sessionId() {
- if (!this.sessionId_) {
- throw Error('not currently connected')
- }
- return this.sessionId_;
- }
-
- /**
- * @param {string} data .
- * @return {!promise.Promise} .
- * @private
- */
- sendCommand_(data) {
- let self = this;
- return new promise.Promise(function(fulfill, reject) {
- // TODO: support reconnecting with the extension.
- if (!self.socket_) {
- self.destroySession_().finally(function() {
- reject(Error('The connection to the SafariDriver was closed'));
- });
- return;
- }
-
- self.log_.fine(() => '>>> ' + data);
- self.socket_.send(data, function(err) {
- if (err) {
- reject(err);
- return;
- }
- });
-
- self.socket_.once('message', function(data) {
- try {
- self.log_.fine(() => '<<< ' + data);
- data = JSON.parse(data);
- } catch (ex) {
- reject(Error('Failed to parse driver message: ' + data));
- return;
- }
-
- try {
- error.checkLegacyResponse(data['response']);
- fulfill(data['response']['value']);
- } catch (ex) {
- reject(ex);
- }
- });
- });
- }
-
- /**
- * @param {!command.Command} command .
- * @private
- */
- startSafari_(command) {
- this.server_ = new Server();
-
- this.safari_ = this.server_.start().then(function(address) {
- var tasks = cleanSession(
- /** @type {!Object} */(
- command.getParameters()['desiredCapabilities']));
- tasks.push(
- findSafariExecutable(),
- createConnectFile(
- 'http://' + address.address + ':' + address.port));
-
- return Promise.all(tasks).then(function(/** !Array<string> */tasks) {
- var exe = tasks[tasks.length - 2];
- var html = tasks[tasks.length - 1];
- return exec(exe, {args: [html]});
- });
- });
-
- return new Promise((resolve, reject) => {
- let start = Date.now();
- let timer = setTimeout(function() {
- let elapsed = Date.now() - start;
- reject(Error(
- 'Failed to connect to the SafariDriver after ' + elapsed +
- ' ms; Have you installed the latest extension from ' +
- 'http://selenium-release.storage.googleapis.com/index.html?'));
- }, 10 * 1000);
-
- this.server_.once('connection', socket => {
- clearTimeout(timer);
- this.socket_ = socket;
- this.sessionId_ = getRandomString();
- socket.once('close', () => {
- this.socket_ = null;
- this.sessionId_ = null;
- });
- resolve();
- });
- });
- }
-
- /**
- * Destroys the active session by stopping the WebSocket server and killing the
- * Safari subprocess.
- * @private
- */
- destroySession_() {
- var tasks = [];
- if (this.server_) {
- tasks.push(this.server_.stop());
- }
- if (this.safari_) {
- tasks.push(this.safari_.then(function(safari) {
- safari.kill();
- return safari.result();
- }));
- }
- var self = this;
- return promise.all(tasks).finally(function() {
- self.server_ = null;
- self.socket_ = null;
- self.safari_ = null;
- });
- }
-}
/**
@@ -498,10 +67,7 @@ class ServiceBuilder extends remote.DriverService.Builder {
}
-/** @const */
const OPTIONS_CAPABILITY_KEY = 'safari.options';
-const LEGACY_DRIVER_CAPABILITY_KEY = 'legacyDriver'
-
/**
@@ -517,9 +83,6 @@ class Options {
/** @private {?./lib/capabilities.ProxyConfig} */
this.proxy_ = null;
-
- /** @private {boolean} */
- this.legacyDriver_ = false;
}
/**
@@ -546,10 +109,6 @@ class Options {
options.setLoggingPrefs(capabilities.get(Capability.LOGGING_PREFS));
}
- if (capabilities.has(LEGACY_DRIVER_CAPABILITY_KEY)) {
- options.useLegacyDriver(capabilities.get(LEGACY_DRIVER_CAPABILITY_KEY));
- }
-
return options;
}
@@ -569,18 +128,6 @@ class Options {
}
/**
- * Sets whether to use the legacy driver from the Selenium project. This option
- * is disabled by default.
- *
- * @param {boolean} enable Whether to enable the legacy driver.
- * @return {!Options} A self reference.
- */
- useLegacyDriver(enable) {
- this.legacyDriver_ = enable;
- return this;
- }
-
- /**
* Sets the logging preferences for the new session.
* @param {!./lib/logging.Preferences} prefs The logging preferences.
* @return {!Options} A self reference.
@@ -618,7 +165,6 @@ class Options {
if (this.options_) {
caps.set(OPTIONS_CAPABILITY_KEY, this);
}
- caps.set(LEGACY_DRIVER_CAPABILITY_KEY, this.legacyDriver_);
return caps;
}
@@ -645,49 +191,28 @@ class Options {
*/
class Driver extends webdriver.WebDriver {
/**
+ * Creates a new Safari session.
+ *
* @param {(Options|Capabilities)=} opt_config The configuration
* options for the new session.
* @param {promise.ControlFlow=} opt_flow The control flow to create
* the driver under.
+ * @return {!Driver} A new driver instance.
*/
- constructor(opt_config, opt_flow) {
- let caps,
- executor,
- useLegacyDriver = false,
- onQuit = () => {};
-
+ static createSession(opt_config, opt_flow) {
+ let caps;
if (opt_config instanceof Options) {
caps = opt_config.toCapabilities();
} else {
caps = opt_config || Capabilities.safari()
}
- if (caps.has(LEGACY_DRIVER_CAPABILITY_KEY)) {
- useLegacyDriver = caps.get(LEGACY_DRIVER_CAPABILITY_KEY);
- caps.delete(LEGACY_DRIVER_CAPABILITY_KEY);
- }
-
- if (useLegacyDriver) {
- executor = new CommandExecutor();
- } else {
- let service = new ServiceBuilder().build();
-
- executor = new http_.Executor(
- service.start()
- .then(url => new http_.HttpClient(url))
- );
-
- onQuit = () => service.kill();
- }
-
- let driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
-
- super(driver.getSession(), executor, driver.controlFlow());
+ let service = new ServiceBuilder().build();
+ let executor = new http.Executor(
+ service.start().then(url => new http.HttpClient(url)));
- /** @override */
- this.quit = () => {
- return super.quit().finally(onQuit);
- };
+ return /** @type {!Driver} */(webdriver.WebDriver.createSession(
+ executor, caps, opt_flow, this, () => service.kill()));
}
}
diff --git a/node_modules/selenium-webdriver/test/actions_test.js b/node_modules/selenium-webdriver/test/actions_test.js
index 7ea0047ad..ef218f7d7 100644
--- a/node_modules/selenium-webdriver/test/actions_test.js
+++ b/node_modules/selenium-webdriver/test/actions_test.js
@@ -26,28 +26,26 @@ var Browser = require('..').Browser,
test.suite(function(env) {
var driver;
- test.beforeEach(function() { driver = env.builder().build(); });
- test.afterEach(function() { driver.quit(); });
+ test.beforeEach(function*() { driver = yield env.builder().build(); });
+ test.afterEach(function() { return driver.quit(); });
test.ignore(
env.browsers(Browser.FIREFOX, Browser.PHANTOM_JS, Browser.SAFARI)).
describe('WebDriver.actions()', function() {
- test.it('can move to and click element in an iframe', function() {
- driver.get(fileServer.whereIs('click_tests/click_in_iframe.html'));
+ test.it('can move to and click element in an iframe', function*() {
+ yield driver.get(fileServer.whereIs('click_tests/click_in_iframe.html'));
- driver.wait(until.elementLocated(By.id('ifr')), 5000)
- .then(function(frame) {
- driver.switchTo().frame(frame);
- });
+ yield driver.wait(until.elementLocated(By.id('ifr')), 5000)
+ .then(frame => driver.switchTo().frame(frame));
- var link = driver.findElement(By.id('link'));
- driver.actions()
+ let link = yield driver.findElement(By.id('link'));
+ yield driver.actions()
.mouseMove(link)
.click()
.perform();
- driver.wait(until.titleIs('Submitted Successfully!'), 5000);
+ return driver.wait(until.titleIs('Submitted Successfully!'), 5000);
});
});
diff --git a/node_modules/selenium-webdriver/test/chrome/options_test.js b/node_modules/selenium-webdriver/test/chrome/options_test.js
index 28c4faa91..d36a044e4 100644
--- a/node_modules/selenium-webdriver/test/chrome/options_test.js
+++ b/node_modules/selenium-webdriver/test/chrome/options_test.js
@@ -205,22 +205,22 @@ test.suite(function(env) {
var driver;
test.afterEach(function() {
- driver.quit();
+ return driver.quit();
});
describe('Chrome options', function() {
- test.it('can start Chrome with custom args', function() {
+ test.it('can start Chrome with custom args', function*() {
var options = new chrome.Options().
addArguments('user-agent=foo;bar');
- driver = env.builder().
- setChromeOptions(options).
- build();
+ driver = yield env.builder()
+ .setChromeOptions(options)
+ .build();
- driver.get(test.Pages.ajaxyPage);
+ yield driver.get(test.Pages.ajaxyPage);
- var userAgent = driver.executeScript(
- 'return window.navigator.userAgent');
+ var userAgent =
+ yield driver.executeScript('return window.navigator.userAgent');
assert(userAgent).equalTo('foo;bar');
});
});
diff --git a/node_modules/selenium-webdriver/test/cookie_test.js b/node_modules/selenium-webdriver/test/cookie_test.js
index 3912fdbee..40f7d9b57 100644
--- a/node_modules/selenium-webdriver/test/cookie_test.js
+++ b/node_modules/selenium-webdriver/test/cookie_test.js
@@ -29,126 +29,127 @@ var test = require('../lib/test'),
test.suite(function(env) {
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
test.after(function() {
- driver.quit();
+ return driver.quit();
});
- test.ignore(env.browsers(Browser.SAFARI)). // Cookie handling is broken.
+ // Cookie handling is broken.
+ test.ignore(env.browsers(Browser.PHANTOM_JS, Browser.SAFARI)).
describe('Cookie Management;', function() {
- test.beforeEach(function() {
- driver.get(fileserver.Pages.ajaxyPage);
- driver.manage().deleteAllCookies();
- assertHasCookies();
+ test.beforeEach(function*() {
+ yield driver.get(fileserver.Pages.ajaxyPage);
+ yield driver.manage().deleteAllCookies();
+ return assertHasCookies();
});
- test.it('can add new cookies', function() {
+ test.it('can add new cookies', function*() {
var cookie = createCookieSpec();
- driver.manage().addCookie(cookie);
- driver.manage().getCookie(cookie.name).then(function(actual) {
+ yield driver.manage().addCookie(cookie);
+ yield driver.manage().getCookie(cookie.name).then(function(actual) {
assert.equal(actual.value, cookie.value);
});
});
- test.it('can get all cookies', function() {
+ test.it('can get all cookies', function*() {
var cookie1 = createCookieSpec();
var cookie2 = createCookieSpec();
- driver.manage().addCookie(cookie1);
- driver.manage().addCookie(cookie2);
+ yield driver.manage().addCookie(cookie1);
+ yield driver.manage().addCookie(cookie2);
- assertHasCookies(cookie1, cookie2);
+ return assertHasCookies(cookie1, cookie2);
});
test.ignore(env.browsers(Browser.IE)).
- it('only returns cookies visible to the current page', function() {
+ it('only returns cookies visible to the current page', function*() {
var cookie1 = createCookieSpec();
- driver.manage().addCookie(cookie1);
+ yield driver.manage().addCookie(cookie1);
var pageUrl = fileserver.whereIs('page/1');
var cookie2 = createCookieSpec({
path: url.parse(pageUrl).pathname
});
- driver.get(pageUrl);
- driver.manage().addCookie(cookie2);
- assertHasCookies(cookie1, cookie2);
+ yield driver.get(pageUrl);
+ yield driver.manage().addCookie(cookie2);
+ yield assertHasCookies(cookie1, cookie2);
- driver.get(fileserver.Pages.ajaxyPage);
- assertHasCookies(cookie1);
+ yield driver.get(fileserver.Pages.ajaxyPage);
+ yield assertHasCookies(cookie1);
- driver.get(pageUrl);
- assertHasCookies(cookie1, cookie2);
+ yield driver.get(pageUrl);
+ yield assertHasCookies(cookie1, cookie2);
});
- test.it('can delete all cookies', function() {
+ test.it('can delete all cookies', function*() {
var cookie1 = createCookieSpec();
var cookie2 = createCookieSpec();
- driver.executeScript(
+ yield driver.executeScript(
'document.cookie = arguments[0] + "=" + arguments[1];' +
'document.cookie = arguments[2] + "=" + arguments[3];',
cookie1.name, cookie1.value, cookie2.name, cookie2.value);
- assertHasCookies(cookie1, cookie2);
+ yield assertHasCookies(cookie1, cookie2);
- driver.manage().deleteAllCookies();
- assertHasCookies();
+ yield driver.manage().deleteAllCookies();
+ yield assertHasCookies();
});
- test.it('can delete cookies by name', function() {
+ test.it('can delete cookies by name', function*() {
var cookie1 = createCookieSpec();
var cookie2 = createCookieSpec();
- driver.executeScript(
+ yield driver.executeScript(
'document.cookie = arguments[0] + "=" + arguments[1];' +
'document.cookie = arguments[2] + "=" + arguments[3];',
cookie1.name, cookie1.value, cookie2.name, cookie2.value);
- assertHasCookies(cookie1, cookie2);
+ yield assertHasCookies(cookie1, cookie2);
- driver.manage().deleteCookie(cookie1.name);
- assertHasCookies(cookie2);
+ yield driver.manage().deleteCookie(cookie1.name);
+ yield assertHasCookies(cookie2);
});
- test.it('should only delete cookie with exact name', function() {
+ test.it('should only delete cookie with exact name', function*() {
var cookie1 = createCookieSpec();
var cookie2 = createCookieSpec();
var cookie3 = {name: cookie1.name + 'xx', value: cookie1.value};
- driver.executeScript(
+ yield driver.executeScript(
'document.cookie = arguments[0] + "=" + arguments[1];' +
'document.cookie = arguments[2] + "=" + arguments[3];' +
'document.cookie = arguments[4] + "=" + arguments[5];',
cookie1.name, cookie1.value, cookie2.name, cookie2.value,
cookie3.name, cookie3.value);
- assertHasCookies(cookie1, cookie2, cookie3);
+ yield assertHasCookies(cookie1, cookie2, cookie3);
- driver.manage().deleteCookie(cookie1.name);
- assertHasCookies(cookie2, cookie3);
+ yield driver.manage().deleteCookie(cookie1.name);
+ yield assertHasCookies(cookie2, cookie3);
});
- test.it('can delete cookies set higher in the path', function() {
+ test.it('can delete cookies set higher in the path', function*() {
var cookie = createCookieSpec();
var childUrl = fileserver.whereIs('child/childPage.html');
var grandchildUrl = fileserver.whereIs(
'child/grandchild/grandchildPage.html');
- driver.get(childUrl);
- driver.manage().addCookie(cookie);
- assertHasCookies(cookie);
+ yield driver.get(childUrl);
+ yield driver.manage().addCookie(cookie);
+ yield assertHasCookies(cookie);
- driver.get(grandchildUrl);
- assertHasCookies(cookie);
+ yield driver.get(grandchildUrl);
+ yield assertHasCookies(cookie);
- driver.manage().deleteCookie(cookie.name);
- assertHasCookies();
+ yield driver.manage().deleteCookie(cookie.name);
+ yield assertHasCookies();
- driver.get(childUrl);
- assertHasCookies();
+ yield driver.get(childUrl);
+ yield assertHasCookies();
});
test.ignore(env.browsers(
@@ -156,20 +157,20 @@ test.suite(function(env) {
Browser.FIREFOX,
'legacy-' + Browser.FIREFOX,
Browser.IE)).
- it('should retain cookie expiry', function() {
+ it('should retain cookie expiry', function*() {
let expirationDelay = 5 * 1000;
let expiry = new Date(Date.now() + expirationDelay);
let cookie = createCookieSpec({expiry});
- driver.manage().addCookie(cookie);
- driver.manage().getCookie(cookie.name).then(function(actual) {
+ yield driver.manage().addCookie(cookie);
+ yield driver.manage().getCookie(cookie.name).then(function(actual) {
assert.equal(actual.value, cookie.value);
// expiry times are exchanged in seconds since January 1, 1970 UTC.
assert.equal(actual.expiry, Math.floor(expiry.getTime() / 1000));
});
- driver.sleep(expirationDelay);
- assertHasCookies();
+ yield driver.sleep(expirationDelay);
+ yield assertHasCookies();
});
});
@@ -192,9 +193,8 @@ test.suite(function(env) {
return map;
}
- function assertHasCookies(var_args) {
- var expected = Array.prototype.slice.call(arguments, 0);
- driver.manage().getCookies().then(function(cookies) {
+ function assertHasCookies(...expected) {
+ return driver.manage().getCookies().then(function(cookies) {
assert.equal(cookies.length, expected.length,
'Wrong # of cookies.' +
'\n Expected: ' + JSON.stringify(expected) +
diff --git a/node_modules/selenium-webdriver/test/element_finding_test.js b/node_modules/selenium-webdriver/test/element_finding_test.js
index 819e15655..9f4568b8b 100644
--- a/node_modules/selenium-webdriver/test/element_finding_test.js
+++ b/node_modules/selenium-webdriver/test/element_finding_test.js
@@ -34,34 +34,34 @@ test.suite(function(env) {
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
- test.after(function() {
- driver.quit();
+ after(function() {
+ return driver.quit();
});
describe('finding elements', function() {
test.it(
'should work after loading multiple pages in a row',
- function() {
- driver.get(Pages.formPage);
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.linkText('click me')).click();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ function*() {
+ yield driver.get(Pages.formPage);
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.findElement(By.linkText('click me')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
});
describe('By.id()', function() {
- test.it('should work', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.id('linkId')).click();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ test.it('should work', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.findElement(By.id('linkId')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
});
- test.it('should fail if ID not present on page', function() {
- driver.get(Pages.formPage);
- driver.findElement(By.id('nonExistantButton')).
+ test.it('should fail if ID not present on page', function*() {
+ yield driver.get(Pages.formPage);
+ return driver.findElement(By.id('nonExistantButton')).
then(fail, function(e) {
assert(e).instanceOf(error.NoSuchElementError);
});
@@ -70,182 +70,178 @@ test.suite(function(env) {
test.it(
'should find multiple elements by ID even though that is ' +
'malformed HTML',
- function() {
- driver.get(Pages.nestedPage);
- driver.findElements(By.id('2')).then(function(elements) {
- assert(elements.length).equalTo(8);
- });
+ function*() {
+ yield driver.get(Pages.nestedPage);
+
+ let elements = yield driver.findElements(By.id('2'));
+ assert(elements.length).equalTo(8);
});
});
describe('By.linkText()', function() {
- test.it('should be able to click on link identified by text', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.linkText('click me')).click();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ test.it('should be able to click on link identified by text', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.findElement(By.linkText('click me')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
});
test.it(
'should be able to find elements by partial link text',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.partialLinkText('ick me')).click();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.findElement(By.partialLinkText('ick me')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
});
- test.it('should work when link text contains equals sign', function() {
- driver.get(Pages.xhtmlTestPage);
- var id = driver.findElement(By.linkText('Link=equalssign')).
- getAttribute('id');
+ test.it('should work when link text contains equals sign', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let el = yield driver.findElement(By.linkText('Link=equalssign'));
+
+ let id = yield el.getAttribute('id');
assert(id).equalTo('linkWithEqualsSign');
});
test.it('matches by partial text when containing equals sign',
- function() {
- driver.get(Pages.xhtmlTestPage);
- var id = driver.findElement(By.partialLinkText('Link=')).
- getAttribute('id');
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let link = yield driver.findElement(By.partialLinkText('Link='));
+
+ let id = yield link.getAttribute('id');
assert(id).equalTo('linkWithEqualsSign');
});
test.it('works when searching for multiple and text contains =',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.linkText('Link=equalssign')).
- then(function(elements) {
- assert(elements.length).equalTo(1);
- return elements[0].getAttribute('id');
- }).
- then(function(id) {
- assert(id).equalTo('linkWithEqualsSign');
- });
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements =
+ yield driver.findElements(By.linkText('Link=equalssign'));
+
+ assert(elements.length).equalTo(1);
+
+ let id = yield elements[0].getAttribute('id');
+ assert(id).equalTo('linkWithEqualsSign');
});
test.it(
'works when searching for multiple with partial text containing =',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.partialLinkText('Link=')).
- then(function(elements) {
- assert(elements.length).equalTo(1);
- return elements[0].getAttribute('id');
- }).
- then(function(id) {
- assert(id).equalTo('linkWithEqualsSign');
- });
- });
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements =
+ yield driver.findElements(By.partialLinkText('Link='));
+
+ assert(elements.length).equalTo(1);
+
+ let id = yield elements[0].getAttribute('id');
+ assert(id).equalTo('linkWithEqualsSign');
+ });
test.it('should be able to find multiple exact matches',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.linkText('click me')).
- then(function(elements) {
- assert(elements.length).equalTo(2);
- });
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements = yield driver.findElements(By.linkText('click me'));
+ assert(elements.length).equalTo(2);
});
test.it('should be able to find multiple partial matches',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.partialLinkText('ick me')).
- then(function(elements) {
- assert(elements.length).equalTo(2);
- });
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements =
+ yield driver.findElements(By.partialLinkText('ick me'));
+ assert(elements.length).equalTo(2);
});
- // See https://github.com/mozilla/geckodriver/issues/137
- test.ignore(browsers(Browser.FIREFOX)).
- it('works on XHTML pages', function() {
- driver.get(test.whereIs('actualXhtmlPage.xhtml'));
+ test.ignore(browsers(Browser.SAFARI)).
+ it('works on XHTML pages', function*() {
+ yield driver.get(test.whereIs('actualXhtmlPage.xhtml'));
- var el = driver.findElement(By.linkText('Foo'));
- assert(el.getText()).equalTo('Foo');
+ let el = yield driver.findElement(By.linkText('Foo'));
+ return assert(el.getText()).equalTo('Foo');
});
});
describe('By.name()', function() {
- test.it('should work', function() {
- driver.get(Pages.formPage);
+ test.it('should work', function*() {
+ yield driver.get(Pages.formPage);
- var el = driver.findElement(By.name('checky'));
- assert(el.getAttribute('value')).equalTo('furrfu');
+ let el = yield driver.findElement(By.name('checky'));
+ yield assert(el.getAttribute('value')).equalTo('furrfu');
});
- test.it('should find multiple elements with same name', function() {
- driver.get(Pages.nestedPage);
- driver.findElements(By.name('checky')).then(function(elements) {
- assert(elements.length).greaterThan(1);
- });
+ test.it('should find multiple elements with same name', function*() {
+ yield driver.get(Pages.nestedPage);
+
+ let elements = yield driver.findElements(By.name('checky'));
+ assert(elements.length).greaterThan(1);
});
test.it(
'should be able to find elements that do not support name property',
- function() {
- driver.get(Pages.nestedPage);
- driver.findElement(By.name('div1'));
+ function*() {
+ yield driver.get(Pages.nestedPage);
+ yield driver.findElement(By.name('div1'));
// Pass if this does not return an error.
});
- test.it('shoudl be able to find hidden elements by name', function() {
- driver.get(Pages.formPage);
- driver.findElement(By.name('hidden'));
+ test.it('shoudl be able to find hidden elements by name', function*() {
+ yield driver.get(Pages.formPage);
+ yield driver.findElement(By.name('hidden'));
// Pass if this does not return an error.
});
});
describe('By.className()', function() {
- test.it('should work', function() {
- driver.get(Pages.xhtmlTestPage);
+ test.it('should work', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.className('extraDiv'));
- assert(el.getText()).startsWith('Another div starts here.');
+ let el = yield driver.findElement(By.className('extraDiv'));
+ yield assert(el.getText()).startsWith('Another div starts here.');
});
- test.it('should work when name is first name among many', function() {
- driver.get(Pages.xhtmlTestPage);
+ test.it('should work when name is first name among many', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.className('nameA'));
- assert(el.getText()).equalTo('An H2 title');
+ let el = yield driver.findElement(By.className('nameA'));
+ yield assert(el.getText()).equalTo('An H2 title');
});
- test.it('should work when name is last name among many', function() {
- driver.get(Pages.xhtmlTestPage);
+ test.it('should work when name is last name among many', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.className('nameC'));
- assert(el.getText()).equalTo('An H2 title');
+ let el = yield driver.findElement(By.className('nameC'));
+ yield assert(el.getText()).equalTo('An H2 title');
});
- test.it('should work when name is middle of many', function() {
- driver.get(Pages.xhtmlTestPage);
+ test.it('should work when name is middle of many', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.className('nameBnoise'));
- assert(el.getText()).equalTo('An H2 title');
+ let el = yield driver.findElement(By.className('nameBnoise'));
+ yield assert(el.getText()).equalTo('An H2 title');
});
- test.it('should work when name surrounded by whitespace', function() {
- driver.get(Pages.xhtmlTestPage);
+ test.it('should work when name surrounded by whitespace', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.className('spaceAround'));
- assert(el.getText()).equalTo('Spaced out');
+ let el = yield driver.findElement(By.className('spaceAround'));
+ yield assert(el.getText()).equalTo('Spaced out');
});
- test.it('should fail if queried name only partially matches', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.className('nameB')).
+ test.it('should fail if queried name only partially matches', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ return driver.findElement(By.className('nameB')).
then(fail, function(e) {
assert(e).instanceOf(error.NoSuchElementError);
});
});
- test.it('should implicitly wait', function() {
+ test.it('should implicitly wait', function*() {
var TIMEOUT_IN_MS = 1000;
var EPSILON = TIMEOUT_IN_MS / 2;
- driver.manage().timeouts().implicitlyWait(TIMEOUT_IN_MS);
- driver.get(Pages.formPage);
+ yield driver.manage().timeouts().implicitlyWait(TIMEOUT_IN_MS);
+ yield driver.get(Pages.formPage);
var start = new Date();
- driver.findElement(By.id('nonExistantButton')).
+ return driver.findElement(By.id('nonExistantButton')).
then(fail, function(e) {
var end = new Date();
assert(e).instanceOf(error.NoSuchElementError);
@@ -253,11 +249,11 @@ test.suite(function(env) {
});
});
- test.it('should be able to find multiple matches', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.className('nameC')).then(function(elements) {
- assert(elements.length).greaterThan(1);
- });
+ test.it('should be able to find multiple matches', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+
+ let elements = yield driver.findElements(By.className('nameC'));
+ assert(elements.length).greaterThan(1);
});
test.it('permits compound class names', function() {
@@ -269,133 +265,136 @@ test.suite(function(env) {
});
describe('By.xpath()', function() {
- test.it('should work with multiple matches', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.xpath('//div')).then(function(elements) {
- assert(elements.length).greaterThan(1);
- });
+ test.it('should work with multiple matches', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements = yield driver.findElements(By.xpath('//div'));
+ assert(elements.length).greaterThan(1);
});
- test.it('should work for selectors using contains keyword', function() {
- driver.get(Pages.nestedPage);
- driver.findElement(By.xpath('//a[contains(., "hello world")]'));
+ test.it('should work for selectors using contains keyword', function*() {
+ yield driver.get(Pages.nestedPage);
+ yield driver.findElement(By.xpath('//a[contains(., "hello world")]'));
// Pass if no error.
});
});
describe('By.tagName()', function() {
- test.it('works', function() {
- driver.get(Pages.formPage);
+ test.it('works', function*() {
+ yield driver.get(Pages.formPage);
- var el = driver.findElement(By.tagName('input'));
- assert(el.getTagName()).equalTo('input');
+ let el = yield driver.findElement(By.tagName('input'));
+ yield assert(el.getTagName()).equalTo('input');
});
- test.it('can find multiple elements', function() {
- driver.get(Pages.formPage);
- driver.findElements(By.tagName('input')).then(function(elements) {
- assert(elements.length).greaterThan(1);
- });
+ test.it('can find multiple elements', function*() {
+ yield driver.get(Pages.formPage);
+
+ let elements = yield driver.findElements(By.tagName('input'));
+ assert(elements.length).greaterThan(1);
});
});
describe('By.css()', function() {
- test.it('works', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.css('div.content'));
+ test.it('works', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.findElement(By.css('div.content'));
// Pass if no error.
});
- test.it('can find multiple elements', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.css('p')).then(function(elements) {
- assert(elements.length).greaterThan(1);
- });
+ test.it('can find multiple elements', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+
+ let elements = yield driver.findElements(By.css('p'));
+ assert(elements.length).greaterThan(1);
// Pass if no error.
});
test.it(
'should find first matching element when searching by ' +
'compound CSS selector',
- function() {
- driver.get(Pages.xhtmlTestPage);
- var el = driver.findElement(By.css('div.extraDiv, div.content'));
- assert(el.getAttribute('class')).equalTo('content');
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+
+ let el =
+ yield driver.findElement(By.css('div.extraDiv, div.content'));
+ yield assert(el.getAttribute('class')).equalTo('content');
});
test.it('should be able to find multiple elements by compound selector',
- function() {
- driver.get(Pages.xhtmlTestPage);
- driver.findElements(By.css('div.extraDiv, div.content')).
- then(function(elements) {
- assertClassIs(elements[0], 'content');
- assertClassIs(elements[1], 'extraDiv');
-
- function assertClassIs(el, expected) {
- assert(el.getAttribute('class')).equalTo(expected);
- }
- });
+ function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ let elements =
+ yield driver.findElements(By.css('div.extraDiv, div.content'));
+
+ return Promise.all([
+ assertClassIs(elements[0], 'content'),
+ assertClassIs(elements[1], 'extraDiv')
+ ]);
+
+ function assertClassIs(el, expected) {
+ return assert(el.getAttribute('class')).equalTo(expected);
+ }
});
// IE only supports short version option[selected].
test.ignore(browsers(Browser.IE)).
- it('should be able to find element by boolean attribute', function() {
- driver.get(test.whereIs(
+ it('should be able to find element by boolean attribute', function*() {
+ yield driver.get(test.whereIs(
'locators_tests/boolean_attribute_selected.html'));
- var el = driver.findElement(By.css('option[selected="selected"]'));
- assert(el.getAttribute('value')).equalTo('two');
+ let el = yield driver.findElement(By.css('option[selected="selected"]'));
+ yield assert(el.getAttribute('value')).equalTo('two');
});
test.it(
'should be able to find element with short ' +
'boolean attribute selector',
- function() {
- driver.get(test.whereIs(
+ function*() {
+ yield driver.get(test.whereIs(
'locators_tests/boolean_attribute_selected.html'));
- var el = driver.findElement(By.css('option[selected]'));
- assert(el.getAttribute('value')).equalTo('two');
+ let el = yield driver.findElement(By.css('option[selected]'));
+ yield assert(el.getAttribute('value')).equalTo('two');
});
test.it(
'should be able to find element with short boolean attribute ' +
'selector on HTML4 page',
- function() {
- driver.get(test.whereIs(
+ function*() {
+ yield driver.get(test.whereIs(
'locators_tests/boolean_attribute_selected_html4.html'));
- var el = driver.findElement(By.css('option[selected]'));
- assert(el.getAttribute('value')).equalTo('two');
+ let el = yield driver.findElement(By.css('option[selected]'));
+ yield assert(el.getAttribute('value')).equalTo('two');
});
});
describe('by custom locator', function() {
- test.it('handles single element result', function() {
- driver.get(Pages.javascriptPage);
+ test.it('handles single element result', function*() {
+ yield driver.get(Pages.javascriptPage);
- let link = driver.findElement(function(driver) {
+ let link = yield driver.findElement(function(driver) {
let links = driver.findElements(By.tagName('a'));
return promise.filter(links, function(link) {
return link.getAttribute('id').then(id => id === 'updatediv');
}).then(links => links[0]);
});
- assert(link.getText()).isEqualTo('Update a div');
+ yield assert(link.getText()).matches(/Update\s+a\s+div/);
});
- test.it('uses first element if locator resolves to list', function() {
- driver.get(Pages.javascriptPage);
+ test.it('uses first element if locator resolves to list', function*() {
+ yield driver.get(Pages.javascriptPage);
- let link = driver.findElement(function() {
+ let link = yield driver.findElement(function() {
return driver.findElements(By.tagName('a'));
});
- assert(link.getText()).isEqualTo('Change the page title!');
+ yield assert(link.getText()).isEqualTo('Change the page title!');
});
- test.it('fails if locator returns non-webelement value', function() {
- driver.get(Pages.javascriptPage);
+ test.it('fails if locator returns non-webelement value', function*() {
+ yield driver.get(Pages.javascriptPage);
let link = driver.findElement(function() {
return driver.getTitle();
@@ -406,5 +405,22 @@ test.suite(function(env) {
(e) => assert(e).instanceOf(TypeError));
});
});
+
+ describe('switchTo().activeElement()', function() {
+ // SAFARI's new session response does not identify it as a W3C browser,
+ // so the command is sent in the unsupported wire protocol format.
+ test.ignore(browsers(Browser.SAFARI)).
+ it('returns document.activeElement', function*() {
+ yield driver.get(Pages.formPage);
+
+ let email = yield driver.findElement(By.css('#email'));
+ yield driver.executeScript('arguments[0].focus()', email);
+
+ let ae = yield driver.switchTo().activeElement();
+ let equal = yield driver.executeScript(
+ 'return arguments[0] === arguments[1]', email, ae);
+ assert(equal).isTrue();
+ });
+ });
});
});
diff --git a/node_modules/selenium-webdriver/test/execute_script_test.js b/node_modules/selenium-webdriver/test/execute_script_test.js
index 2ee150ed8..97eaa1ed1 100644
--- a/node_modules/selenium-webdriver/test/execute_script_test.js
+++ b/node_modules/selenium-webdriver/test/execute_script_test.js
@@ -29,23 +29,23 @@ var webdriver = require('..'),
test.suite(function(env) {
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
test.after(function() {
- driver.quit();
+ return driver.quit();
});
test.beforeEach(function() {
- driver.get(test.Pages.echoPage);
+ return driver.get(test.Pages.echoPage);
});
describe('executeScript;', function() {
var shouldHaveFailed = new Error('Should have failed');
test.it('fails if script throws', function() {
- execute('throw new Error("boom")')
+ return execute('throw new Error("boom")')
.then(function() { throw shouldHaveFailed; })
.catch(function(e) {
// The java WebDriver server adds a bunch of crap to error messages.
@@ -55,7 +55,7 @@ test.suite(function(env) {
});
test.it('fails if script does not parse', function() {
- execute('throw function\\*')
+ return execute('throw function\\*')
.then(function() { throw shouldHaveFailed; })
.catch(function(e) {
assert(e).notEqualTo(shouldHaveFailed);
@@ -63,64 +63,64 @@ test.suite(function(env) {
});
describe('scripts;', function() {
- test.it('do not pollute the global scope', function() {
- execute('var x = 1;');
- assert(execute('return typeof x;')).equalTo('undefined');
+ test.it('do not pollute the global scope', function*() {
+ yield execute('var x = 1;');
+ yield assert(execute('return typeof x;')).equalTo('undefined');
});
- test.it('can set global variables', function() {
- execute('window.x = 1234;');
- assert(execute('return x;')).equalTo(1234);
+ test.it('can set global variables', function*() {
+ yield execute('window.x = 1234;');
+ yield assert(execute('return x;')).equalTo(1234);
});
- test.it('may be defined as a function expression', function() {
- assert(execute(function() {
+ test.it('may be defined as a function expression', function*() {
+ let result = yield execute(function() {
return 1234 + 'abc';
- })).equalTo('1234abc');
+ });
+ assert(result).equalTo('1234abc');
});
});
describe('return values;', function() {
test.it('returns undefined as null', function() {
- assert(execute('var x; return x;')).isNull();
+ return assert(execute('var x; return x;')).isNull();
});
test.it('can return null', function() {
- assert(execute('return null;')).isNull();
+ return assert(execute('return null;')).isNull();
});
- test.it('can return numbers', function() {
- assert(execute('return 1234')).equalTo(1234);
- assert(execute('return 3.1456')).equalTo(3.1456);
+ test.it('can return numbers', function*() {
+ yield assert(execute('return 1234')).equalTo(1234);
+ yield assert(execute('return 3.1456')).equalTo(3.1456);
});
test.it('can return strings', function() {
- assert(execute('return "hello"')).equalTo('hello');
+ return assert(execute('return "hello"')).equalTo('hello');
});
- test.it('can return booleans', function() {
- assert(execute('return true')).equalTo(true);
- assert(execute('return false')).equalTo(false);
+ test.it('can return booleans', function*() {
+ yield assert(execute('return true')).equalTo(true);
+ yield assert(execute('return false')).equalTo(false);
});
test.it('can return an array of primitives', function() {
- execute('var x; return [1, false, null, 3.14, x]')
+ return execute('var x; return [1, false, null, 3.14, x]')
.then(verifyJson([1, false, null, 3.14, null]));
});
test.it('can return nested arrays', function() {
- execute('return [[1, 2, [3]]]')
- .then(verifyJson([[1, 2, [3]]]));
+ return execute('return [[1, 2, [3]]]').then(verifyJson([[1, 2, [3]]]));
});
- test.ignore(env.browsers(Browser.IE, Browser.SAFARI)).
+ test.ignore(env.browsers(Browser.IE)).
it('can return empty object literal', function() {
- execute('return {}').then(verifyJson({}));
+ return execute('return {}').then(verifyJson({}));
});
test.it('can return object literals', function() {
- execute('return {a: 1, b: false, c: null}').then(function(result) {
+ return execute('return {a: 1, b: false, c: null}').then(result => {
verifyJson(['a', 'b', 'c'])(Object.keys(result).sort());
assert(result.a).equalTo(1);
assert(result.b).equalTo(false);
@@ -129,118 +129,116 @@ test.suite(function(env) {
});
test.it('can return complex object literals', function() {
- execute('return {a:{b: "hello"}}').then(verifyJson({a:{b: 'hello'}}));
+ return execute('return {a:{b: "hello"}}')
+ .then(verifyJson({a:{b: 'hello'}}));
});
- test.it('can return dom elements as web elements', function() {
- execute('return document.querySelector(".header.host")')
- .then(function(result) {
- assert(result).instanceOf(webdriver.WebElement);
- assert(result.getText()).startsWith('host: ');
- });
+ test.it('can return dom elements as web elements', function*() {
+ let result =
+ yield execute('return document.querySelector(".header.host")');
+ assert(result).instanceOf(webdriver.WebElement);
+
+ return assert(result.getText()).startsWith('host: ');
});
- test.it('can return array of dom elements', function() {
- execute('var nodes = document.querySelectorAll(".request,.host");' +
- 'return [nodes[0], nodes[1]];')
- .then(function(result) {
- assert(result.length).equalTo(2);
+ test.it('can return array of dom elements', function*() {
+ let result = yield execute(
+ 'var nodes = document.querySelectorAll(".request,.host");' +
+ 'return [nodes[0], nodes[1]];');
+ assert(result.length).equalTo(2);
- assert(result[0]).instanceOf(webdriver.WebElement);
- assert(result[0].getText()).startsWith('GET ');
+ assert(result[0]).instanceOf(webdriver.WebElement);
+ yield assert(result[0].getText()).startsWith('GET ');
- assert(result[1]).instanceOf(webdriver.WebElement);
- assert(result[1].getText()).startsWith('host: ');
- });
+ assert(result[1]).instanceOf(webdriver.WebElement);
+ yield assert(result[1].getText()).startsWith('host: ');
});
- test.it('can return a NodeList as an array of web elements', function() {
- execute('return document.querySelectorAll(".request,.host");')
- .then(function(result) {
- assert(result.length).equalTo(2);
+ test.it('can return a NodeList as an array of web elements', function*() {
+ let result =
+ yield execute('return document.querySelectorAll(".request,.host");')
- assert(result[0]).instanceOf(webdriver.WebElement);
- assert(result[0].getText()).startsWith('GET ');
+ assert(result.length).equalTo(2);
- assert(result[1]).instanceOf(webdriver.WebElement);
- assert(result[1].getText()).startsWith('host: ');
- });
+ assert(result[0]).instanceOf(webdriver.WebElement);
+ yield assert(result[0].getText()).startsWith('GET ');
+
+ assert(result[1]).instanceOf(webdriver.WebElement);
+ yield assert(result[1].getText()).startsWith('host: ');
});
- test.it('can return object literal with element property', function() {
- execute('return {a: document.body}').then(function(result) {
- assert(result.a).instanceOf(webdriver.WebElement);
- assert(result.a.getTagName()).equalTo('body');
- });
+ test.it('can return object literal with element property', function*() {
+ let result = yield execute('return {a: document.body}');
+
+ assert(result.a).instanceOf(webdriver.WebElement);
+ yield assert(result.a.getTagName()).equalTo('body');
});
});
describe('parameters;', function() {
- test.it('can pass numeric arguments', function() {
- assert(execute('return arguments[0]', 12)).equalTo(12);
- assert(execute('return arguments[0]', 3.14)).equalTo(3.14);
+ test.it('can pass numeric arguments', function*() {
+ yield assert(execute('return arguments[0]', 12)).equalTo(12);
+ yield assert(execute('return arguments[0]', 3.14)).equalTo(3.14);
});
- test.it('can pass boolean arguments', function() {
- assert(execute('return arguments[0]', true)).equalTo(true);
- assert(execute('return arguments[0]', false)).equalTo(false);
+ test.it('can pass boolean arguments', function*() {
+ yield assert(execute('return arguments[0]', true)).equalTo(true);
+ yield assert(execute('return arguments[0]', false)).equalTo(false);
});
- test.it('can pass string arguments', function() {
- assert(execute('return arguments[0]', 'hi')).equalTo('hi');
+ test.it('can pass string arguments', function*() {
+ yield assert(execute('return arguments[0]', 'hi')).equalTo('hi');
});
- test.it('can pass null arguments', function() {
- assert(execute('return arguments[0] === null', null)).equalTo(true);
- assert(execute('return arguments[0]', null)).equalTo(null);
+ test.it('can pass null arguments', function*() {
+ yield assert(execute('return arguments[0] === null', null)).equalTo(true);
+ yield assert(execute('return arguments[0]', null)).equalTo(null);
});
- test.it('passes undefined as a null argument', function() {
+ test.it('passes undefined as a null argument', function*() {
var x;
- assert(execute('return arguments[0] === null', x)).equalTo(true);
- assert(execute('return arguments[0]', x)).equalTo(null);
+ yield assert(execute('return arguments[0] === null', x)).equalTo(true);
+ yield assert(execute('return arguments[0]', x)).equalTo(null);
});
- test.it('can pass multiple arguments', function() {
- assert(execute('return arguments.length')).equalTo(0);
- assert(execute('return arguments.length', 1, 'a', false)).equalTo(3);
+ test.it('can pass multiple arguments', function*() {
+ yield assert(execute('return arguments.length')).equalTo(0);
+ yield assert(execute('return arguments.length', 1, 'a', false)).equalTo(3);
});
- test.ignore(env.browsers(Browser.FIREFOX)).
- it('can return arguments object as array', function() {
- execute('return arguments', 1, 'a', false).then(function(val) {
- assert(val.length).equalTo(3);
- assert(val[0]).equalTo(1);
- assert(val[1]).equalTo('a');
- assert(val[2]).equalTo(false);
- });
+ test.ignore(env.browsers(Browser.FIREFOX, Browser.SAFARI)).
+ it('can return arguments object as array', function*() {
+ let val = yield execute('return arguments', 1, 'a', false);
+
+ assert(val.length).equalTo(3);
+ assert(val[0]).equalTo(1);
+ assert(val[1]).equalTo('a');
+ assert(val[2]).equalTo(false);
});
- test.it('can pass object literal', function() {
- execute(
+ test.it('can pass object literal', function*() {
+ let result = yield execute(
'return [typeof arguments[0], arguments[0].a]', {a: 'hello'})
- .then(function(result) {
- assert(result[0]).equalTo('object');
- assert(result[1]).equalTo('hello');
- });
+ assert(result[0]).equalTo('object');
+ assert(result[1]).equalTo('hello');
});
- test.it('WebElement arguments are passed as DOM elements', function() {
- var el = driver.findElement(By.tagName('div'));
- assert(execute('return arguments[0].tagName.toLowerCase();', el))
- .equalTo('div');
+ test.it('WebElement arguments are passed as DOM elements', function*() {
+ let el = yield driver.findElement(By.tagName('div'));
+ let result =
+ yield execute('return arguments[0].tagName.toLowerCase();', el);
+ assert(result).equalTo('div');
});
- test.it('can pass array containing object literals', function() {
- execute('return arguments[0]', [{color: "red"}]).then(function(result) {
- assert(result.length).equalTo(1);
- assert(result[0].color).equalTo('red');
- });
+ test.it('can pass array containing object literals', function*() {
+ let result = yield execute('return arguments[0]', [{color: "red"}]);
+ assert(result.length).equalTo(1);
+ assert(result[0].color).equalTo('red');
});
test.it('does not modify object literal parameters', function() {
var input = {color: 'red'};
- execute('return arguments[0];', input).then(verifyJson(input));
+ return execute('return arguments[0];', input).then(verifyJson(input));
});
});
@@ -248,7 +246,7 @@ test.suite(function(env) {
describe('issue 8223;', function() {
describe('using for..in loops;', function() {
test.it('can return array built from for-loop index', function() {
- execute(function() {
+ return execute(function() {
var ret = [];
for (var i = 0; i < 3; i++) {
ret.push(i);
@@ -258,7 +256,7 @@ test.suite(function(env) {
});
test.it('can copy input array contents', function() {
- execute(function(input) {
+ return execute(function(input) {
var ret = [];
for (var i in input) {
ret.push(input[i]);
@@ -268,7 +266,7 @@ test.suite(function(env) {
});
test.it('can iterate over input object keys', function() {
- execute(function(thing) {
+ return execute(function(thing) {
var ret = [];
for (var w in thing.words) {
ret.push(thing.words[w].word);
@@ -281,7 +279,7 @@ test.suite(function(env) {
describe('recursive functions;', function() {
test.it('can build array from input', function() {
var input = ['fa', 'fe', 'fi'];
- execute(function(thearray) {
+ return execute(function(thearray) {
var ret = [];
function build_response(thearray, ret) {
ret.push(thearray.shift());
@@ -294,7 +292,7 @@ test.suite(function(env) {
test.it('can build array from elements in object', function() {
var input = {words: [{word: 'fa'}, {word: 'fe'}, {word: 'fi'}]};
- execute(function(thing) {
+ return execute(function(thing) {
var ret = [];
function build_response(thing, ret) {
var item = thing.words.shift();
@@ -342,7 +340,7 @@ test.suite(function(env) {
function verifyJson(expected) {
return function(actual) {
- assert(JSON.stringify(actual)).equalTo(JSON.stringify(expected));
+ return assert(JSON.stringify(actual)).equalTo(JSON.stringify(expected));
};
}
diff --git a/node_modules/selenium-webdriver/test/fingerprint_test.js b/node_modules/selenium-webdriver/test/fingerprint_test.js
index ca36ce142..0a13dd748 100644
--- a/node_modules/selenium-webdriver/test/fingerprint_test.js
+++ b/node_modules/selenium-webdriver/test/fingerprint_test.js
@@ -35,21 +35,26 @@ test.suite(function(env) {
});
describe('fingerprinting', function() {
- test.it('it should fingerprint the navigator object', function() {
- driver.get(Pages.simpleTestPage);
- assert(driver.executeScript('return navigator.webdriver')).equalTo(true);
+ test.it('it should fingerprint the navigator object', function*() {
+ yield driver.get(Pages.simpleTestPage);
+
+ let wd = yield driver.executeScript('return navigator.webdriver');
+ assert(wd).equalTo(true);
});
- test.it('fingerprint must not be writable', function() {
- driver.get(Pages.simpleTestPage);
- assert(driver.executeScript(
- 'navigator.webdriver = "ohai"; return navigator.webdriver'))
- .equalTo(true);
+ test.it('fingerprint must not be writable', function*() {
+ yield driver.get(Pages.simpleTestPage);
+
+ let wd = yield driver.executeScript(
+ 'navigator.webdriver = "ohai"; return navigator.webdriver');
+ assert(wd).equalTo(true);
});
- test.it('leaves fingerprint on svg pages', function() {
- driver.get(Pages.svgPage);
- assert(driver.executeScript('return navigator.webdriver')).equalTo(true);
+ test.it('leaves fingerprint on svg pages', function*() {
+ yield driver.get(Pages.svgPage);
+
+ let wd = yield driver.executeScript('return navigator.webdriver');
+ assert(wd).equalTo(true);
});
});
diff --git a/node_modules/selenium-webdriver/test/firefox/firefox_test.js b/node_modules/selenium-webdriver/test/firefox/firefox_test.js
index 6f3f4b3d9..485964f91 100644
--- a/node_modules/selenium-webdriver/test/firefox/firefox_test.js
+++ b/node_modules/selenium-webdriver/test/firefox/firefox_test.js
@@ -44,11 +44,30 @@ test.suite(function(env) {
test.afterEach(function() {
if (driver) {
- driver.quit();
+ return driver.quit();
}
});
- test.it('can start Firefox with custom preferences', function() {
+ /**
+ * Runs a test that requires Firefox Developer Edition. The test will be
+ * skipped if dev cannot be found on the current system.
+ */
+ function runWithFirefoxDev(options, testFn) {
+ let binary = new firefox.Binary();
+ binary.useDevEdition();
+ return binary.locate().then(exe => {
+ options.setBinary(exe);
+ driver = env.builder()
+ .setFirefoxOptions(options)
+ .build();
+ return driver.call(testFn);
+ }, err => {
+ console.warn(
+ 'Skipping test: could not find Firefox Dev Edition: ' + err);
+ });
+ }
+
+ test.it('can start Firefox with custom preferences', function*() {
var profile = new firefox.Profile();
profile.setPreference('general.useragent.override', 'foo;bar');
@@ -58,68 +77,72 @@ test.suite(function(env) {
setFirefoxOptions(options).
build();
- driver.get('data:text/html,<html><div>content</div></html>');
+ yield driver.get('data:text/html,<html><div>content</div></html>');
- var userAgent = driver.executeScript(
+ var userAgent = yield driver.executeScript(
'return window.navigator.userAgent');
assert(userAgent).equalTo('foo;bar');
});
test.it('can start Firefox with a jetpack extension', function() {
- var profile = new firefox.Profile();
+ let profile = new firefox.Profile();
profile.addExtension(JETPACK_EXTENSION);
- var options = new firefox.Options().setProfile(profile);
+ let options = new firefox.Options().setProfile(profile);
- driver = env.builder().
- setFirefoxOptions(options).
- build();
+ return runWithFirefoxDev(options, function*() {
+ yield loadJetpackPage(driver,
+ 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
- loadJetpackPage(driver,
- 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
- assert(driver.findElement({id: 'jetpack-sample-banner'}).getText())
- .equalTo('Hello, world!');
+ let text =
+ yield driver.findElement({id: 'jetpack-sample-banner'}).getText();
+ assert(text).equalTo('Hello, world!');
+ });
});
test.it('can start Firefox with a normal extension', function() {
- var profile = new firefox.Profile();
+ let profile = new firefox.Profile();
profile.addExtension(NORMAL_EXTENSION);
- var options = new firefox.Options().setProfile(profile);
+ let options = new firefox.Options().setProfile(profile);
- driver = env.builder().
- setFirefoxOptions(options).
- build();
+ return runWithFirefoxDev(options, function*() {
+ yield driver.get('data:text/html,<html><div>content</div></html>');
- driver.get('data:text/html,<html><div>content</div></html>');
- assert(driver.findElement({id: 'sample-extension-footer'}).getText())
- .equalTo('Goodbye');
+ let footer =
+ yield driver.findElement({id: 'sample-extension-footer'});
+ let text = yield footer.getText();
+ assert(text).equalTo('Goodbye');
+ });
});
test.it('can start Firefox with multiple extensions', function() {
- var profile = new firefox.Profile();
+ let profile = new firefox.Profile();
profile.addExtension(JETPACK_EXTENSION);
profile.addExtension(NORMAL_EXTENSION);
- var options = new firefox.Options().setProfile(profile);
+ let options = new firefox.Options().setProfile(profile);
- driver = env.builder().
- setFirefoxOptions(options).
- build();
+ return runWithFirefoxDev(options, function*() {
+ yield loadJetpackPage(driver,
+ 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
+
+ let banner =
+ yield driver.findElement({id: 'jetpack-sample-banner'}).getText();
+ assert(banner).equalTo('Hello, world!');
- loadJetpackPage(driver,
- 'data:text/html;charset=UTF-8,<html><div>content</div></html>');
- assert(driver.findElement({id: 'jetpack-sample-banner'}).getText())
- .equalTo('Hello, world!');
- assert(driver.findElement({id: 'sample-extension-footer'}).getText())
- .equalTo('Goodbye');
+ let footer =
+ yield driver.findElement({id: 'sample-extension-footer'})
+ .getText();
+ assert(footer).equalTo('Goodbye');
+ });
});
function loadJetpackPage(driver, url) {
// On linux the jetpack extension does not always run the first time
// we load a page. If this happens, just reload the page (a simple
// refresh doesn't appear to work).
- driver.wait(function() {
+ return driver.wait(function() {
driver.get(url);
return driver.findElements({id: 'jetpack-sample-banner'})
.then(found => found.length > 0);
@@ -131,21 +154,21 @@ test.suite(function(env) {
var driver1, driver2;
test.ignore(env.isRemote).
- it('can start multiple sessions with single binary instance', function() {
+ it('can start multiple sessions with single binary instance', function*() {
var options = new firefox.Options().setBinary(new firefox.Binary);
env.builder().setFirefoxOptions(options);
- driver1 = env.builder().build();
- driver2 = env.builder().build();
+ driver1 = yield env.builder().build();
+ driver2 = yield env.builder().build();
// Ok if this doesn't fail.
});
- test.afterEach(function() {
+ test.afterEach(function*() {
if (driver1) {
- driver1.quit();
+ yield driver1.quit();
}
if (driver2) {
- driver2.quit();
+ yield driver2.quit();
}
});
});
@@ -153,32 +176,35 @@ test.suite(function(env) {
describe('context switching', function() {
var driver;
- test.beforeEach(function() {
- driver = env.builder().build();
+ test.beforeEach(function*() {
+ driver = yield env.builder().build();
});
test.afterEach(function() {
if (driver) {
- driver.quit();
+ return driver.quit();
}
});
test.ignore(() => !env.isMarionette).
it('can get context', function() {
- assert(driver.getContext()).equalTo(Context.CONTENT);
+ return assert(driver.getContext()).equalTo(Context.CONTENT);
});
test.ignore(() => !env.isMarionette).
- it('can set context', function() {
- driver.setContext(Context.CHROME);
- assert(driver.getContext()).equalTo(Context.CHROME);
- driver.setContext(Context.CONTENT);
- assert(driver.getContext()).equalTo(Context.CONTENT);
+ it('can set context', function*() {
+ yield driver.setContext(Context.CHROME);
+ let ctxt = yield driver.getContext();
+ assert(ctxt).equalTo(Context.CHROME);
+
+ yield driver.setContext(Context.CONTENT);
+ ctxt = yield driver.getContext();
+ assert(ctxt).equalTo(Context.CONTENT);
});
test.ignore(() => !env.isMarionette).
it('throws on unknown context', function() {
- driver.setContext("foo").then(assert.fail, function(e) {
+ return driver.setContext("foo").then(assert.fail, function(e) {
assert(e).instanceOf(error.InvalidArgumentError);
});
});
diff --git a/node_modules/selenium-webdriver/test/firefox/profile_test.js b/node_modules/selenium-webdriver/test/firefox/profile_test.js
index 807c07b72..de61c26b5 100644
--- a/node_modules/selenium-webdriver/test/firefox/profile_test.js
+++ b/node_modules/selenium-webdriver/test/firefox/profile_test.js
@@ -52,7 +52,6 @@ describe('Profile', function() {
it('allows overriding mutable properties', function() {
var profile = new Profile();
- assert.equal('about:blank', profile.getPreference('browser.newtab.url'));
profile.setPreference('browser.newtab.url', 'http://www.example.com');
assert.equal('http://www.example.com',
diff --git a/node_modules/selenium-webdriver/test/http/util_test.js b/node_modules/selenium-webdriver/test/http/util_test.js
index aa7a9158a..6361c0650 100644
--- a/node_modules/selenium-webdriver/test/http/util_test.js
+++ b/node_modules/selenium-webdriver/test/http/util_test.js
@@ -17,11 +17,12 @@
'use strict';
-var assert = require('assert'),
- http = require('http');
+const assert = require('assert');
+const http = require('http');
-var error = require('../../lib/error');
-var util = require('../../http/util');
+const error = require('../../lib/error');
+const util = require('../../http/util');
+const promise = require('../../lib/promise');
describe('selenium-webdriver/http/util', function() {
@@ -123,19 +124,15 @@ describe('selenium-webdriver/http/util', function() {
function() {});
});
- it('can cancel wait', function(done) {
+ it('can cancel wait', function() {
status = 1;
- var err = Error('cancelled!');
- var isReady = util.waitForServer(baseUrl, 200).
- then(function() { done('Did not expect to succeed'); }).
- then(null, function(e) {
- assert.equal('cancelled!', e.message);
- }).
- then(function() { done(); }, done);
-
- setTimeout(function() {
- isReady.cancel('cancelled!');
- }, 50);
+ let cancel = new Promise(resolve => {
+ setTimeout(_ => resolve(), 50)
+ });
+ return util.waitForServer(baseUrl, 200, cancel)
+ .then(
+ () => { throw Error('Did not expect to succeed!'); },
+ (e) => assert.ok(e instanceof promise.CancellationError));
});
});
@@ -167,18 +164,15 @@ describe('selenium-webdriver/http/util', function() {
});
});
- it('can cancel wait', function(done) {
+ it('can cancel wait', function() {
responseCode = 404;
- var isReady = util.waitForUrl(baseUrl, 200).
- then(function() { done('Did not expect to succeed'); }).
- then(null, function(e) {
- assert.equal('cancelled!', e.message);
- }).
- then(function() { done(); }, done);
-
- setTimeout(function() {
- isReady.cancel('cancelled!');
- }, 50);
+ let cancel = new Promise(resolve => {
+ setTimeout(_ => resolve(), 50);
+ });
+ return util.waitForUrl(baseUrl, 200, cancel)
+ .then(
+ () => { throw Error('Did not expect to succeed!'); },
+ (e) => assert.ok(e instanceof promise.CancellationError));
});
});
});
diff --git a/node_modules/selenium-webdriver/test/io_test.js b/node_modules/selenium-webdriver/test/io_test.js
index 42ec8d3a3..840d7644b 100644
--- a/node_modules/selenium-webdriver/test/io_test.js
+++ b/node_modules/selenium-webdriver/test/io_test.js
@@ -307,7 +307,7 @@ describe('io', function() {
});
it('catches errors from invalid input', function() {
- return io.read(1234)
+ return io.read({})
.then(() => assert.fail('should have failed'),
(e) => assert.ok(e instanceof TypeError));
});
diff --git a/node_modules/selenium-webdriver/test/lib/http_test.js b/node_modules/selenium-webdriver/test/lib/http_test.js
index 343a04800..1c6c073ad 100644
--- a/node_modules/selenium-webdriver/test/lib/http_test.js
+++ b/node_modules/selenium-webdriver/test/lib/http_test.js
@@ -84,12 +84,14 @@ describe('http', function() {
describe('command routing', function() {
it('rejects unrecognized commands', function() {
- assert.throws(
- () => executor.execute(new Command('fake-name')),
- function (err) {
- return err instanceof error.UnknownCommandError
- && 'Unrecognized command: fake-name' === err.message;
- });
+ return executor.execute(new Command('fake-name'))
+ .then(assert.fail, err => {
+ if (err instanceof error.UnknownCommandError
+ && 'Unrecognized command: fake-name' === err.message) {
+ return;
+ }
+ throw err;
+ })
});
it('rejects promise if client fails to send request', function() {
diff --git a/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js
index a89391590..207f490a1 100644
--- a/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js
+++ b/node_modules/selenium-webdriver/test/lib/promise_aplus_test.js
@@ -18,57 +18,61 @@
'use strict';
const promise = require('../../lib/promise');
+const {enablePromiseManager} = require('../../lib/test/promise');
describe('Promises/A+ Compliance Tests', function() {
- // The promise spec does not define behavior for unhandled rejections and
- // assumes they are effectively swallowed. This is not the case with our
- // implementation, so we have to disable error propagation to test that the
- // rest of our behavior is compliant.
- // We run the tests with a separate instance of the control flow to ensure
- // disablign error propagation does not impact other tests.
- var flow = new promise.ControlFlow();
- flow.setPropagateUnhandledRejections(false);
+ enablePromiseManager(() => {
+ // The promise spec does not define behavior for unhandled rejections and
+ // assumes they are effectively swallowed. This is not the case with our
+ // implementation, so we have to disable error propagation to test that the
+ // rest of our behavior is compliant.
+ // We run the tests with a separate instance of the control flow to ensure
+ // disablign error propagation does not impact other tests.
+ var flow = new promise.ControlFlow();
+ flow.setPropagateUnhandledRejections(false);
- // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios.
- var realDescribe = global.describe;
- global.describe = function(name, fn) {
- realDescribe(name, function() {
- var prefix = 'Promises/A+ Compliance Tests 2.2.6: '
- + '`then` may be called multiple times on the same promise.';
- var suffix = 'even when one handler is added inside another handler';
- if (this.fullTitle().startsWith(prefix)
- && this.fullTitle().endsWith(suffix)) {
- var realSpecify = global.specify;
- try {
- global.specify = function(name) {
- realSpecify(name);
- };
+ // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios.
+ var realDescribe = global.describe;
+ global.describe = function(name, fn) {
+ realDescribe(name, function() {
+ var prefix = 'Promises/A+ Compliance Tests '
+ + 'SELENIUM_PROMISE_MANAGER=true 2.2.6: '
+ + '`then` may be called multiple times on the same promise.';
+ var suffix = 'even when one handler is added inside another handler';
+ if (this.fullTitle().startsWith(prefix)
+ && this.fullTitle().endsWith(suffix)) {
+ var realSpecify = global.specify;
+ try {
+ global.specify = function(name) {
+ realSpecify(name);
+ };
+ fn();
+ } finally {
+ global.specify = realSpecify;
+ }
+ } else {
fn();
- } finally {
- global.specify = realSpecify;
}
- } else {
- fn();
+ });
+ };
+
+ require('promises-aplus-tests').mocha({
+ resolved: function(value) {
+ return new promise.Promise((fulfill) => fulfill(value), flow);
+ },
+ rejected: function(error) {
+ return new promise.Promise((_, reject) => reject(error), flow);
+ },
+ deferred: function() {
+ var d = new promise.Deferred(flow);
+ return {
+ resolve: d.fulfill,
+ reject: d.reject,
+ promise: d.promise
+ };
}
});
- };
- require('promises-aplus-tests').mocha({
- resolved: function(value) {
- return new promise.Promise((fulfill) => fulfill(value), flow);
- },
- rejected: function(error) {
- return new promise.Promise((_, reject) => reject(error), flow);
- },
- deferred: function() {
- var d = new promise.Deferred(flow);
- return {
- resolve: d.fulfill,
- reject: d.reject,
- promise: d.promise
- };
- }
+ global.describe = realDescribe;
});
-
- global.describe = realDescribe;
});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_error_test.js b/node_modules/selenium-webdriver/test/lib/promise_error_test.js
index e6de3cb92..b89a2f875 100644
--- a/node_modules/selenium-webdriver/test/lib/promise_error_test.js
+++ b/node_modules/selenium-webdriver/test/lib/promise_error_test.js
@@ -27,6 +27,7 @@ const testutil = require('./testutil');
const assert = require('assert');
const promise = require('../../lib/promise');
+const {enablePromiseManager} = require('../../lib/test/promise');
const NativePromise = Promise;
const StubError = testutil.StubError;
@@ -34,244 +35,178 @@ const throwStubError = testutil.throwStubError;
const assertIsStubError = testutil.assertIsStubError;
describe('promise error handling', function() {
- var flow, uncaughtExceptions;
-
- beforeEach(function setUp() {
- flow = promise.controlFlow();
- uncaughtExceptions = [];
- flow.on('uncaughtException', onUncaughtException);
- });
-
- afterEach(function tearDown() {
- return waitForIdle(flow).then(function() {
- assert.deepEqual(
- [], uncaughtExceptions, 'There were uncaught exceptions');
- flow.reset();
- });
- });
-
- function onUncaughtException(e) {
- uncaughtExceptions.push(e);
- }
-
- function waitForAbort(opt_flow, opt_n) {
- var n = opt_n || 1;
- var theFlow = opt_flow || flow;
- theFlow.removeAllListeners(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
- return new NativePromise(function(fulfill, reject) {
- theFlow.once('idle', function() {
- reject(Error('expected flow to report an unhandled error'));
- });
-
- var errors = [];
- theFlow.on('uncaughtException', onError);
- function onError(e) {
- errors.push(e);
- if (errors.length === n) {
- theFlow.removeListener('uncaughtException', onError);
- fulfill(n === 1 ? errors[0] : errors);
- }
+ enablePromiseManager(() => {
+ var flow, uncaughtExceptions;
+
+ beforeEach(function setUp() {
+ if (promise.USE_PROMISE_MANAGER) {
+ flow = promise.controlFlow();
+ uncaughtExceptions = [];
+ flow.on('uncaughtException', onUncaughtException);
}
});
- }
-
- function waitForIdle(opt_flow) {
- var theFlow = opt_flow || flow;
- return new NativePromise(function(fulfill, reject) {
- if (theFlow.isIdle()) {
- fulfill();
- return;
- }
- theFlow.once('idle', fulfill);
- theFlow.once('uncaughtException', reject);
- });
- }
-
- it('testRejectedPromiseTriggersErrorCallback', function() {
- return promise.rejected(new StubError).
- then(assert.fail, assertIsStubError);
- });
- describe('callback throws trigger subsequent error callback', function() {
- it('fulfilled promise', function() {
- return promise.fulfilled().
- then(throwStubError).
- then(assert.fail, assertIsStubError);
- });
-
- it('rejected promise', function() {
- var e = Error('not the droids you are looking for');
- return promise.rejected(e).
- then(assert.fail, throwStubError).
- then(assert.fail, assertIsStubError);
- });
- });
-
- describe('callback returns rejected promise triggers subsequent errback', function() {
- it('from fulfilled callback', function() {
- return promise.fulfilled().then(function() {
- return promise.rejected(new StubError);
- }).then(assert.fail, assertIsStubError);
- });
-
- it('from rejected callback', function() {
- var e = Error('not the droids you are looking for');
- return promise.rejected(e).
- then(assert.fail, function() {
- return promise.rejected(new StubError);
- }).
- then(assert.fail, assertIsStubError);
+ afterEach(function tearDown() {
+ if (promise.USE_PROMISE_MANAGER) {
+ return waitForIdle(flow).then(function() {
+ assert.deepEqual(
+ [], uncaughtExceptions, 'There were uncaught exceptions');
+ flow.reset();
+ });
+ }
});
- });
-
- it('testReportsUnhandledRejectionsThroughTheControlFlow', function() {
- promise.rejected(new StubError);
- return waitForAbort().then(assertIsStubError);
- });
- describe('multiple unhandled rejections outside a task', function() {
- it('are reported in order they occurred', function() {
- var e1 = Error('error 1');
- var e2 = Error('error 2');
+ function onUncaughtException(e) {
+ uncaughtExceptions.push(e);
+ }
- promise.rejected(e1);
- promise.rejected(e2);
+ function waitForAbort(opt_flow, opt_n) {
+ var n = opt_n || 1;
+ var theFlow = opt_flow || flow;
+ theFlow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once('idle', function() {
+ reject(Error('expected flow to report an unhandled error'));
+ });
- return waitForAbort(flow).then(function(error) {
- assert.ok(
- error instanceof promise.MultipleUnhandledRejectionError);
- // TODO: switch to Array.from when we drop node 0.12.x
var errors = [];
- for (var e of error.errors) {
+ theFlow.on('uncaughtException', onError);
+ function onError(e) {
errors.push(e);
+ if (errors.length === n) {
+ theFlow.removeListener('uncaughtException', onError);
+ fulfill(n === 1 ? errors[0] : errors);
+ }
}
- assert.deepEqual([e1, e2], errors);
});
- });
- });
-
- describe('does not report unhandled rejection when', function() {
- it('handler added before next tick', function() {
- promise.rejected(new StubError).then(assert.fail, assertIsStubError);
- return waitForIdle();
- });
+ }
- it('added async but before next tick', function() {
- var called = false;
+ function waitForIdle(opt_flow) {
+ var theFlow = opt_flow || flow;
return new NativePromise(function(fulfill, reject) {
- var aPromise;
- NativePromise.resolve().then(function() {
- aPromise.then(assert.fail, function(e) {
- called = true;
- assertIsStubError(e);
- });
- waitForIdle().then(fulfill, reject);
- });
- aPromise = promise.rejected(new StubError);
- }).then(function() {
- assert.ok(called);
- })
+ if (theFlow.isIdle()) {
+ fulfill();
+ return;
+ }
+ theFlow.once('idle', fulfill);
+ theFlow.once('uncaughtException', reject);
+ });
+ }
+
+ it('testRejectedPromiseTriggersErrorCallback', function() {
+ return promise.rejected(new StubError).
+ then(assert.fail, assertIsStubError);
});
- });
- it('testTaskThrows', function() {
- return flow.execute(throwStubError).then(assert.fail, assertIsStubError);
- });
+ describe('callback throws trigger subsequent error callback', function() {
+ it('fulfilled promise', function() {
+ return promise.fulfilled().
+ then(throwStubError).
+ then(assert.fail, assertIsStubError);
+ });
- it('testTaskReturnsRejectedPromise', function() {
- return flow.execute(function() {
- return promise.rejected(new StubError)
- }).then(assert.fail, assertIsStubError);
- });
+ it('rejected promise', function() {
+ var e = Error('not the droids you are looking for');
+ return promise.rejected(e).
+ then(assert.fail, throwStubError).
+ then(assert.fail, assertIsStubError);
+ });
+ });
- it('testTaskHasUnhandledRejection', function() {
- return flow.execute(function() {
- promise.rejected(new StubError)
- }).then(assert.fail, assertIsStubError);
- });
+ describe('callback returns rejected promise triggers subsequent errback', function() {
+ it('from fulfilled callback', function() {
+ return promise.fulfilled().then(function() {
+ return promise.rejected(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
- it('testTaskfails_returnedPromiseIsUnhandled', function() {
- flow.execute(throwStubError);
- return waitForAbort().then(assertIsStubError);
- });
+ it('from rejected callback', function() {
+ var e = Error('not the droids you are looking for');
+ return promise.rejected(e).
+ then(assert.fail, function() {
+ return promise.rejected(new StubError);
+ }).
+ then(assert.fail, assertIsStubError);
+ });
+ });
- it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() {
- var seen = [];
- flow.execute(function() {
+ it('testReportsUnhandledRejectionsThroughTheControlFlow', function() {
promise.rejected(new StubError);
-
- flow.execute(() => seen.push('a'))
- .then(() => seen.push('b'), (e) => seen.push(e));
- flow.execute(() => seen.push('c'))
- .then(() => seen.push('b'), (e) => seen.push(e));
+ return waitForAbort().then(assertIsStubError);
});
- return waitForAbort()
- .then(assertIsStubError)
- .then(() => assert.deepEqual([], seen));
- });
+ describe('multiple unhandled rejections outside a task', function() {
+ it('are reported in order they occurred', function() {
+ var e1 = Error('error 1');
+ var e2 = Error('error 2');
- describe('nested task failures', function() {
- it('returns up to paren', function() {
- return flow.execute(function() {
- return flow.execute(function() {
- return flow.execute(throwStubError);
- });
- }).then(assert.fail, assertIsStubError);
- });
+ promise.rejected(e1);
+ promise.rejected(e2);
- it('task throws; uncaught error bubbles up', function() {
- flow.execute(function() {
- flow.execute(function() {
- flow.execute(throwStubError);
+ return waitForAbort(flow).then(function(error) {
+ assert.ok(
+ error instanceof promise.MultipleUnhandledRejectionError);
+ // TODO: switch to Array.from when we drop node 0.12.x
+ var errors = [];
+ for (var e of error.errors) {
+ errors.push(e);
+ }
+ assert.deepEqual([e1, e2], errors);
});
});
- return waitForAbort().then(assertIsStubError);
});
- it('task throws; uncaught error bubbles up; is caught at root', function() {
- flow.execute(function() {
- flow.execute(function() {
- flow.execute(throwStubError);
- });
- }).then(assert.fail, assertIsStubError);
- return waitForIdle();
- });
+ describe('does not report unhandled rejection when', function() {
+ it('handler added before next tick', function() {
+ promise.rejected(new StubError).then(assert.fail, assertIsStubError);
+ return waitForIdle();
+ });
- it('unhandled rejection bubbles up', function() {
- flow.execute(function() {
- flow.execute(function() {
- flow.execute(function() {
- promise.rejected(new StubError);
+ it('added async but before next tick', function() {
+ var called = false;
+ return new NativePromise(function(fulfill, reject) {
+ var aPromise;
+ NativePromise.resolve().then(function() {
+ aPromise.then(assert.fail, function(e) {
+ called = true;
+ assertIsStubError(e);
+ });
+ waitForIdle().then(fulfill, reject);
});
- });
+ aPromise = promise.rejected(new StubError);
+ }).then(function() {
+ assert.ok(called);
+ })
});
- return waitForAbort().then(assertIsStubError);
});
- it('unhandled rejection bubbles up; caught at root', function() {
- flow.execute(function() {
- flow.execute(function() {
- promise.rejected(new StubError);
- });
+ it('testTaskThrows', function() {
+ return flow.execute(throwStubError).then(assert.fail, assertIsStubError);
+ });
+
+ it('testTaskReturnsRejectedPromise', function() {
+ return flow.execute(function() {
+ return promise.rejected(new StubError)
}).then(assert.fail, assertIsStubError);
- return waitForIdle();
});
- it('mixtureof hanging and free subtasks', function() {
- flow.execute(function() {
- return flow.execute(function() {
- flow.execute(throwStubError);
- });
- });
+ it('testTaskHasUnhandledRejection', function() {
+ return flow.execute(function() {
+ promise.rejected(new StubError)
+ }).then(assert.fail, assertIsStubError);
+ });
+
+ it('testTaskfails_returnedPromiseIsUnhandled', function() {
+ flow.execute(throwStubError);
return waitForAbort().then(assertIsStubError);
});
- it('cancels remaining tasks', function() {
+ it('testTaskHasUnhandledRejection_cancelsRemainingSubTasks', function() {
var seen = [];
flow.execute(function() {
- flow.execute(() => promise.rejected(new StubError));
+ promise.rejected(new StubError);
+
flow.execute(() => seen.push('a'))
.then(() => seen.push('b'), (e) => seen.push(e));
flow.execute(() => seen.push('c'))
@@ -282,598 +217,667 @@ describe('promise error handling', function() {
.then(assertIsStubError)
.then(() => assert.deepEqual([], seen));
});
- });
-
- it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() {
- return flow.execute(function() {
- return {
- 'then': function(_, errback) {
- errback('abc123');
- }
- };
- }).then(assert.fail, function(value) {
- assert.equal('abc123', value);
- });
- });
-
- describe('ControlFlow#wait();', function() {
- describe('condition throws;', function() {
- it('failure is caught', function() {
- return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError);
- });
-
- it('failure is not caught', function() {
- flow.wait(throwStubError, 50);
- return waitForAbort().then(assertIsStubError);
- });
- });
- describe('condition returns promise', function() {
- it('failure is caught', function() {
- return flow.wait(function() {
- return promise.rejected(new StubError);
- }, 50).then(assert.fail, assertIsStubError);
+ describe('nested task failures', function() {
+ it('returns up to paren', function() {
+ return flow.execute(function() {
+ return flow.execute(function() {
+ return flow.execute(throwStubError);
+ });
+ }).then(assert.fail, assertIsStubError);
});
- it('failure is not caught', function() {
- flow.wait(function() {
- return promise.rejected(new StubError);
- }, 50);
+ it('task throws; uncaught error bubbles up', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ });
return waitForAbort().then(assertIsStubError);
});
- });
- describe('condition has unhandled promise rejection', function() {
- it('failure is caught', function() {
- return flow.wait(function() {
- promise.rejected(new StubError);
- }, 50).then(assert.fail, assertIsStubError);
+ it('task throws; uncaught error bubbles up; is caught at root', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
});
- it('failure is not caught', function() {
- flow.wait(function() {
- promise.rejected(new StubError);
- }, 50);
+ it('unhandled rejection bubbles up', function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ flow.execute(function() {
+ promise.rejected(new StubError);
+ });
+ });
+ });
return waitForAbort().then(assertIsStubError);
});
- });
- describe('condition has subtask failure', function() {
- it('failure is caught', function() {
- return flow.wait(function() {
+ it('unhandled rejection bubbles up; caught at root', function() {
+ flow.execute(function() {
flow.execute(function() {
- flow.execute(throwStubError);
+ promise.rejected(new StubError);
});
- }, 50).then(assert.fail, assertIsStubError);
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
});
- it('failure is not caught', function() {
- flow.wait(function() {
- flow.execute(function() {
+ it('mixtureof hanging and free subtasks', function() {
+ flow.execute(function() {
+ return flow.execute(function() {
flow.execute(throwStubError);
});
- }, 50);
+ });
return waitForAbort().then(assertIsStubError);
});
- });
- });
- describe('errback throws a new error', function() {
- it('start with normal promise', function() {
- var error = Error('an error');
- return promise.rejected(error).
- catch(function(e) {
- assert.equal(e, error);
- throw new StubError;
- }).
- catch(assertIsStubError);
+ it('cancels remaining tasks', function() {
+ var seen = [];
+ flow.execute(function() {
+ flow.execute(() => promise.rejected(new StubError));
+ flow.execute(() => seen.push('a'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ flow.execute(() => seen.push('c'))
+ .then(() => seen.push('b'), (e) => seen.push(e));
+ });
+
+ return waitForAbort()
+ .then(assertIsStubError)
+ .then(() => assert.deepEqual([], seen));
+ });
});
- it('start with task result', function() {
- var error = Error('an error');
+ it('testTaskReturnsPromiseLikeObjectThatInvokesErrback', function() {
return flow.execute(function() {
- throw error;
- }).
- catch(function(e) {
- assert.equal(e, error);
- throw new StubError;
- }).
- catch(assertIsStubError);
+ return {
+ 'then': function(_, errback) {
+ errback('abc123');
+ }
+ };
+ }).then(assert.fail, function(value) {
+ assert.equal('abc123', value);
+ });
});
- it('start with normal promise; uncaught error', function() {
- var error = Error('an error');
- promise.rejected(error).
- catch(function(e) {
- assert.equal(e, error);
- throw new StubError;
- });
- return waitForAbort().then(assertIsStubError);
- });
+ describe('ControlFlow#wait();', function() {
+ describe('condition throws;', function() {
+ it('failure is caught', function() {
+ return flow.wait(throwStubError, 50).then(assert.fail, assertIsStubError);
+ });
- it('start with task result; uncaught error', function() {
- var error = Error('an error');
- flow.execute(function() {
- throw error;
- }).
- catch(function(e) {
- assert.equal(e, error);
- throw new StubError;
+ it('failure is not caught', function() {
+ flow.wait(throwStubError, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
});
- return waitForAbort().then(assertIsStubError);
- });
- });
- it('thrownPromiseCausesCallbackRejection', function() {
- let p = promise.fulfilled(1234);
- return promise.fulfilled().then(function() {
- throw p;
- }).then(assert.fail, function(value) {
- assert.strictEqual(p, value);
- });
- });
+ describe('condition returns promise', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ return promise.rejected(new StubError);
+ }, 50).then(assert.fail, assertIsStubError);
+ });
- describe('task throws promise', function() {
- it('promise was fulfilled', function() {
- var toThrow = promise.fulfilled(1234);
- flow.execute(function() {
- throw toThrow;
- }).then(assert.fail, function(value) {
- assert.equal(toThrow, value);
- return toThrow;
- }).then(function(value) {
- assert.equal(1234, value);
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ return promise.rejected(new StubError);
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
});
- return waitForIdle();
- });
-
- it('promise was rejected', function() {
- var toThrow = promise.rejected(new StubError);
- toThrow.catch(function() {}); // For tearDown.
- flow.execute(function() {
- throw toThrow;
- }).then(assert.fail, function(e) {
- assert.equal(toThrow, e);
- return e;
- }).then(assert.fail, assertIsStubError);
- return waitForIdle();
- });
- });
- it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() {
- var d = promise.defer();
- flow.execute(function() {
- promise.rejected(new StubError);
- return d.promise;
- }).then(assert.fail, assertIsStubError);
+ describe('condition has unhandled promise rejection', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ promise.rejected(new StubError);
+ }, 50).then(assert.fail, assertIsStubError);
+ });
- return waitForIdle().then(function() {
- return d.promise;
- }).then(assert.fail, function(e) {
- assert.equal('CancellationError: StubError', e.toString());
- });
- });
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ promise.rejected(new StubError);
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
- it('testFailsParentTaskIfAsyncScheduledTaskFails', function() {
- var d = promise.defer();
- flow.execute(function() {
- flow.execute(throwStubError);
- return d.promise;
- }).then(assert.fail, assertIsStubError);
+ describe('condition has subtask failure', function() {
+ it('failure is caught', function() {
+ return flow.wait(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }, 50).then(assert.fail, assertIsStubError);
+ });
- return waitForIdle().then(function() {
- return d.promise;
- }).then(assert.fail, function(e) {
- assert.equal('CancellationError: StubError', e.toString());
+ it('failure is not caught', function() {
+ flow.wait(function() {
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ });
+ }, 50);
+ return waitForAbort().then(assertIsStubError);
+ });
+ });
});
- });
- describe('long stack traces', function() {
- afterEach(() => promise.LONG_STACK_TRACES = false);
+ describe('errback throws a new error', function() {
+ it('start with normal promise', function() {
+ var error = Error('an error');
+ return promise.rejected(error).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ }).
+ catch(assertIsStubError);
+ });
- it('always includes task stacks in failures', function() {
- promise.LONG_STACK_TRACES = false;
- flow.execute(function() {
- flow.execute(function() {
- flow.execute(throwStubError, 'throw error');
- }, 'two');
- }, 'three').
- then(assert.fail, function(e) {
- assertIsStubError(e);
- if (typeof e.stack !== 'string') {
- return;
- }
- var messages = e.stack.split(/\n/).filter(function(line, index) {
- return /^From: /.test(line);
- });
- assert.deepEqual([
- 'From: Task: throw error',
- 'From: Task: two',
- 'From: Task: three'
- ], messages);
+ it('start with task result', function() {
+ var error = Error('an error');
+ return flow.execute(function() {
+ throw error;
+ }).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ }).
+ catch(assertIsStubError);
+ });
+
+ it('start with normal promise; uncaught error', function() {
+ var error = Error('an error');
+ promise.rejected(error).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
+ });
+ return waitForAbort().then(assertIsStubError);
});
- return waitForIdle();
- });
- it('does not include completed tasks', function () {
- flow.execute(function() {}, 'succeeds');
- flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) {
- assertIsStubError(e);
- if (typeof e.stack !== 'string') {
- return;
- }
- var messages = e.stack.split(/\n/).filter(function(line, index) {
- return /^From: /.test(line);
+ it('start with task result; uncaught error', function() {
+ var error = Error('an error');
+ flow.execute(function() {
+ throw error;
+ }).
+ catch(function(e) {
+ assert.equal(e, error);
+ throw new StubError;
});
- assert.deepEqual(['From: Task: kaboom'], messages);
+ return waitForAbort().then(assertIsStubError);
});
- return waitForIdle();
});
- it('does not include promise chain when disabled', function() {
- promise.LONG_STACK_TRACES = false;
- flow.execute(function() {
- flow.execute(function() {
- return promise.fulfilled().
- then(function() {}).
- then(function() {}).
- then(throwStubError);
- }, 'eventually assert.fails');
- }, 'start').
- then(assert.fail, function(e) {
- assertIsStubError(e);
- if (typeof e.stack !== 'string') {
- return;
- }
- var messages = e.stack.split(/\n/).filter(function(line, index) {
- return /^From: /.test(line);
- });
- assert.deepEqual([
- 'From: Task: eventually assert.fails',
- 'From: Task: start'
- ], messages);
+ it('thrownPromiseCausesCallbackRejection', function() {
+ let p = promise.fulfilled(1234);
+ return promise.fulfilled().then(function() {
+ throw p;
+ }).then(assert.fail, function(value) {
+ assert.strictEqual(p, value);
});
- return waitForIdle();
});
- it('includes promise chain when enabled', function() {
- promise.LONG_STACK_TRACES = true;
- flow.execute(function() {
+ describe('task throws promise', function() {
+ it('promise was fulfilled', function() {
+ var toThrow = promise.fulfilled(1234);
flow.execute(function() {
- return promise.fulfilled().
- then(function() {}).
- then(function() {}).
- then(throwStubError);
- }, 'eventually assert.fails');
- }, 'start').
- then(assert.fail, function(e) {
- assertIsStubError(e);
- if (typeof e.stack !== 'string') {
- return;
- }
- var messages = e.stack.split(/\n/).filter(function(line, index) {
- return /^From: /.test(line);
+ throw toThrow;
+ }).then(assert.fail, function(value) {
+ assert.equal(toThrow, value);
+ return toThrow;
+ }).then(function(value) {
+ assert.equal(1234, value);
});
- assert.deepEqual([
- 'From: Promise: then',
- 'From: Task: eventually assert.fails',
- 'From: Task: start'
- ], messages);
+ return waitForIdle();
});
- return waitForIdle();
- });
- });
- describe('frame cancels remaining tasks', function() {
- it('on unhandled task failure', function() {
- var run = false;
- return flow.execute(function() {
- flow.execute(throwStubError);
- flow.execute(function() { run = true; });
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.ok(!run);
+ it('promise was rejected', function() {
+ var toThrow = promise.rejected(new StubError);
+ toThrow.catch(function() {}); // For tearDown.
+ flow.execute(function() {
+ throw toThrow;
+ }).then(assert.fail, function(e) {
+ assert.equal(toThrow, e);
+ return e;
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle();
});
});
- it('on unhandled promise rejection', function() {
- var run = false;
- return flow.execute(function() {
+ it('testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult', function() {
+ var d = promise.defer();
+ flow.execute(function() {
promise.rejected(new StubError);
- flow.execute(function() { run = true; });
+ return d.promise;
+ }).then(assert.fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return d.promise;
}).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.ok(!run);
+ assert.equal('CancellationError: StubError', e.toString());
});
});
- it('if task throws', function() {
- var run = false;
- return flow.execute(function() {
- flow.execute(function() { run = true; });
- throw new StubError;
+ it('testFailsParentTaskIfAsyncScheduledTaskFails', function() {
+ var d = promise.defer();
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ return d.promise;
+ }).then(assert.fail, assertIsStubError);
+
+ return waitForIdle().then(function() {
+ return d.promise;
}).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.ok(!run);
+ assert.equal('CancellationError: StubError', e.toString());
});
});
- describe('task callbacks scheduled in another frame', function() {
- flow = promise.controlFlow();
- function noop() {}
-
- let subTask;
+ describe('long stack traces', function() {
+ afterEach(() => promise.LONG_STACK_TRACES = false);
- before(function() {
+ it('always includes task stacks in failures', function() {
+ promise.LONG_STACK_TRACES = false;
flow.execute(function() {
- // This task will be discarded and never run because of the error below.
- subTask = flow.execute(() => 'abc');
- throw new StubError('stub');
- }).catch(noop);
- });
-
- function assertCancellation(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal(
- 'Task was discarded due to a previous failure: stub', e.message);
- }
-
- it('are rejected with cancellation error', function() {
- let result;
- return Promise.resolve().then(function() {
- return flow.execute(function() {
- result = subTask.then(assert.fail);
+ flow.execute(function() {
+ flow.execute(throwStubError, 'throw error');
+ }, 'two');
+ }, 'three').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
});
- })
- .then(() => result)
- .then(assert.fail, assertCancellation);
+ assert.deepEqual([
+ 'From: Task: throw error',
+ 'From: Task: two',
+ 'From: Task: three'
+ ], messages);
+ });
+ return waitForIdle();
+ });
+
+ it('does not include completed tasks', function () {
+ flow.execute(function() {}, 'succeeds');
+ flow.execute(throwStubError, 'kaboom').then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
+ });
+ assert.deepEqual(['From: Task: kaboom'], messages);
+ });
+ return waitForIdle();
});
- it('cancellation errors propagate through callbacks (1)', function() {
- let result;
- return Promise.resolve().then(function() {
- return flow.execute(function() {
- result = subTask
- .then(assert.fail, assertCancellation)
- .then(() => 'abc123');
+ it('does not include promise chain when disabled', function() {
+ promise.LONG_STACK_TRACES = false;
+ flow.execute(function() {
+ flow.execute(function() {
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(throwStubError);
+ }, 'eventually assert.fails');
+ }, 'start').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
});
- })
- .then(() => result)
- .then(value => assert.equal('abc123', value));
+ assert.deepEqual([
+ 'From: Task: eventually assert.fails',
+ 'From: Task: start'
+ ], messages);
+ });
+ return waitForIdle();
});
- it('cancellation errors propagate through callbacks (2)', function() {
- let result;
- return Promise.resolve().then(function() {
- return flow.execute(function() {
- result = subTask.then(assert.fail)
- .then(noop, assertCancellation)
- .then(() => 'fin');
+ it('includes promise chain when enabled', function() {
+ promise.LONG_STACK_TRACES = true;
+ flow.execute(function() {
+ flow.execute(function() {
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(throwStubError);
+ }, 'eventually assert.fails');
+ }, 'start').
+ then(assert.fail, function(e) {
+ assertIsStubError(e);
+ if (typeof e.stack !== 'string') {
+ return;
+ }
+ var messages = e.stack.split(/\n/).filter(function(line, index) {
+ return /^From: /.test(line);
});
- })
- // Verify result actually computed successfully all the way through.
- .then(() => result)
- .then(value => assert.equal('fin', value));
+ assert.deepEqual([
+ 'From: Promise: then',
+ 'From: Task: eventually assert.fails',
+ 'From: Task: start'
+ ], messages);
+ });
+ return waitForIdle();
});
});
- });
- it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() {
- var seen = [];
- return flow.execute(function() {
- flow.execute(throwStubError);
+ describe('frame cancels remaining tasks', function() {
+ it('on unhandled task failure', function() {
+ var run = false;
+ return flow.execute(function() {
+ flow.execute(throwStubError);
+ flow.execute(function() { run = true; });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
- flow.execute(function() {
- seen.push(1);
- }).then(function() {
- seen.push(2);
- }, function() {
- seen.push(3);
- });
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.deepEqual([], seen);
- });
- });
+ it('on unhandled promise rejection', function() {
+ var run = false;
+ return flow.execute(function() {
+ promise.rejected(new StubError);
+ flow.execute(function() { run = true; });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
- it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() {
- var seen = [];
- return flow.execute(function() {
- flow.execute(throwStubError);
+ it('if task throws', function() {
+ var run = false;
+ return flow.execute(function() {
+ flow.execute(function() { run = true; });
+ throw new StubError;
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.ok(!run);
+ });
+ });
- return flow.execute(function() {
- seen.push(1);
- }).then(function() {
- seen.push(2);
- }, function() {
- seen.push(3);
- });
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.deepEqual([], seen);
- });
- });
+ describe('task callbacks scheduled in another frame', function() {
+ flow = promise.controlFlow();
+ function noop() {}
- it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() {
- var seen = [];
- return flow.execute(function() {
- flow.execute(throwStubError);
+ let subTask;
- // None of the callbacks on this promise should execute because the
- // task assert.failure above is never handled, causing the containing task to
- // abort.
- promise.fulfilled().then(function() {
- seen.push(1);
- return flow.execute(function() {
- seen.push(2);
+ before(function() {
+ flow.execute(function() {
+ // This task will be discarded and never run because of the error below.
+ subTask = flow.execute(() => 'abc');
+ throw new StubError('stub');
+ }).catch(noop);
});
- }).finally(function() {
- seen.push(3);
- });
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.deepEqual([], seen);
- });
- });
+ function assertCancellation(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal(
+ 'Task was discarded due to a previous failure: stub', e.message);
+ }
- it('testTaskIsCancelledAfterWaitTimeout', function() {
- var seen = [];
- return flow.execute(function() {
- flow.wait(function() {
- return promise.delayed(50);
- }, 5);
+ it('are rejected with cancellation error', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask.then(assert.fail);
+ });
+ })
+ .then(() => result)
+ .then(assert.fail, assertCancellation);
+ });
- return flow.execute(function() {
- seen.push(1);
- }).then(function() {
- seen.push(2);
- }, function() {
- seen.push(3);
- });
- }).then(assert.fail, function() {
- assert.deepEqual([], seen);
+ it('cancellation errors propagate through callbacks (1)', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask
+ .then(assert.fail, assertCancellation)
+ .then(() => 'abc123');
+ });
+ })
+ .then(() => result)
+ .then(value => assert.equal('abc123', value));
+ });
+
+ it('cancellation errors propagate through callbacks (2)', function() {
+ let result;
+ return Promise.resolve().then(function() {
+ return flow.execute(function() {
+ result = subTask.then(assert.fail)
+ .then(noop, assertCancellation)
+ .then(() => 'fin');
+ });
+ })
+ // Verify result actually computed successfully all the way through.
+ .then(() => result)
+ .then(value => assert.equal('fin', value));
+ });
+ });
});
- });
- describe('task callbacks get cancellation error if registered after task was cancelled', function() {
- it('(a)', function() {
- var task;
- flow.execute(function() {
+ it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return', function() {
+ var seen = [];
+ return flow.execute(function() {
flow.execute(throwStubError);
- task = flow.execute(function() {});
- }).then(assert.fail, assertIsStubError);
- return waitForIdle().then(function() {
- return task.then(assert.fail, function(e) {
- assert.ok(e instanceof promise.CancellationError);
+
+ flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
});
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
});
});
- it('(b)', function() {
+ it('testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn', function() {
var seen = [];
-
- var task;
- flow.execute(function() {
+ return flow.execute(function() {
flow.execute(throwStubError);
- task = flow.execute(function() {});
- task.then(() => seen.push(1))
- .then(() => seen.push(2));
- task.then(() => seen.push(3))
- .then(() => seen.push(4));
+ return flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
+ });
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
+ });
+ });
- }).then(assert.fail, assertIsStubError);
+ it('testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.execute(throwStubError);
- return waitForIdle().then(function() {
- return task.then(assert.fail, function(e) {
- seen.push(5);
- assert.ok(e instanceof promise.CancellationError);
+ // None of the callbacks on this promise should execute because the
+ // task assert.failure above is never handled, causing the containing task to
+ // abort.
+ promise.fulfilled().then(function() {
+ seen.push(1);
+ return flow.execute(function() {
+ seen.push(2);
+ });
+ }).finally(function() {
+ seen.push(3);
});
- }).then(() => assert.deepEqual([5], seen));
+
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.deepEqual([], seen);
+ });
});
- });
- it('unhandledRejectionInParallelTaskQueue', function() {
- var seen = [];
- function schedule(name) {
- return flow.execute(() => seen.push(name), name);
- }
+ it('testTaskIsCancelledAfterWaitTimeout', function() {
+ var seen = [];
+ return flow.execute(function() {
+ flow.wait(function() {
+ return promise.delayed(50);
+ }, 5);
- flow.async(function() {
- schedule('a.1');
- flow.execute(throwStubError, 'a.2 (throws)');
+ return flow.execute(function() {
+ seen.push(1);
+ }).then(function() {
+ seen.push(2);
+ }, function() {
+ seen.push(3);
+ });
+ }).then(assert.fail, function() {
+ assert.deepEqual([], seen);
+ });
});
- var b3;
- flow.async(function() {
- schedule('b.1');
- schedule('b.2');
- b3 = schedule('b.3');
- });
+ describe('task callbacks get cancellation error if registered after task was cancelled', function() {
+ it('(a)', function() {
+ var task;
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+ }).then(assert.fail, assertIsStubError);
+ return waitForIdle().then(function() {
+ return task.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ });
+ });
- var c3;
- flow.async(function() {
- schedule('c.1');
- schedule('c.2');
- c3 = schedule('c.3');
- });
+ it('(b)', function() {
+ var seen = [];
+
+ var task;
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+
+ task.then(() => seen.push(1))
+ .then(() => seen.push(2));
+ task.then(() => seen.push(3))
+ .then(() => seen.push(4));
+
+ }).then(assert.fail, assertIsStubError);
- function assertWasCancelled(p) {
- return p.then(assert.fail, function(e) {
- assert.ok(e instanceof promise.CancellationError);
+ return waitForIdle().then(function() {
+ return task.then(assert.fail, function(e) {
+ seen.push(5);
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ }).then(() => assert.deepEqual([5], seen));
});
- }
+ });
- return waitForAbort()
- .then(function() {
- assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen);
- assert.ok(!b3.isPending());
- assert.ok(!c3.isPending());
- })
- .then(() => assertWasCancelled(b3))
- .then(() => assertWasCancelled(c3));
- });
+ it('unhandledRejectionInParallelTaskQueue', function() {
+ var seen = [];
+ function schedule(name) {
+ return flow.execute(() => seen.push(name), name);
+ }
- it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() {
- flow.removeAllListeners(); // For tearDown.
+ flow.async(function() {
+ schedule('a.1');
+ flow.execute(throwStubError, 'a.2 (throws)');
+ });
- var task;
- return new Promise(function(fulfill) {
- flow.once('uncaughtException', fulfill);
+ var b3;
flow.async(function() {
- task = flow.execute(function() {});
- throw Error('boom');
+ schedule('b.1');
+ schedule('b.2');
+ b3 = schedule('b.3');
});
- }).then(function(error) {
- assert.ok(error instanceof promise.CancellationError);
- assert.ok(!task.isPending());
- return task.catch(function(error) {
- assert.ok(error instanceof promise.CancellationError);
+
+ var c3;
+ flow.async(function() {
+ schedule('c.1');
+ schedule('c.2');
+ c3 = schedule('c.3');
});
+
+ function assertWasCancelled(p) {
+ return p.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ }
+
+ return waitForAbort()
+ .then(function() {
+ assert.deepEqual(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen);
+ })
+ .then(() => assertWasCancelled(b3))
+ .then(() => assertWasCancelled(c3));
});
- });
- describe('does not wait for values thrown from callbacks to be resolved', function() {
- it('(a)', function() {
- var p1 = promise.fulfilled();
- var reason = promise.fulfilled('should not see me');
- return p1.then(function() {
- throw reason;
- }).then(assert.fail, function(e) {
- assert.equal(reason, e);
+ it('errorsInAsyncFunctionsAreReportedAsUnhandledRejection', function() {
+ flow.removeAllListeners(); // For tearDown.
+
+ var task;
+ return new Promise(function(fulfill) {
+ flow.once('uncaughtException', fulfill);
+ flow.async(function() {
+ task = flow.execute(function() {});
+ throw Error('boom');
+ });
+ }).then(function(error) {
+ assert.ok(error instanceof promise.CancellationError);
+ return task.catch(function(error) {
+ assert.ok(error instanceof promise.CancellationError);
+ });
});
});
- it('(b)', function() {
- var p1 = promise.fulfilled();
- var reason = promise.rejected('should not see me');
- reason.catch(function() {}); // For tearDown.
- return p1.then(function() {
- throw reason;
- }).then(assert.fail, function(e) {
- assert.equal(reason, e);
+ describe('does not wait for values thrown from callbacks to be resolved', function() {
+ it('(a)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.fulfilled('should not see me');
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
});
- });
- it('(c)', function() {
- var p1 = promise.fulfilled();
- var reason = promise.defer();
- setTimeout(() => reason.fulfill('should not see me'), 100);
- return p1.then(function() {
- throw reason.promise;
- }).then(assert.fail, function(e) {
- assert.equal(reason.promise, e);
+ it('(b)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.rejected('should not see me');
+ reason.catch(function() {}); // For tearDown.
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
});
- });
- it('(d)', function() {
- var p1 = promise.fulfilled();
- var reason = {then: function() {}}; // A thenable like object.
- return p1.then(function() {
- throw reason;
- }).then(assert.fail, function(e) {
- assert.equal(reason, e);
+ it('(c)', function() {
+ var p1 = promise.fulfilled();
+ var reason = promise.defer();
+ setTimeout(() => reason.fulfill('should not see me'), 100);
+ return p1.then(function() {
+ throw reason.promise;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason.promise, e);
+ });
+ });
+
+ it('(d)', function() {
+ var p1 = promise.fulfilled();
+ var reason = {then: function() {}}; // A thenable like object.
+ return p1.then(function() {
+ throw reason;
+ }).then(assert.fail, function(e) {
+ assert.equal(reason, e);
+ });
});
});
});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_flow_test.js b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js
index b42ac5209..407fd547e 100644
--- a/node_modules/selenium-webdriver/test/lib/promise_flow_test.js
+++ b/node_modules/selenium-webdriver/test/lib/promise_flow_test.js
@@ -22,7 +22,9 @@ const fail = assert.fail;
const sinon = require('sinon');
const testutil = require('./testutil');
+const {TimeoutError} = require('../../lib/error');
const promise = require('../../lib/promise');
+const {enablePromiseManager} = require('../../lib/test/promise');
const NativePromise = Promise;
@@ -33,2252 +35,2254 @@ const callbackPair = testutil.callbackPair;
const throwStubError = testutil.throwStubError;
describe('promise control flow', function() {
- let flow, flowHistory, messages, uncaughtExceptions;
-
- beforeEach(function setUp() {
- promise.LONG_STACK_TRACES = false;
- flow = new promise.ControlFlow();
- promise.setDefaultFlow(flow);
- messages = [];
- flowHistory = [];
-
- uncaughtExceptions = [];
- flow.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
- onUncaughtException);
- });
-
- afterEach(function tearDown() {
- flow.removeAllListeners(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
- assert.deepEqual([], uncaughtExceptions,
- 'There were uncaught exceptions');
- flow.reset();
- promise.LONG_STACK_TRACES = false;
- });
+ enablePromiseManager(() => {
+ let flow, flowHistory, messages, uncaughtExceptions;
- function onUncaughtException(e) {
- uncaughtExceptions.push(e);
- }
-
- function waitForAbort(opt_flow) {
- var theFlow = opt_flow || flow;
- theFlow.removeAllListeners(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
- return new NativePromise(function(fulfill, reject) {
- theFlow.once(promise.ControlFlow.EventType.IDLE, function() {
- reject(Error('expected flow to report an unhandled error'));
- });
- theFlow.once(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
- fulfill);
- });
- }
-
- function waitForIdle(opt_flow) {
- var theFlow = opt_flow || flow;
- return new NativePromise(function(fulfill, reject) {
- theFlow.once(promise.ControlFlow.EventType.IDLE, fulfill);
- theFlow.once(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject);
- });
- }
+ beforeEach(function setUp() {
+ promise.LONG_STACK_TRACES = false;
+ flow = new promise.ControlFlow();
+ promise.setDefaultFlow(flow);
+ messages = [];
+ flowHistory = [];
- function timeout(ms) {
- return new NativePromise(function(fulfill) {
- setTimeout(fulfill, ms);
+ uncaughtExceptions = [];
+ flow.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ onUncaughtException);
});
- }
-
- function schedule(msg, opt_return) {
- return scheduleAction(msg, function() {
- return opt_return;
- });
- }
-
- /**
- * @param {string} value The value to push.
- * @param {promise.Promise=} opt_taskPromise Promise to return from
- * the task.
- * @return {!promise.Promise} The result.
- */
- function schedulePush(value, opt_taskPromise) {
- return scheduleAction(value, function() {
- messages.push(value);
- return opt_taskPromise;
- });
- }
-
- /**
- * @param {string} msg Debug message.
- * @param {!Function} actionFn The function.
- * @return {!promise.Promise} The function result.
- */
- function scheduleAction(msg, actionFn) {
- return promise.controlFlow().execute(function() {
- flowHistory.push(msg);
- return actionFn();
- }, msg);
- }
-
- /**
- * @param {!Function} condition The condition function.
- * @param {number=} opt_timeout The timeout.
- * @param {string=} opt_message Optional message.
- * @return {!promise.Promise} The wait result.
- */
- function scheduleWait(condition, opt_timeout, opt_message) {
- var msg = opt_message || '';
- // It's not possible to hook into when the wait itself is scheduled, so
- // we record each iteration of the wait loop.
- var count = 0;
- return promise.controlFlow().wait(function() {
- flowHistory.push((count++) + ': ' + msg);
- return condition();
- }, opt_timeout, msg);
- }
-
- function asyncRun(fn, opt_self) {
- NativePromise.resolve().then(() => fn.call(opt_self));
- }
-
- function assertFlowHistory(var_args) {
- var expected = Array.prototype.slice.call(arguments, 0);
- assert.deepEqual(expected, flowHistory);
- }
-
- function assertMessages(var_args) {
- var expected = Array.prototype.slice.call(arguments, 0);
- assert.deepEqual(expected, messages);
- }
-
- function assertingMessages(var_args) {
- var args = Array.prototype.slice.call(arguments, 0);
- return () => assertMessages.apply(null, args);
- }
-
- function assertFlowIs(flow) {
- assert.equal(flow, promise.controlFlow());
- }
-
- describe('testScheduling', function() {
- it('aSimpleFunction', function() {
- schedule('go');
- return waitForIdle().then(function() {
- assertFlowHistory('go');
- });
- });
-
- it('aSimpleFunctionWithANonPromiseReturnValue', function() {
- schedule('go', 123).then(function(value) {
- assert.equal(123, value);
- });
- return waitForIdle().then(function() {
- assertFlowHistory('go');
- });
+ afterEach(function tearDown() {
+ flow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ assert.deepEqual([], uncaughtExceptions,
+ 'There were uncaught exceptions');
+ flow.reset();
+ promise.LONG_STACK_TRACES = false;
});
- it('aSimpleSequence', function() {
- schedule('a');
- schedule('b');
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
+ function onUncaughtException(e) {
+ uncaughtExceptions.push(e);
+ }
- it('invokesCallbacksWhenTaskIsDone', function() {
- var d = new promise.Deferred();
- var called = false;
- var done = schedule('a', d.promise).then(function(value) {
- called = true;
- assert.equal(123, value);
- });
- return timeout(5).then(function() {
- assert.ok(!called);
- d.fulfill(123);
- return done;
- }).
- then(function() {
- assertFlowHistory('a');
+ function defer() {
+ let d = {};
+ let promise = new Promise((resolve, reject) => {
+ Object.assign(d, {resolve, reject});
});
- });
-
- it('blocksUntilPromiseReturnedByTaskIsResolved', function() {
- var done = promise.defer();
- schedulePush('a', done.promise);
- schedulePush('b');
- setTimeout(function() {
- done.fulfill();
- messages.push('c');
- }, 25);
- return waitForIdle().then(assertingMessages('a', 'c', 'b'));
- });
+ d.promise = promise;
+ return d;
+ }
- it('waitsForReturnedPromisesToResolve', function() {
- var d1 = new promise.Deferred();
- var d2 = new promise.Deferred();
-
- var callback = sinon.spy();
- schedule('a', d1.promise).then(callback);
-
- return timeout(5).then(function() {
- assert(!callback.called);
- d1.fulfill(d2.promise);
- return timeout(5);
- }).then(function() {
- assert(!callback.called);
- d2.fulfill('fluffy bunny');
- return waitForIdle();
- }).then(function() {
- assert(callback.called);
- assert.equal('fluffy bunny', callback.getCall(0).args[0]);
- assertFlowHistory('a');
+ function waitForAbort(opt_flow) {
+ var theFlow = opt_flow || flow;
+ theFlow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once(promise.ControlFlow.EventType.IDLE, function() {
+ reject(Error('expected flow to report an unhandled error'));
+ });
+ theFlow.once(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ fulfill);
});
- });
+ }
- it('executesTasksInAFutureTurnAfterTheyAreScheduled', function() {
+ function waitForIdle(opt_flow) {
+ var theFlow = opt_flow || flow;
+ return new NativePromise(function(fulfill, reject) {
+ theFlow.once(promise.ControlFlow.EventType.IDLE, fulfill);
+ theFlow.once(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject);
+ });
+ }
+
+ function timeout(ms) {
+ return new NativePromise(function(fulfill) {
+ setTimeout(fulfill, ms);
+ });
+ }
+
+
+ function schedule(msg, opt_return) {
+ return scheduleAction(msg, function() {
+ return opt_return;
+ });
+ }
+
+ /**
+ * @param {string} value The value to push.
+ * @param {promise.Promise=} opt_taskPromise Promise to return from
+ * the task.
+ * @return {!promise.Promise} The result.
+ */
+ function schedulePush(value, opt_taskPromise) {
+ return scheduleAction(value, function() {
+ messages.push(value);
+ return opt_taskPromise;
+ });
+ }
+
+ /**
+ * @param {string} msg Debug message.
+ * @param {!Function} actionFn The function.
+ * @return {!promise.Promise} The function result.
+ */
+ function scheduleAction(msg, actionFn) {
+ return promise.controlFlow().execute(function() {
+ flowHistory.push(msg);
+ return actionFn();
+ }, msg);
+ }
+
+ /**
+ * @param {!Function} condition The condition function.
+ * @param {number=} opt_timeout The timeout.
+ * @param {string=} opt_message Optional message.
+ * @return {!promise.Promise} The wait result.
+ */
+ function scheduleWait(condition, opt_timeout, opt_message) {
+ var msg = opt_message || '';
+ // It's not possible to hook into when the wait itself is scheduled, so
+ // we record each iteration of the wait loop.
var count = 0;
- function incr() { count++; }
-
- scheduleAction('', incr);
- assert.equal(0, count);
- return waitForIdle().then(function() {
- assert.equal(1, count);
+ return promise.controlFlow().wait(function() {
+ flowHistory.push((count++) + ': ' + msg);
+ return condition();
+ }, opt_timeout, msg);
+ }
+
+ function asyncRun(fn, opt_self) {
+ NativePromise.resolve().then(() => fn.call(opt_self));
+ }
+
+ function assertFlowHistory(var_args) {
+ var expected = Array.prototype.slice.call(arguments, 0);
+ assert.deepEqual(expected, flowHistory);
+ }
+
+ function assertMessages(var_args) {
+ var expected = Array.prototype.slice.call(arguments, 0);
+ assert.deepEqual(expected, messages);
+ }
+
+ function assertingMessages(var_args) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ return () => assertMessages.apply(null, args);
+ }
+
+ function assertFlowIs(flow) {
+ assert.equal(flow, promise.controlFlow());
+ }
+
+ describe('testScheduling', function() {
+ it('aSimpleFunction', function() {
+ schedule('go');
+ return waitForIdle().then(function() {
+ assertFlowHistory('go');
+ });
});
- });
- it('executesOneTaskPerTurnOfTheEventLoop', function() {
- var order = [];
- function go() {
- order.push(order.length / 2);
- asyncRun(function() {
- order.push('-');
+ it('aSimpleFunctionWithANonPromiseReturnValue', function() {
+ schedule('go', 123).then(function(value) {
+ assert.equal(123, value);
});
- }
-
- scheduleAction('', go);
- scheduleAction('', go);
- return waitForIdle().then(function() {
- assert.deepEqual([0, '-', 1, '-'], order);
- })
- });
+ return waitForIdle().then(function() {
+ assertFlowHistory('go');
+ });
+ });
- it('firstScheduledTaskIsWithinACallback', function() {
- promise.fulfilled().then(function() {
+ it('aSimpleSequence', function() {
schedule('a');
schedule('b');
schedule('c');
- }).then(function() {
- assertFlowHistory('a', 'b', 'c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- return waitForIdle();
- });
- it('newTasksAddedWhileWaitingOnTaskReturnedPromise1', function() {
- scheduleAction('a', function() {
- var d = promise.defer();
- setTimeout(function() {
- schedule('c');
- d.fulfill();
- }, 10);
- return d.promise;
- });
- schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ it('invokesCallbacksWhenTaskIsDone', function() {
+ var d = new promise.Deferred();
+ var called = false;
+ var done = schedule('a', d.promise).then(function(value) {
+ called = true;
+ assert.equal(123, value);
+ });
+ return timeout(5).then(function() {
+ assert.ok(!called);
+ d.fulfill(123);
+ return done;
+ }).
+ then(function() {
+ assertFlowHistory('a');
+ });
});
- });
- it('newTasksAddedWhileWaitingOnTaskReturnedPromise2', function() {
- scheduleAction('a', function() {
- var d = promise.defer();
+ it('blocksUntilPromiseReturnedByTaskIsResolved', function() {
+ var done = promise.defer();
+ schedulePush('a', done.promise);
+ schedulePush('b');
setTimeout(function() {
- schedule('c');
- asyncRun(d.fulfill);
- }, 10);
- return d.promise;
- });
- schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'b');
+ done.fulfill();
+ messages.push('c');
+ }, 25);
+ return waitForIdle().then(assertingMessages('a', 'c', 'b'));
});
- });
- });
- describe('testFraming', function() {
- it('callbacksRunInANewFrame', function() {
- schedule('a').then(function() {
- schedule('c');
+ it('waitsForReturnedPromisesToResolve', function() {
+ var d1 = new promise.Deferred();
+ var d2 = new promise.Deferred();
+
+ var callback = sinon.spy();
+ schedule('a', d1.promise).then(callback);
+
+ return timeout(5).then(function() {
+ assert(!callback.called);
+ d1.fulfill(d2.promise);
+ return timeout(5);
+ }).then(function() {
+ assert(!callback.called);
+ d2.fulfill('fluffy bunny');
+ return waitForIdle();
+ }).then(function() {
+ assert(callback.called);
+ assert.equal('fluffy bunny', callback.getCall(0).args[0]);
+ assertFlowHistory('a');
+ });
});
- schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'b');
+
+ it('executesTasksInAFutureTurnAfterTheyAreScheduled', function() {
+ var count = 0;
+ function incr() { count++; }
+
+ scheduleAction('', incr);
+ assert.equal(0, count);
+ return waitForIdle().then(function() {
+ assert.equal(1, count);
+ });
});
- });
- it('lotsOfNesting', function() {
- schedule('a').then(function() {
- schedule('c').then(function() {
- schedule('e').then(function() {
- schedule('g');
+ it('executesOneTaskPerTurnOfTheEventLoop', function() {
+ var order = [];
+ function go() {
+ order.push(order.length / 2);
+ asyncRun(function() {
+ order.push('-');
});
- schedule('f');
- });
- schedule('d');
+ }
+
+ scheduleAction('', go);
+ scheduleAction('', go);
+ return waitForIdle().then(function() {
+ assert.deepEqual([0, '-', 1, '-'], order);
+ })
});
- schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b');
+ it('firstScheduledTaskIsWithinACallback', function() {
+ promise.fulfilled().then(function() {
+ schedule('a');
+ schedule('b');
+ schedule('c');
+ }).then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
+ return waitForIdle();
});
- });
- it('callbackReturnsPromiseThatDependsOnATask_1', function() {
- schedule('a').then(function() {
+ it('newTasksAddedWhileWaitingOnTaskReturnedPromise1', function() {
+ scheduleAction('a', function() {
+ var d = promise.defer();
+ setTimeout(function() {
+ schedule('c');
+ d.fulfill();
+ }, 10);
+ return d.promise;
+ });
schedule('b');
- return promise.delayed(5).then(function() {
- return schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
});
});
- schedule('d');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd');
+ it('newTasksAddedWhileWaitingOnTaskReturnedPromise2', function() {
+ scheduleAction('a', function() {
+ var d = promise.defer();
+ setTimeout(function() {
+ schedule('c');
+ asyncRun(d.fulfill);
+ }, 10);
+ return d.promise;
+ });
+ schedule('b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
});
});
- it('callbackReturnsPromiseThatDependsOnATask_2', function() {
- schedule('a').then(function() {
+ describe('testFraming', function() {
+ it('callbacksRunInANewFrame', function() {
+ schedule('a').then(function() {
+ schedule('c');
+ });
schedule('b');
- return promise.delayed(5).
- then(function() { return promise.delayed(5) }).
- then(function() { return promise.delayed(5) }).
- then(function() { return promise.delayed(5) }).
- then(function() { return schedule('c'); });
- });
- schedule('d');
-
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
});
- });
- it('eachCallbackWaitsForAllScheduledTasksToComplete', function() {
- schedule('a').
- then(function() {
- schedule('b');
- schedule('c');
- }).
- then(function() {
- schedule('d');
+ it('lotsOfNesting', function() {
+ schedule('a').then(function() {
+ schedule('c').then(function() {
+ schedule('e').then(function() {
+ schedule('g');
+ });
+ schedule('f');
});
- schedule('e');
+ schedule('d');
+ });
+ schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b');
+ });
});
- });
- it('eachCallbackWaitsForReturnTasksToComplete', function() {
- schedule('a').
- then(function() {
- schedule('b');
+ it('callbackReturnsPromiseThatDependsOnATask_1', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ return promise.delayed(5).then(function() {
return schedule('c');
- }).
- then(function() {
- schedule('d');
});
- schedule('e');
+ });
+ schedule('d');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
});
- });
- it('callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow', function() {
- promise.fulfilled().then(function() {
- schedule('b');
- });
- schedule('a');
+ it('callbackReturnsPromiseThatDependsOnATask_2', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ return promise.delayed(5).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return schedule('c'); });
+ });
+ schedule('d');
- return waitForIdle().then(function() {
- assertFlowHistory('b', 'a');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
});
- });
- it('callbacksInterruptTheFlowWhenPromiseIsResolved', function() {
- schedule('a').then(function() {
- schedule('c');
- });
- schedule('b');
+ it('eachCallbackWaitsForAllScheduledTasksToComplete', function() {
+ schedule('a').
+ then(function() {
+ schedule('b');
+ schedule('c');
+ }).
+ then(function() {
+ schedule('d');
+ });
+ schedule('e');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
});
- });
- it('allCallbacksInAFrameAreScheduledWhenPromiseIsResolved', function() {
- var a = schedule('a');
- a.then(function() { schedule('b'); });
- schedule('c');
- a.then(function() { schedule('d'); });
- schedule('e');
+ it('eachCallbackWaitsForReturnTasksToComplete', function() {
+ schedule('a').
+ then(function() {
+ schedule('b');
+ return schedule('c');
+ }).
+ then(function() {
+ schedule('d');
+ });
+ schedule('e');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
});
- });
- it('tasksScheduledInInActiveFrameDoNotGetPrecedence', function() {
- var d = promise.fulfilled();
- schedule('a');
- schedule('b');
- d.then(function() { schedule('c'); });
+ it('callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow', function() {
+ promise.fulfilled().then(function() {
+ schedule('b');
+ });
+ schedule('a');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('b', 'a');
+ });
});
- });
- it('tasksScheduledInAFrameGetPrecedence_1', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- a.then(function() {
+ it('callbacksInterruptTheFlowWhenPromiseIsResolved', function() {
+ schedule('a').then(function() {
schedule('c');
- schedule('d');
- });
- var e = schedule('e');
- a.then(function() {
- schedule('f');
- e.then(function() {
- schedule('g');
- });
- schedule('h');
});
- schedule('i');
- });
- schedule('j');
+ schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
});
- });
- });
- describe('testErrorHandling', function() {
- it('thrownErrorsArePassedToTaskErrback', function() {
- scheduleAction('function that throws', throwStubError).
- then(fail, assertIsStubError);
- return waitForIdle();
- });
+ it('allCallbacksInAFrameAreScheduledWhenPromiseIsResolved', function() {
+ var a = schedule('a');
+ a.then(function() { schedule('b'); });
+ schedule('c');
+ a.then(function() { schedule('d'); });
+ schedule('e');
- it('thrownErrorsPropagateThroughPromiseChain', function() {
- scheduleAction('function that throws', throwStubError).
- then(fail).
- then(fail, assertIsStubError);
- return waitForIdle();
- });
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
+ });
- it('catchesErrorsFromFailedTasksInAFrame', function() {
- schedule('a').then(function() {
+ it('tasksScheduledInInActiveFrameDoNotGetPrecedence', function() {
+ var d = promise.fulfilled();
+ schedule('a');
schedule('b');
- scheduleAction('function that throws', throwStubError);
- }).
- then(fail, assertIsStubError);
- return waitForIdle();
- });
+ d.then(function() { schedule('c'); });
- it('abortsIfOnlyTaskReturnsAnUnhandledRejection', function() {
- scheduleAction('function that returns rejected promise', function() {
- return promise.rejected(new StubError);
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- return waitForAbort().then(assertIsStubError);
- });
- it('abortsIfThereIsAnUnhandledRejection', function() {
- promise.rejected(new StubError);
- schedule('this should not run');
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory(/* none */);
- });
- });
-
- it('abortsSequenceIfATaskFails', function() {
- schedule('a');
- schedule('b');
- scheduleAction('c', throwStubError);
- schedule('d'); // Should never execute.
-
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'b', 'c');
+ it('tasksScheduledInAFrameGetPrecedence_1', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() {
+ schedule('c');
+ schedule('d');
});
- });
-
- it('abortsFromUnhandledFramedTaskFailures_1', function() {
- schedule('outer task').then(function() {
- scheduleAction('inner task', throwStubError);
- });
- schedule('this should not run');
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('outer task', 'inner task');
+ var e = schedule('e');
+ a.then(function() {
+ schedule('f');
+ e.then(function() {
+ schedule('g');
+ });
+ schedule('h');
});
- });
+ schedule('i');
+ });
+ schedule('j');
- it('abortsFromUnhandledFramedTaskFailures_2', function() {
- schedule('a').then(function() {
- schedule('b').then(function() {
- scheduleAction('c', throwStubError);
- // This should not execute.
- schedule('d');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');
});
});
-
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
});
- it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject', function() {
- var callback = sinon.spy();
+ describe('testErrorHandling', function() {
+ it('thrownErrorsArePassedToTaskErrback', function() {
+ scheduleAction('function that throws', throwStubError).
+ then(fail, assertIsStubError);
+ return waitForIdle();
+ });
- scheduleAction('', function() {
- var obj = {'foo': promise.rejected(new StubError)};
- return promise.fullyResolved(obj).then(callback);
+ it('thrownErrorsPropagateThroughPromiseChain', function() {
+ scheduleAction('function that throws', throwStubError).
+ then(fail).
+ then(fail, assertIsStubError);
+ return waitForIdle();
});
- return waitForAbort().
- then(assertIsStubError).
- then(() => assert(!callback.called));
- });
+ it('catchesErrorsFromFailedTasksInAFrame', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ scheduleAction('function that throws', throwStubError);
+ }).
+ then(fail, assertIsStubError);
+ return waitForIdle();
+ });
- it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback', function() {
- var callback1 = sinon.spy();
- var callback2 = sinon.spy();
+ it('abortsIfOnlyTaskReturnsAnUnhandledRejection', function() {
+ scheduleAction('function that returns rejected promise', function() {
+ return promise.rejected(new StubError);
+ });
+ return waitForAbort().then(assertIsStubError);
+ });
- scheduleAction('', function() {
- var obj = {'foo': promise.rejected(new StubError)};
- return promise.fullyResolved(obj).then(callback1);
- }).then(callback2);
+ it('abortsIfThereIsAnUnhandledRejection', function() {
+ promise.rejected(new StubError);
+ schedule('this should not run');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory(/* none */);
+ });
+ });
- return waitForAbort().
- then(assertIsStubError).
- then(() => assert(!callback1.called)).
- then(() => assert(!callback2.called));
- });
+ it('abortsSequenceIfATaskFails', function() {
+ schedule('a');
+ schedule('b');
+ scheduleAction('c', throwStubError);
+ schedule('d'); // Should never execute.
- it('canCatchErrorsFromNestedTasks', function() {
- var errback = sinon.spy();
- schedule('a').
- then(function() {
- return scheduleAction('b', throwStubError);
- }).
- catch(errback);
- return waitForIdle().then(function() {
- assert(errback.called);
- assertIsStubError(errback.getCall(0).args[0]);
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- });
- it('nestedCommandFailuresCanBeCaughtAndSuppressed', function() {
- var errback = sinon.spy();
- schedule('a').then(function() {
- return schedule('b').then(function() {
- return schedule('c').then(function() {
- throw new StubError;
- });
+ it('abortsFromUnhandledFramedTaskFailures_1', function() {
+ schedule('outer task').then(function() {
+ scheduleAction('inner task', throwStubError);
});
- }).catch(errback);
- schedule('d');
- return waitForIdle().
- then(function() {
- assert(errback.called);
- assertIsStubError(errback.getCall(0).args[0]);
- assertFlowHistory('a', 'b', 'c', 'd');
- });
- });
-
- it('aTaskWithAnUnhandledPromiseRejection', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- promise.rejected(new StubError);
+ schedule('this should not run');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('outer task', 'inner task');
+ });
});
- schedule('should never run');
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'sub-tasks');
+ it('abortsFromUnhandledFramedTaskFailures_2', function() {
+ schedule('a').then(function() {
+ schedule('b').then(function() {
+ scheduleAction('c', throwStubError);
+ // This should not execute.
+ schedule('d');
});
- });
+ });
- it('aTaskThatReutrnsARejectedPromise', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- return promise.rejected(new StubError);
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- schedule('should never run');
-
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'sub-tasks');
- });
- });
- it('discardsSubtasksIfTaskThrows', function() {
- var pair = callbackPair(null, assertIsStubError);
- scheduleAction('a', function() {
- schedule('b');
- schedule('c');
- throwStubError();
- }).then(pair.callback, pair.errback);
- schedule('d');
+ it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject', function() {
+ var callback = sinon.spy();
- return waitForIdle().
- then(pair.assertErrback).
- then(function() {
- assertFlowHistory('a', 'd');
- });
- });
+ scheduleAction('', function() {
+ var obj = {'foo': promise.rejected(new StubError)};
+ return promise.fullyResolved(obj).then(callback);
+ });
- it('discardsRemainingSubtasksIfASubtaskFails', function() {
- var pair = callbackPair(null, assertIsStubError);
- scheduleAction('a', function() {
- schedule('b');
- scheduleAction('c', throwStubError);
- schedule('d');
- }).then(pair.callback, pair.errback);
- schedule('e');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(() => assert(!callback.called));
+ });
- return waitForIdle().
- then(pair.assertErrback).
- then(function() {
- assertFlowHistory('a', 'b', 'c', 'e');
- });
- });
- });
+ it('abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback', function() {
+ var callback1 = sinon.spy();
+ var callback2 = sinon.spy();
- describe('testTryModelingFinally', function() {
- it('happyPath', function() {
- /* Model:
- try {
- doFoo();
- doBar();
- } finally {
- doBaz();
- }
- */
- schedulePush('foo').
- then(() => schedulePush('bar')).
- finally(() => schedulePush('baz'));
- return waitForIdle().then(assertingMessages('foo', 'bar', 'baz'));
- });
+ scheduleAction('', function() {
+ var obj = {'foo': promise.rejected(new StubError)};
+ return promise.fullyResolved(obj).then(callback1);
+ }).then(callback2);
- it('firstTryFails', function() {
- /* Model:
- try {
- doFoo();
- doBar();
- } finally {
- doBaz();
- }
- */
-
- scheduleAction('doFoo and throw', function() {
- messages.push('foo');
- throw new StubError;
- }).
- then(function() { schedulePush('bar'); }).
- finally(function() { schedulePush('baz'); });
+ return waitForAbort().
+ then(assertIsStubError).
+ then(() => assert(!callback1.called)).
+ then(() => assert(!callback2.called));
+ });
- return waitForAbort().
- then(assertIsStubError).
- then(assertingMessages('foo', 'baz'));
- });
+ it('canCatchErrorsFromNestedTasks', function() {
+ var errback = sinon.spy();
+ schedule('a').
+ then(function() {
+ return scheduleAction('b', throwStubError);
+ }).
+ catch(errback);
+ return waitForIdle().then(function() {
+ assert(errback.called);
+ assertIsStubError(errback.getCall(0).args[0]);
+ });
+ });
- it('secondTryFails', function() {
- /* Model:
- try {
- doFoo();
- doBar();
- } finally {
- doBaz();
- }
- */
-
- schedulePush('foo').
- then(function() {
- return scheduleAction('doBar and throw', function() {
- messages.push('bar');
+ it('nestedCommandFailuresCanBeCaughtAndSuppressed', function() {
+ var errback = sinon.spy();
+ schedule('a').then(function() {
+ return schedule('b').then(function() {
+ return schedule('c').then(function() {
throw new StubError;
});
- }).
- finally(function() {
- return schedulePush('baz');
});
- return waitForAbort().
- then(assertIsStubError).
- then(assertingMessages('foo', 'bar', 'baz'));
- });
- });
-
- describe('testTaskCallbacksInterruptFlow', function() {
- it('(base case)', function() {
- schedule('a').then(function() {
- schedule('b');
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ }).catch(errback);
+ schedule('d');
+ return waitForIdle().
+ then(function() {
+ assert(errback.called);
+ assertIsStubError(errback.getCall(0).args[0]);
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
});
- });
- it('taskDependsOnImmediatelyFulfilledPromise', function() {
- scheduleAction('a', function() {
- return promise.fulfilled();
- }).then(function() {
- schedule('b');
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
+ it('aTaskWithAnUnhandledPromiseRejection', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ promise.rejected(new StubError);
+ });
+ schedule('should never run');
- it('taskDependsOnPreviouslyFulfilledPromise', function() {
- var aPromise = promise.fulfilled(123);
- scheduleAction('a', function() {
- return aPromise;
- }).then(function(value) {
- assert.equal(123, value);
- schedule('b');
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
});
- });
- it('taskDependsOnAsyncPromise', function() {
- scheduleAction('a', function() {
- return promise.delayed(25);
- }).then(function() {
- schedule('b');
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
+ it('aTaskThatReutrnsARejectedPromise', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ });
+ schedule('should never run');
- it('promiseChainedToTaskInterruptFlow', function() {
- schedule('a').then(function() {
- return promise.fulfilled();
- }).then(function() {
- return promise.fulfilled();
- }).then(function() {
- schedule('b');
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
});
- });
- it('nestedTaskCallbacksInterruptFlowWhenResolved', function() {
- schedule('a').then(function() {
- schedule('b').then(function() {
+ it('discardsSubtasksIfTaskThrows', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleAction('a', function() {
+ schedule('b');
schedule('c');
- });
- });
- schedule('d');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd');
+ throwStubError();
+ }).then(pair.callback, pair.errback);
+ schedule('d');
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory('a', 'd');
+ });
});
- });
- });
- describe('testDelayedNesting', function() {
+ it('discardsRemainingSubtasksIfASubtaskFails', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleAction('a', function() {
+ schedule('b');
+ scheduleAction('c', throwStubError);
+ schedule('d');
+ }).then(pair.callback, pair.errback);
+ schedule('e');
- it('1', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- a.then(function() { schedule('c'); });
- schedule('d');
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory('a', 'b', 'c', 'e');
+ });
});
- schedule('e');
+ });
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ describe('testTryModelingFinally', function() {
+ it('happyPath', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+ schedulePush('foo').
+ then(() => schedulePush('bar')).
+ finally(() => schedulePush('baz'));
+ return waitForIdle().then(assertingMessages('foo', 'bar', 'baz'));
+ });
+
+ it('firstTryFails', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+
+ scheduleAction('doFoo and throw', function() {
+ messages.push('foo');
+ throw new StubError;
+ }).
+ then(function() { schedulePush('bar'); }).
+ finally(function() { schedulePush('baz'); });
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(assertingMessages('foo', 'baz'));
+ });
+
+ it('secondTryFails', function() {
+ /* Model:
+ try {
+ doFoo();
+ doBar();
+ } finally {
+ doBaz();
+ }
+ */
+
+ schedulePush('foo').
+ then(function() {
+ return scheduleAction('doBar and throw', function() {
+ messages.push('bar');
+ throw new StubError;
+ });
+ }).
+ finally(function() {
+ return schedulePush('baz');
+ });
+ return waitForAbort().
+ then(assertIsStubError).
+ then(assertingMessages('foo', 'bar', 'baz'));
});
});
- it('2', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- a.then(function() { schedule('c'); });
- schedule('d');
- a.then(function() { schedule('e'); });
+ describe('testTaskCallbacksInterruptFlow', function() {
+ it('(base case)', function() {
+ schedule('a').then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- schedule('f');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ it('taskDependsOnImmediatelyFulfilledPromise', function() {
+ scheduleAction('a', function() {
+ return promise.fulfilled();
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- });
- it('3', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- a.then(function() { schedule('c'); });
- a.then(function() { schedule('d'); });
+ it('taskDependsOnPreviouslyFulfilledPromise', function() {
+ var aPromise = promise.fulfilled(123);
+ scheduleAction('a', function() {
+ return aPromise;
+ }).then(function(value) {
+ assert.equal(123, value);
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- schedule('e');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ it('taskDependsOnAsyncPromise', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(25);
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- });
- it('4', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- a.then(function() { schedule('c'); }).then(function() {
- schedule('d');
+ it('promiseChainedToTaskInterruptFlow', function() {
+ schedule('a').then(function() {
+ return promise.fulfilled();
+ }).then(function() {
+ return promise.fulfilled();
+ }).then(function() {
+ schedule('b');
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
});
- a.then(function() { schedule('e'); });
});
- schedule('f');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ it('nestedTaskCallbacksInterruptFlowWhenResolved', function() {
+ schedule('a').then(function() {
+ schedule('b').then(function() {
+ schedule('c');
+ });
+ });
+ schedule('d');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd');
+ });
});
});
- it('5', function() {
- var a = schedule('a');
- schedule('b').then(function() {
- var c;
- a.then(function() { c = schedule('c'); }).then(function() {
+ describe('testDelayedNesting', function() {
+
+ it('1', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
schedule('d');
- a.then(function() { schedule('e'); });
- c.then(function() { schedule('f'); });
- schedule('g');
});
- a.then(function() { schedule('h'); });
- });
- schedule('i');
-
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');
- });
- });
- });
+ schedule('e');
- describe('testWaiting', function() {
- it('onAConditionThatIsAlwaysTrue', function() {
- scheduleWait(function() { return true;}, 0, 'waiting on true');
- return waitForIdle().then(function() {
- assertFlowHistory('0: waiting on true');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
});
- });
- it('aSimpleCountingCondition', function() {
- var count = 0;
- scheduleWait(function() {
- return ++count == 3;
- }, 100, 'counting to 3');
+ it('2', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
+ schedule('d');
+ a.then(function() { schedule('e'); });
+ });
+ schedule('f');
- return waitForIdle().then(function() {
- assert.equal(3, count);
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ });
});
- });
-
- it('aConditionThatReturnsAPromise', function() {
- var d = new promise.Deferred();
- var count = 0;
- scheduleWait(function() {
- count += 1;
- return d.promise;
- }, 0, 'waiting for promise');
+ it('3', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); });
+ a.then(function() { schedule('d'); });
+ });
+ schedule('e');
- return timeout(50).then(function() {
- assert.equal(1, count);
- d.fulfill(123);
- return waitForIdle();
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e');
+ });
});
- });
- it('aConditionThatReturnsAPromise_2', function() {
- var count = 0;
- scheduleWait(function() {
- return promise.fulfilled(++count == 3);
- }, 100, 'waiting for promise');
+ it('4', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ a.then(function() { schedule('c'); }).then(function() {
+ schedule('d');
+ });
+ a.then(function() { schedule('e'); });
+ });
+ schedule('f');
- return waitForIdle().then(function() {
- assert.equal(3, count);
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
+ });
});
- });
- it('aConditionThatReturnsATaskResult', function() {
- var count = 0;
- scheduleWait(function() {
- return scheduleAction('increment count', function() {
- return ++count == 3;
+ it('5', function() {
+ var a = schedule('a');
+ schedule('b').then(function() {
+ var c;
+ a.then(function() { c = schedule('c'); }).then(function() {
+ schedule('d');
+ a.then(function() { schedule('e'); });
+ c.then(function() { schedule('f'); });
+ schedule('g');
+ });
+ a.then(function() { schedule('h'); });
});
- }, 100, 'counting to 3');
- schedule('post wait');
+ schedule('i');
- return waitForIdle().then(function() {
- assert.equal(3, count);
- assertFlowHistory(
- '0: counting to 3', 'increment count',
- '1: counting to 3', 'increment count',
- '2: counting to 3', 'increment count',
- 'post wait');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');
+ });
});
});
- it('conditionContainsASubtask', function() {
- var count = 0;
- scheduleWait(function() {
- schedule('sub task');
- return ++count == 3;
- }, 100, 'counting to 3');
- schedule('post wait');
-
- return waitForIdle().then(function() {
- assert.equal(3, count);
- assertFlowHistory(
- '0: counting to 3', 'sub task',
- '1: counting to 3', 'sub task',
- '2: counting to 3', 'sub task',
- 'post wait');
+ describe('testWaiting', function() {
+ it('onAConditionThatIsAlwaysTrue', function() {
+ scheduleWait(function() { return true;}, 0, 'waiting on true');
+ return waitForIdle().then(function() {
+ assertFlowHistory('0: waiting on true');
+ });
});
- });
- it('cancelsWaitIfScheduledTaskFails', function() {
- var pair = callbackPair(null, assertIsStubError);
- scheduleWait(function() {
- scheduleAction('boom', throwStubError);
- schedule('this should not run');
- return true;
- }, 100, 'waiting to go boom').then(pair.callback, pair.errback);
- schedule('post wait');
+ it('aSimpleCountingCondition', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return ++count == 3;
+ }, 100, 'counting to 3');
- return waitForIdle().
- then(pair.assertErrback).
- then(function() {
- assertFlowHistory(
- '0: waiting to go boom', 'boom',
- 'post wait');
- });
- });
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ });
+ });
- it('failsIfConditionThrows', function() {
- var callbacks = callbackPair(null, assertIsStubError);
- scheduleWait(throwStubError, 0, 'goes boom').
- then(callbacks.callback, callbacks.errback);
- schedule('post wait');
+ it('aConditionThatReturnsAPromise', function() {
+ var d = new promise.Deferred();
+ var count = 0;
- return waitForIdle().
- then(callbacks.assertErrback).
- then(function() {
- assertFlowHistory('0: goes boom', 'post wait');
- });
- });
+ scheduleWait(function() {
+ count += 1;
+ return d.promise;
+ }, 0, 'waiting for promise');
- it('failsIfConditionReturnsARejectedPromise', function() {
- var callbacks = callbackPair(null, assertIsStubError);
- scheduleWait(function() {
- return promise.rejected(new StubError);
- }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
- schedule('post wait');
+ return timeout(50).then(function() {
+ assert.equal(1, count);
+ d.fulfill(123);
+ return waitForIdle();
+ });
+ });
- return waitForIdle().
- then(callbacks.assertErrback).
- then(function() {
- assertFlowHistory('0: goes boom', 'post wait');
- });
- });
+ it('aConditionThatReturnsAPromise_2', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return promise.fulfilled(++count == 3);
+ }, 100, 'waiting for promise');
- it('failsIfConditionHasUnhandledRejection', function() {
- var callbacks = callbackPair(null, assertIsStubError);
- scheduleWait(function() {
- promise.controlFlow().execute(throwStubError);
- }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
- schedule('post wait');
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ });
+ });
- return waitForIdle().
- then(callbacks.assertErrback).
- then(function() {
- assertFlowHistory('0: goes boom', 'post wait');
+ it('aConditionThatReturnsATaskResult', function() {
+ var count = 0;
+ scheduleWait(function() {
+ return scheduleAction('increment count', function() {
+ return ++count == 3;
});
- });
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ assertFlowHistory(
+ '0: counting to 3', 'increment count',
+ '1: counting to 3', 'increment count',
+ '2: counting to 3', 'increment count',
+ 'post wait');
+ });
+ });
- it('failsIfConditionHasAFailedSubtask', function() {
- var callbacks = callbackPair(null, assertIsStubError);
- var count = 0;
- scheduleWait(function() {
- scheduleAction('maybe throw', function() {
- if (++count == 2) {
- throw new StubError;
- }
+ it('conditionContainsASubtask', function() {
+ var count = 0;
+ scheduleWait(function() {
+ schedule('sub task');
+ return ++count == 3;
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(3, count);
+ assertFlowHistory(
+ '0: counting to 3', 'sub task',
+ '1: counting to 3', 'sub task',
+ '2: counting to 3', 'sub task',
+ 'post wait');
});
- }, 100, 'waiting').then(callbacks.callback, callbacks.errback);
- schedule('post wait');
+ });
- return waitForIdle().then(function() {
- assert.equal(2, count);
- assertFlowHistory(
- '0: waiting', 'maybe throw',
- '1: waiting', 'maybe throw',
- 'post wait');
+ it('cancelsWaitIfScheduledTaskFails', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return true;
+ }, 100, 'waiting to go boom').then(pair.callback, pair.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ assertFlowHistory(
+ '0: waiting to go boom', 'boom',
+ 'post wait');
+ });
});
- });
- it('pollingLoopWaitsForAllScheduledTasksInCondition', function() {
- var count = 0;
- scheduleWait(function() {
- scheduleAction('increment count', function() { ++count; });
- return count >= 3;
- }, 100, 'counting to 3');
- schedule('post wait');
+ it('failsIfConditionThrows', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(throwStubError, 0, 'goes boom').
+ then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
- return waitForIdle().then(function() {
- assert.equal(4, count);
- assertFlowHistory(
- '0: counting to 3', 'increment count',
- '1: counting to 3', 'increment count',
- '2: counting to 3', 'increment count',
- '3: counting to 3', 'increment count',
- 'post wait');
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
});
- });
- it('waitsForeverOnAZeroTimeout', function() {
- var done = false;
- setTimeout(function() {
- done = true;
- }, 150);
- var waitResult = scheduleWait(function() {
- return done;
- }, 0);
+ it('failsIfConditionReturnsARejectedPromise', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ return promise.rejected(new StubError);
+ }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
- return timeout(75).then(function() {
- assert.ok(!done);
- return timeout(100);
- }).then(function() {
- assert.ok(done);
- return waitResult;
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
});
- });
- it('waitsForeverIfTimeoutOmitted', function() {
- var done = false;
- setTimeout(function() {
- done = true;
- }, 150);
- var waitResult = scheduleWait(function() {
- return done;
+ it('failsIfConditionHasUnhandledRejection', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ scheduleWait(function() {
+ promise.controlFlow().execute(throwStubError);
+ }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().
+ then(callbacks.assertErrback).
+ then(function() {
+ assertFlowHistory('0: goes boom', 'post wait');
+ });
});
- return timeout(75).then(function() {
- assert.ok(!done);
- return timeout(100);
- }).then(function() {
- assert.ok(done);
- return waitResult;
+ it('failsIfConditionHasAFailedSubtask', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
+ var count = 0;
+ scheduleWait(function() {
+ scheduleAction('maybe throw', function() {
+ if (++count == 2) {
+ throw new StubError;
+ }
+ });
+ }, 100, 'waiting').then(callbacks.callback, callbacks.errback);
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(2, count);
+ assertFlowHistory(
+ '0: waiting', 'maybe throw',
+ '1: waiting', 'maybe throw',
+ 'post wait');
+ });
});
- });
- it('timesOut_nonZeroTimeout', function() {
- var count = 0;
- scheduleWait(function() {
- count += 1;
- var ms = count === 2 ? 65 : 5;
- return promise.delayed(ms).then(function() {
- return false;
- });
- }, 60, 'counting to 3');
- return waitForAbort().then(function(e) {
- switch (count) {
- case 1:
- assertFlowHistory('0: counting to 3');
- break;
- case 2:
- assertFlowHistory('0: counting to 3', '1: counting to 3');
- break;
- default:
- fail('unexpected polling count: ' + count);
- }
- assert.ok(
- /^counting to 3\nWait timed out after \d+ms$/.test(e.message));
+ it('pollingLoopWaitsForAllScheduledTasksInCondition', function() {
+ var count = 0;
+ scheduleWait(function() {
+ scheduleAction('increment count', function() { ++count; });
+ return count >= 3;
+ }, 100, 'counting to 3');
+ schedule('post wait');
+
+ return waitForIdle().then(function() {
+ assert.equal(4, count);
+ assertFlowHistory(
+ '0: counting to 3', 'increment count',
+ '1: counting to 3', 'increment count',
+ '2: counting to 3', 'increment count',
+ '3: counting to 3', 'increment count',
+ 'post wait');
+ });
});
- });
- it('shouldFailIfConditionReturnsARejectedPromise', function() {
- scheduleWait(function() {
- return promise.rejected(new StubError);
- }, 100, 'returns rejected promise on first pass');
- return waitForAbort().then(assertIsStubError);
- });
+ it('waitsForeverOnAZeroTimeout', function() {
+ var done = false;
+ setTimeout(function() {
+ done = true;
+ }, 150);
+ var waitResult = scheduleWait(function() {
+ return done;
+ }, 0);
+
+ return timeout(75).then(function() {
+ assert.ok(!done);
+ return timeout(100);
+ }).then(function() {
+ assert.ok(done);
+ return waitResult;
+ });
+ });
- it('scheduleWithIntermittentWaits', function() {
- schedule('a');
- scheduleWait(function() { return true; }, 0, 'wait 1');
- schedule('b');
- scheduleWait(function() { return true; }, 0, 'wait 2');
- schedule('c');
- scheduleWait(function() { return true; }, 0, 'wait 3');
+ it('waitsForeverIfTimeoutOmitted', function() {
+ var done = false;
+ setTimeout(function() {
+ done = true;
+ }, 150);
+ var waitResult = scheduleWait(function() {
+ return done;
+ });
- return waitForIdle().then(function() {
- assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3');
+ return timeout(75).then(function() {
+ assert.ok(!done);
+ return timeout(100);
+ }).then(function() {
+ assert.ok(done);
+ return waitResult;
+ });
});
- });
- it('scheduleWithIntermittentAndNestedWaits', function() {
- schedule('a');
- scheduleWait(function() { return true; }, 0, 'wait 1').
- then(function() {
- schedule('d');
- scheduleWait(function() { return true; }, 0, 'wait 2');
- schedule('e');
+ it('timesOut_nonZeroTimeout', function() {
+ var count = 0;
+ scheduleWait(function() {
+ count += 1;
+ var ms = count === 2 ? 65 : 5;
+ return promise.delayed(ms).then(function() {
+ return false;
});
- schedule('b');
- scheduleWait(function() { return true; }, 0, 'wait 3');
- schedule('c');
- scheduleWait(function() { return true; }, 0, 'wait 4');
-
- return waitForIdle().then(function() {
- assertFlowHistory(
- 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c',
- '0: wait 4');
+ }, 60, 'counting to 3');
+ return waitForAbort().then(function(e) {
+ switch (count) {
+ case 1:
+ assertFlowHistory('0: counting to 3');
+ break;
+ case 2:
+ assertFlowHistory('0: counting to 3', '1: counting to 3');
+ break;
+ default:
+ fail('unexpected polling count: ' + count);
+ }
+ assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e);
+ assert.ok(
+ /^counting to 3\nWait timed out after \d+ms$/.test(e.message));
+ });
});
- });
- it('requiresConditionToBeAPromiseOrFunction', function() {
- assert.throws(function() {
- flow.wait(1234, 0);
+ it('shouldFailIfConditionReturnsARejectedPromise', function() {
+ scheduleWait(function() {
+ return promise.rejected(new StubError);
+ }, 100, 'returns rejected promise on first pass');
+ return waitForAbort().then(assertIsStubError);
});
- flow.wait(function() { return true;}, 0);
- flow.wait(promise.fulfilled(), 0);
- return waitForIdle();
- });
- it('promiseThatDoesNotResolveBeforeTimeout', function() {
- var d = promise.defer();
- flow.wait(d.promise, 5).then(fail, function(e) {
- assert.ok(
- /Timed out waiting for promise to resolve after \d+ms/
- .test(e.message),
- 'unexpected error message: ' + e.message);
+ it('scheduleWithIntermittentWaits', function() {
+ schedule('a');
+ scheduleWait(function() { return true; }, 0, 'wait 1');
+ schedule('b');
+ scheduleWait(function() { return true; }, 0, 'wait 2');
+ schedule('c');
+ scheduleWait(function() { return true; }, 0, 'wait 3');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3');
+ });
});
- return waitForIdle().then(function() {
- assert.ok('Promise should not be cancelled', d.promise.isPending());
+
+ it('scheduleWithIntermittentAndNestedWaits', function() {
+ schedule('a');
+ scheduleWait(function() { return true; }, 0, 'wait 1').
+ then(function() {
+ schedule('d');
+ scheduleWait(function() { return true; }, 0, 'wait 2');
+ schedule('e');
+ });
+ schedule('b');
+ scheduleWait(function() { return true; }, 0, 'wait 3');
+ schedule('c');
+ scheduleWait(function() { return true; }, 0, 'wait 4');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory(
+ 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c',
+ '0: wait 4');
+ });
});
- });
- it('unboundedWaitOnPromiseResolution', function() {
- var messages = [];
- var d = promise.defer();
- var waitResult = flow.wait(d.promise).then(function(value) {
- messages.push('b');
- assert.equal(1234, value);
+ it('requiresConditionToBeAPromiseOrFunction', function() {
+ assert.throws(function() {
+ flow.wait(1234, 0);
+ });
+ flow.wait(function() { return true;}, 0);
+ flow.wait(promise.fulfilled(), 0);
+ return waitForIdle();
});
- setTimeout(function() {
- messages.push('a');
- }, 5);
-
- timeout(10).then(function() {
- assert.deepEqual(['a'], messages);
- assert.ok(waitResult.isPending());
- d.fulfill(1234);
- return waitResult;
- }).then(function() {
- assert.deepEqual(['a', 'b'], messages);
+
+ it('promiseThatDoesNotResolveBeforeTimeout', function() {
+ var d = promise.defer();
+ flow.wait(d.promise, 5).then(fail, function(e) {
+ assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e);
+ assert.ok(
+ /Timed out waiting for promise to resolve after \d+ms/
+ .test(e.message),
+ 'unexpected error message: ' + e.message);
+ });
+ return waitForIdle();
});
- return waitForIdle();
- });
- });
+ it('unboundedWaitOnPromiseResolution', function() {
+ var messages = [];
+ var d = promise.defer();
+ var waitResult = flow.wait(d.promise).then(function(value) {
+ messages.push('b');
+ assert.equal(1234, value);
+ });
+ setTimeout(function() {
+ messages.push('a');
+ }, 5);
- describe('testSubtasks', function() {
- it('(base case)', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- schedule('c');
- schedule('d');
- });
- schedule('b');
+ timeout(10).then(function() {
+ assert.deepEqual(['a'], messages);
+ d.fulfill(1234);
+ return waitResult;
+ }).then(function() {
+ assert.deepEqual(['a', 'b'], messages);
+ });
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b');
+ return waitForIdle();
});
});
- it('nesting', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- schedule('b');
- scheduleAction('sub-sub-tasks', function() {
+ describe('testSubtasks', function() {
+ it('(base case)', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
schedule('c');
schedule('d');
});
- schedule('e');
- });
- schedule('f');
+ schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory(
- 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b');
+ });
});
- });
- it('taskReturnsSubTaskResult_1', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- return schedule('c');
- });
- schedule('b');
+ it('nesting', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ schedule('b');
+ scheduleAction('sub-sub-tasks', function() {
+ schedule('c');
+ schedule('d');
+ });
+ schedule('e');
+ });
+ schedule('f');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'sub-tasks', 'c', 'b');
+ return waitForIdle().then(function() {
+ assertFlowHistory(
+ 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f');
+ });
});
- });
- it('taskReturnsSubTaskResult_2', function() {
- let pair = callbackPair((value) => assert.equal(123, value));
- schedule('a');
- schedule('sub-tasks', promise.fulfilled(123)).then(pair.callback);
- schedule('b');
+ it('taskReturnsSubTaskResult_1', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return schedule('c');
+ });
+ schedule('b');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'sub-tasks','b');
- pair.assertCallback();
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'c', 'b');
+ });
});
- });
- it('taskReturnsPromiseThatDependsOnSubtask_1', function() {
- scheduleAction('a', function() {
- return promise.delayed(10).then(function() {
- schedule('b');
+ it('taskReturnsSubTaskResult_2', function() {
+ let pair = callbackPair((value) => assert.equal(123, value));
+ schedule('a');
+ schedule('sub-tasks', promise.fulfilled(123)).then(pair.callback);
+ schedule('b');
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'sub-tasks','b');
+ pair.assertCallback();
});
});
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
- it('taskReturnsPromiseThatDependsOnSubtask_2', function() {
- scheduleAction('a', function() {
- return promise.fulfilled().then(function() {
- schedule('b');
+ it('taskReturnsPromiseThatDependsOnSubtask_1', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(10).then(function() {
+ schedule('b');
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
});
});
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
- it('taskReturnsPromiseThatDependsOnSubtask_3', function() {
- scheduleAction('a', function() {
- return promise.delayed(10).then(function() {
- return schedule('b');
+ it('taskReturnsPromiseThatDependsOnSubtask_2', function() {
+ scheduleAction('a', function() {
+ return promise.fulfilled().then(function() {
+ schedule('b');
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
});
});
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
- it('taskReturnsPromiseThatDependsOnSubtask_4', function() {
- scheduleAction('a', function() {
- return promise.delayed(5).then(function() {
- return promise.delayed(5).then(function() {
+ it('taskReturnsPromiseThatDependsOnSubtask_3', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(10).then(function() {
return schedule('b');
});
});
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+
+ it('taskReturnsPromiseThatDependsOnSubtask_4', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(5).then(function() {
+ return promise.delayed(5).then(function() {
+ return schedule('b');
+ });
+ });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- });
- it('taskReturnsPromiseThatDependsOnSubtask_5', function() {
- scheduleAction('a', function() {
- return promise.delayed(5).then(function() {
+ it('taskReturnsPromiseThatDependsOnSubtask_5', function() {
+ scheduleAction('a', function() {
return promise.delayed(5).then(function() {
return promise.delayed(5).then(function() {
return promise.delayed(5).then(function() {
- return schedule('b');
+ return promise.delayed(5).then(function() {
+ return schedule('b');
+ });
});
});
});
});
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
- });
- });
- it('taskReturnsPromiseThatDependsOnSubtask_6', function() {
- scheduleAction('a', function() {
- return promise.delayed(5).
- then(function() { return promise.delayed(5) }).
- then(function() { return promise.delayed(5) }).
- then(function() { return promise.delayed(5) }).
- then(function() { return schedule('b'); });
- });
- schedule('c');
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'b', 'c');
+ it('taskReturnsPromiseThatDependsOnSubtask_6', function() {
+ scheduleAction('a', function() {
+ return promise.delayed(5).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return promise.delayed(5) }).
+ then(function() { return schedule('b'); });
+ });
+ schedule('c');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'b', 'c');
+ });
});
- });
- it('subTaskFails_1', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- scheduleAction('sub-task that fails', throwStubError);
+ it('subTaskFails_1', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ scheduleAction('sub-task that fails', throwStubError);
+ });
+ schedule('should never execute');
+
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'sub-task that fails');
+ });
});
- schedule('should never execute');
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'sub-tasks', 'sub-task that fails');
- });
- });
+ it('subTaskFails_2', function() {
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ });
+ schedule('should never execute');
- it('subTaskFails_2', function() {
- schedule('a');
- scheduleAction('sub-tasks', function() {
- return promise.rejected(new StubError);
+ return waitForAbort().
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks');
+ });
});
- schedule('should never execute');
- return waitForAbort().
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('a', 'sub-tasks');
- });
- });
-
- it('subTaskFails_3', function() {
- var callbacks = callbackPair(null, assertIsStubError);
+ it('subTaskFails_3', function() {
+ var callbacks = callbackPair(null, assertIsStubError);
- schedule('a');
- scheduleAction('sub-tasks', function() {
- return promise.rejected(new StubError);
- }).then(callbacks.callback, callbacks.errback);
- schedule('b');
+ schedule('a');
+ scheduleAction('sub-tasks', function() {
+ return promise.rejected(new StubError);
+ }).then(callbacks.callback, callbacks.errback);
+ schedule('b');
- return waitForIdle().
- then(function() {
- assertFlowHistory('a', 'sub-tasks', 'b');
- callbacks.assertErrback();
- });
+ return waitForIdle().
+ then(function() {
+ assertFlowHistory('a', 'sub-tasks', 'b');
+ callbacks.assertErrback();
+ });
+ });
});
- });
- describe('testEventLoopWaitsOnPendingPromiseRejections', function() {
- it('oneRejection', function() {
- var d = new promise.Deferred;
- scheduleAction('one', function() {
- return d.promise;
+ describe('testEventLoopWaitsOnPendingPromiseRejections', function() {
+ it('oneRejection', function() {
+ var d = new promise.Deferred;
+ scheduleAction('one', function() {
+ return d.promise;
+ });
+ scheduleAction('two', function() {});
+
+ return timeout(50).then(function() {
+ assertFlowHistory('one');
+ d.reject(new StubError);
+ return waitForAbort();
+ }).
+ then(assertIsStubError).
+ then(function() {
+ assertFlowHistory('one');
+ });
});
- scheduleAction('two', function() {});
- return timeout(50).then(function() {
- assertFlowHistory('one');
- d.reject(new StubError);
- return waitForAbort();
- }).
- then(assertIsStubError).
- then(function() {
- assertFlowHistory('one');
+ it('multipleRejections', function() {
+ var once = Error('once');
+ var twice = Error('twice');
+
+ scheduleAction('one', function() {
+ promise.rejected(once);
+ promise.rejected(twice);
+ });
+ var twoResult = scheduleAction('two', function() {});
+
+ flow.removeAllListeners(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
+ return new NativePromise(function(fulfill, reject) {
+ setTimeout(function() {
+ reject(Error('Should have reported the two errors by now'));
+ }, 50);
+ flow.on(
+ promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ fulfill);
+ }).then(function(e) {
+ assert.ok(e instanceof promise.MultipleUnhandledRejectionError,
+ 'Not a MultipleUnhandledRejectionError');
+ let errors = Array.from(e.errors);
+ assert.deepEqual([once, twice], errors);
+ assertFlowHistory('one');
+ });
});
});
- it('multipleRejections', function() {
- var once = Error('once');
- var twice = Error('twice');
+ describe('testCancelsPromiseReturnedByCallbackIfFrameFails', function() {
+ it('promiseCallback', function() {
+ var chainPair = callbackPair(null, assertIsStubError);
+ var deferredPair = callbackPair(null, function(e) {
+ assert.equal('CancellationError: StubError', e.toString(),
+ 'callback result should be cancelled');
+ });
- scheduleAction('one', function() {
- promise.rejected(once);
- promise.rejected(twice);
+ var d = new promise.Deferred();
+ d.promise.then(deferredPair.callback, deferredPair.errback);
+
+ promise.fulfilled().
+ then(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return d.promise;
+ }).
+ then(chainPair.callback, chainPair.errback);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('boom');
+ chainPair.assertErrback('chain errback not invoked');
+ deferredPair.assertErrback('deferred errback not invoked');
+ });
});
- var twoResult = scheduleAction('two', function() {});
- flow.removeAllListeners(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
- return new NativePromise(function(fulfill, reject) {
- setTimeout(function() {
- reject(Error('Should have reported the two errors by now'));
- }, 50);
- flow.on(
- promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
- fulfill);
- }).then(function(e) {
- assert.ok(e instanceof promise.MultipleUnhandledRejectionError,
- 'Not a MultipleUnhandledRejectionError');
- let errors = Array.from(e.errors);
- assert.deepEqual([once, twice], errors);
- assertFlowHistory('one');
- assert.ok(!twoResult.isPending(), 'Did not cancel the second task');
- });
- });
- });
+ it('taskCallback', function() {
+ var chainPair = callbackPair(null, assertIsStubError);
+ var deferredPair = callbackPair(null, function(e) {
+ assert.equal('CancellationError: StubError', e.toString(),
+ 'callback result should be cancelled');
+ });
- describe('testCancelsPromiseReturnedByCallbackIfFrameFails', function() {
- it('promiseCallback', function() {
- var chainPair = callbackPair(null, assertIsStubError);
- var deferredPair = callbackPair(null, function(e) {
- assert.equal('CancellationError: StubError', e.toString(),
- 'callback result should be cancelled');
+ var d = new promise.Deferred();
+ d.promise.then(deferredPair.callback, deferredPair.errback);
+
+ schedule('a').
+ then(function() {
+ scheduleAction('boom', throwStubError);
+ schedule('this should not run');
+ return d.promise;
+ }).
+ then(chainPair.callback, chainPair.errback);
+
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'boom');
+ chainPair.assertErrback('chain errback not invoked');
+ deferredPair.assertErrback('deferred errback not invoked');
+ });
});
+ });
- var d = new promise.Deferred();
- d.promise.then(deferredPair.callback, deferredPair.errback);
-
- promise.fulfilled().
+ it('testMaintainsOrderInCallbacksWhenATaskReturnsAPromise', function() {
+ schedule('__start__', promise.fulfilled()).
then(function() {
- scheduleAction('boom', throwStubError);
- schedule('this should not run');
- return d.promise;
+ messages.push('a');
+ schedulePush('b');
+ messages.push('c');
}).
- then(chainPair.callback, chainPair.errback);
+ then(function() {
+ messages.push('d');
+ });
+ schedulePush('e');
return waitForIdle().then(function() {
- assertFlowHistory('boom');
- chainPair.assertErrback('chain errback not invoked');
- deferredPair.assertErrback('deferred errback not invoked');
+ assertFlowHistory('__start__', 'b', 'e');
+ assertMessages('a', 'c', 'b', 'd', 'e');
});
});
- it('taskCallback', function() {
- var chainPair = callbackPair(null, assertIsStubError);
- var deferredPair = callbackPair(null, function(e) {
- assert.equal('CancellationError: StubError', e.toString(),
- 'callback result should be cancelled');
- });
+ it('testOwningFlowIsActivatedForExecutingTasks', function() {
+ var defaultFlow = promise.controlFlow();
+ var order = [];
- var d = new promise.Deferred();
- d.promise.then(deferredPair.callback, deferredPair.errback);
+ promise.createFlow(function(flow) {
+ assertFlowIs(flow);
+ order.push(0);
- schedule('a').
- then(function() {
- scheduleAction('boom', throwStubError);
- schedule('this should not run');
- return d.promise;
- }).
- then(chainPair.callback, chainPair.errback);
+ defaultFlow.execute(function() {
+ assertFlowIs(defaultFlow);
+ order.push(1);
+ });
+ });
return waitForIdle().then(function() {
- assertFlowHistory('a', 'boom');
- chainPair.assertErrback('chain errback not invoked');
- deferredPair.assertErrback('deferred errback not invoked');
+ assertFlowIs(defaultFlow);
+ assert.deepEqual([0, 1], order);
});
});
- });
- it('testMaintainsOrderInCallbacksWhenATaskReturnsAPromise', function() {
- schedule('__start__', promise.fulfilled()).
- then(function() {
- messages.push('a');
- schedulePush('b');
- messages.push('c');
- }).
- then(function() {
- messages.push('d');
+ it('testCreateFlowReturnsPromisePairedWithCreatedFlow', function() {
+ return new NativePromise(function(fulfill, reject) {
+ var newFlow;
+ promise.createFlow(function(flow) {
+ newFlow = flow;
+ assertFlowIs(newFlow);
+ }).then(function() {
+ assertFlowIs(newFlow);
+ waitForIdle(newFlow).then(fulfill, reject);
});
- schedulePush('e');
-
- return waitForIdle().then(function() {
- assertFlowHistory('__start__', 'b', 'e');
- assertMessages('a', 'c', 'b', 'd', 'e');
+ });
});
- });
- it('testOwningFlowIsActivatedForExecutingTasks', function() {
- var defaultFlow = promise.controlFlow();
- var order = [];
-
- promise.createFlow(function(flow) {
- assertFlowIs(flow);
- order.push(0);
-
- defaultFlow.execute(function() {
+ it('testDeferredFactoriesCreateForActiveFlow_defaultFlow', function() {
+ var e = Error();
+ var defaultFlow = promise.controlFlow();
+ promise.fulfilled().then(function() {
assertFlowIs(defaultFlow);
- order.push(1);
});
- });
-
- return waitForIdle().then(function() {
- assertFlowIs(defaultFlow);
- assert.deepEqual([0, 1], order);
- });
- });
-
- it('testCreateFlowReturnsPromisePairedWithCreatedFlow', function() {
- return new NativePromise(function(fulfill, reject) {
- var newFlow;
- promise.createFlow(function(flow) {
- newFlow = flow;
- assertFlowIs(newFlow);
- }).then(function() {
- assertFlowIs(newFlow);
- waitForIdle(newFlow).then(fulfill, reject);
+ promise.rejected(e).then(null, function(err) {
+ assert.equal(e, err);
+ assertFlowIs(defaultFlow);
+ });
+ promise.defer().promise.then(function() {
+ assertFlowIs(defaultFlow);
});
- });
- });
- it('testDeferredFactoriesCreateForActiveFlow_defaultFlow', function() {
- var e = Error();
- var defaultFlow = promise.controlFlow();
- promise.fulfilled().then(function() {
- assertFlowIs(defaultFlow);
- });
- promise.rejected(e).then(null, function(err) {
- assert.equal(e, err);
- assertFlowIs(defaultFlow);
- });
- promise.defer().promise.then(function() {
- assertFlowIs(defaultFlow);
+ return waitForIdle();
});
- return waitForIdle();
- });
+ it('testDeferredFactoriesCreateForActiveFlow_newFlow', function() {
+ var e = Error();
+ var newFlow = new promise.ControlFlow;
+ newFlow.execute(function() {
+ promise.fulfilled().then(function() {
+ assertFlowIs(newFlow);
+ });
- it('testDeferredFactoriesCreateForActiveFlow_newFlow', function() {
- var e = Error();
- var newFlow = new promise.ControlFlow;
- newFlow.execute(function() {
- promise.fulfilled().then(function() {
- assertFlowIs(newFlow);
- });
+ promise.rejected(e).then(null, function(err) {
+ assert.equal(e, err);
+ assertFlowIs(newFlow);
+ });
- promise.rejected(e).then(null, function(err) {
- assert.equal(e, err);
+ let d = promise.defer();
+ d.promise.then(function() {
+ assertFlowIs(newFlow);
+ });
+ d.fulfill();
+ }).then(function() {
assertFlowIs(newFlow);
});
- let d = promise.defer();
- d.promise.then(function() {
- assertFlowIs(newFlow);
- });
- d.fulfill();
- }).then(function() {
- assertFlowIs(newFlow);
+ return waitForIdle(newFlow);
});
- return waitForIdle(newFlow);
- });
-
- it('testFlowsSynchronizeWithThemselvesNotEachOther', function() {
- var defaultFlow = promise.controlFlow();
- schedulePush('a', 'a');
- promise.controlFlow().timeout(250);
- schedulePush('b', 'b');
+ it('testFlowsSynchronizeWithThemselvesNotEachOther', function() {
+ var defaultFlow = promise.controlFlow();
+ schedulePush('a', 'a');
+ promise.controlFlow().timeout(500);
+ schedulePush('b', 'b');
- promise.createFlow(function() {
- schedulePush('c', 'c');
- schedulePush('d', 'd');
- });
+ promise.createFlow(function(flow2) {
+ assertFlowIs(flow2);
+ schedulePush('c', 'c');
+ schedulePush('d', 'd');
+ });
- return waitForIdle().then(function() {
- assertMessages('a', 'c', 'd', 'b');
+ return waitForIdle().then(function() {
+ assertMessages('a', 'c', 'd', 'b');
+ });
});
- });
- it('testUnhandledErrorsAreReportedToTheOwningFlow', function() {
- var error1 = Error('e1');
- var error2 = Error('e2');
+ it('testUnhandledErrorsAreReportedToTheOwningFlow', function() {
+ var error1 = Error('e1');
+ var error2 = Error('e2');
- var defaultFlow = promise.controlFlow();
- defaultFlow.removeAllListeners('uncaughtException');
+ var defaultFlow = promise.controlFlow();
+ defaultFlow.removeAllListeners('uncaughtException');
- var flow1Error = NativePromise.defer();
- flow1Error.promise.then(function(value) {
- assert.equal(error2, value);
- });
+ var flow1Error = defer();
+ flow1Error.promise.then(function(value) {
+ assert.equal(error2, value);
+ });
- var flow2Error = NativePromise.defer();
- flow2Error.promise.then(function(value) {
- assert.equal(error1, value);
- });
+ var flow2Error = defer();
+ flow2Error.promise.then(function(value) {
+ assert.equal(error1, value);
+ });
- promise.createFlow(function(flow) {
- flow.once('uncaughtException', flow2Error.resolve);
- promise.rejected(error1);
+ promise.createFlow(function(flow) {
+ flow.once('uncaughtException', flow2Error.resolve);
+ promise.rejected(error1);
- defaultFlow.once('uncaughtException', flow1Error.resolve);
- defaultFlow.execute(function() {
- promise.rejected(error2);
+ defaultFlow.once('uncaughtException', flow1Error.resolve);
+ defaultFlow.execute(function() {
+ promise.rejected(error2);
+ });
});
- });
- return NativePromise.all([flow1Error.promise, flow2Error.promise]);
- });
+ return NativePromise.all([flow1Error.promise, flow2Error.promise]);
+ });
- it('testCanSynchronizeFlowsByReturningPromiseFromOneToAnother', function() {
- var flow1 = new promise.ControlFlow;
- var flow1Done = NativePromise.defer();
- flow1.once('idle', flow1Done.resolve);
- flow1.once('uncaughtException', flow1Done.reject);
+ it('testCanSynchronizeFlowsByReturningPromiseFromOneToAnother', function() {
+ var flow1 = new promise.ControlFlow;
+ var flow1Done = defer();
+ flow1.once('idle', flow1Done.resolve);
+ flow1.once('uncaughtException', flow1Done.reject);
- var flow2 = new promise.ControlFlow;
- var flow2Done = NativePromise.defer();
- flow2.once('idle', flow2Done.resolve);
- flow2.once('uncaughtException', flow2Done.reject);
+ var flow2 = new promise.ControlFlow;
+ var flow2Done = defer();
+ flow2.once('idle', flow2Done.resolve);
+ flow2.once('uncaughtException', flow2Done.reject);
- flow1.execute(function() {
- schedulePush('a', 'a');
- return promise.delayed(25);
- }, 'start flow 1');
+ flow1.execute(function() {
+ schedulePush('a', 'a');
+ return promise.delayed(25);
+ }, 'start flow 1');
- flow2.execute(function() {
- schedulePush('b', 'b');
- schedulePush('c', 'c');
flow2.execute(function() {
- return flow1.execute(function() {
- schedulePush('d', 'd');
- }, 'flow 1 task');
- }, 'inject flow1 result into flow2');
- schedulePush('e', 'e');
- }, 'start flow 2');
-
- return NativePromise.all([flow1Done.promise, flow2Done.promise]).
- then(function() {
- assertMessages('a', 'b', 'c', 'd', 'e');
- });
- });
+ schedulePush('b', 'b');
+ schedulePush('c', 'c');
+ flow2.execute(function() {
+ return flow1.execute(function() {
+ schedulePush('d', 'd');
+ }, 'flow 1 task');
+ }, 'inject flow1 result into flow2');
+ schedulePush('e', 'e');
+ }, 'start flow 2');
+
+ return NativePromise.all([flow1Done.promise, flow2Done.promise]).
+ then(function() {
+ assertMessages('a', 'b', 'c', 'd', 'e');
+ });
+ });
- it('testFramesWaitToCompleteForPendingRejections', function() {
- return new NativePromise(function(fulfill, reject) {
+ it('testFramesWaitToCompleteForPendingRejections', function() {
+ return new NativePromise(function(fulfill, reject) {
- promise.controlFlow().execute(function() {
- promise.rejected(new StubError);
- }).then(fulfill, reject);
+ promise.controlFlow().execute(function() {
+ promise.rejected(new StubError);
+ }).then(fulfill, reject);
- }).
- then(() => fail('expected to fail'), assertIsStubError);
- });
+ }).
+ then(() => fail('expected to fail'), assertIsStubError);
+ });
- it('testSynchronizeErrorsPropagateToOuterFlow', function() {
- var outerFlow = new promise.ControlFlow;
- var innerFlow = new promise.ControlFlow;
+ it('testSynchronizeErrorsPropagateToOuterFlow', function() {
+ var outerFlow = new promise.ControlFlow;
+ var innerFlow = new promise.ControlFlow;
- var block = NativePromise.defer();
- innerFlow.execute(function() {
- return block.promise;
- }, 'block inner flow');
+ var block = defer();
+ innerFlow.execute(function() {
+ return block.promise;
+ }, 'block inner flow');
- outerFlow.execute(function() {
- block.resolve();
- return innerFlow.execute(function() {
- promise.rejected(new StubError);
- }, 'trigger unhandled rejection error');
- }, 'run test');
+ outerFlow.execute(function() {
+ block.resolve();
+ return innerFlow.execute(function() {
+ promise.rejected(new StubError);
+ }, 'trigger unhandled rejection error');
+ }, 'run test');
- return NativePromise.all([
- waitForIdle(innerFlow),
- waitForAbort(outerFlow).then(assertIsStubError)
- ]);
- });
+ return NativePromise.all([
+ waitForIdle(innerFlow),
+ waitForAbort(outerFlow).then(assertIsStubError)
+ ]);
+ });
- it('testFailsIfErrbackThrows', function() {
- promise.rejected('').then(null, throwStubError);
- return waitForAbort().then(assertIsStubError);
- });
+ it('testFailsIfErrbackThrows', function() {
+ promise.rejected('').then(null, throwStubError);
+ return waitForAbort().then(assertIsStubError);
+ });
- it('testFailsIfCallbackReturnsRejectedPromise', function() {
- promise.fulfilled().then(function() {
- return promise.rejected(new StubError);
+ it('testFailsIfCallbackReturnsRejectedPromise', function() {
+ promise.fulfilled().then(function() {
+ return promise.rejected(new StubError);
+ });
+ return waitForAbort().then(assertIsStubError);
});
- return waitForAbort().then(assertIsStubError);
- });
- it('testAbortsFrameIfTaskFails', function() {
- promise.fulfilled().then(function() {
- promise.controlFlow().execute(throwStubError);
+ it('testAbortsFrameIfTaskFails', function() {
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(throwStubError);
+ });
+ return waitForAbort().then(assertIsStubError);
});
- return waitForAbort().then(assertIsStubError);
- });
- it('testAbortsFramePromisedChainedFromTaskIsNotHandled', function() {
- promise.fulfilled().then(function() {
- promise.controlFlow().execute(function() {}).
- then(throwStubError);
+ it('testAbortsFramePromisedChainedFromTaskIsNotHandled', function() {
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(function() {}).
+ then(throwStubError);
+ });
+ return waitForAbort().then(assertIsStubError);
});
- return waitForAbort().then(assertIsStubError);
- });
- it('testTrapsChainedUnhandledRejectionsWithinAFrame', function() {
- var pair = callbackPair(null, assertIsStubError);
- promise.fulfilled().then(function() {
- promise.controlFlow().execute(function() {}).
- then(throwStubError);
- }).then(pair.callback, pair.errback);
+ it('testTrapsChainedUnhandledRejectionsWithinAFrame', function() {
+ var pair = callbackPair(null, assertIsStubError);
+ promise.fulfilled().then(function() {
+ promise.controlFlow().execute(function() {}).
+ then(throwStubError);
+ }).then(pair.callback, pair.errback);
- return waitForIdle().then(pair.assertErrback);
- });
+ return waitForIdle().then(pair.assertErrback);
+ });
- it('testCancelsRemainingTasksIfFrameThrowsDuringScheduling', function() {
- var task1, task2;
- var pair = callbackPair(null, assertIsStubError);
- var flow = promise.controlFlow();
- flow.execute(function() {
- task1 = flow.execute(function() {});
- task2 = flow.execute(function() {});
- throw new StubError;
- }).then(pair.callback, pair.errback);
-
- return waitForIdle().
- then(pair.assertErrback).
- then(function() {
- assert.ok(!task1.isPending());
- pair = callbackPair();
- return task1.then(pair.callback, pair.errback);
- }).
- then(function() {
- pair.assertErrback();
- assert.ok(!task2.isPending());
- pair = callbackPair();
- return task2.then(pair.callback, pair.errback);
- }).
- then(function() {
- pair.assertErrback();
- });
- });
+ it('testCancelsRemainingTasksIfFrameThrowsDuringScheduling', function() {
+ var task1, task2;
+ var pair = callbackPair(null, assertIsStubError);
+ var flow = promise.controlFlow();
+ flow.execute(function() {
+ task1 = flow.execute(function() {});
+ task2 = flow.execute(function() {});
+ throw new StubError;
+ }).then(pair.callback, pair.errback);
- it('testCancelsRemainingTasksInFrameIfATaskFails', function() {
- var task;
- var pair = callbackPair(null, assertIsStubError);
- var flow = promise.controlFlow();
- flow.execute(function() {
- flow.execute(throwStubError);
- task = flow.execute(function() {});
- }).then(pair.callback, pair.errback);
-
- return waitForIdle().then(pair.assertErrback).then(function() {
- assert.ok(!task.isPending());
- pair = callbackPair();
- task.then(pair.callback, pair.errback);
- }).then(function() {
- pair.assertErrback();
+ return waitForIdle().
+ then(pair.assertErrback).
+ then(function() {
+ pair = callbackPair();
+ return task1.then(pair.callback, pair.errback);
+ }).
+ then(function() {
+ pair.assertErrback();
+ pair = callbackPair();
+ return task2.then(pair.callback, pair.errback);
+ }).
+ then(function() {
+ pair.assertErrback();
+ });
});
- });
- it('testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow', function() {
- var error = Error('original message');
- var originalStack = error.stack;
- var originalStr = error.toString();
+ it('testCancelsRemainingTasksInFrameIfATaskFails', function() {
+ var task;
+ var pair = callbackPair(null, assertIsStubError);
+ var flow = promise.controlFlow();
+ flow.execute(function() {
+ flow.execute(throwStubError);
+ task = flow.execute(function() {});
+ }).then(pair.callback, pair.errback);
- var pair = callbackPair(null, function(e) {
- assert.equal(error, e);
- assert.equal('original message', e.message);
- assert.equal(originalStack, e.stack);
- assert.equal(originalStr, e.toString());
+ return waitForIdle().then(pair.assertErrback).then(function() {
+ pair = callbackPair();
+ task.then(pair.callback, pair.errback);
+ }).then(function() {
+ pair.assertErrback();
+ });
});
- promise.rejected(error).then(pair.callback, pair.errback);
- return waitForIdle().then(pair.assertErrback);
- });
+ it('testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow', function() {
+ var error = Error('original message');
+ var originalStack = error.stack;
+ var originalStr = error.toString();
- /** See https://github.com/SeleniumHQ/selenium/issues/444 */
- it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1', function() {
- var messages = [];
- flow.execute(function() {
- return promise.fulfilled(['a', 'b', 'c', 'd']);
- }, 'start').then(function(steps) {
- steps.forEach(function(step) {
- promise.fulfilled(step)
- .then(function() {
- messages.push(step + '.1');
- }).then(function() {
- messages.push(step + '.2');
- });
- })
- });
- return waitForIdle().then(function() {
- assert.deepEqual(
- ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
- messages);
+ var pair = callbackPair(null, function(e) {
+ assert.equal(error, e);
+ assert.equal('original message', e.message);
+ assert.equal(originalStack, e.stack);
+ assert.equal(originalStr, e.toString());
+ });
+
+ promise.rejected(error).then(pair.callback, pair.errback);
+ return waitForIdle().then(pair.assertErrback);
});
- });
- /** See https://github.com/SeleniumHQ/selenium/issues/444 */
- it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2', function() {
- var messages = [];
- flow.execute(function() {
- return promise.fulfilled(['a', 'b', 'c', 'd']);
- }, 'start').then(function(steps) {
- steps.forEach(function(step) {
- promise.fulfilled(step)
- .then(function() {
- messages.push(step + '.1');
- }).then(function() {
- flow.execute(function() {}, step + '.2').then(function() {
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function() {
+ messages.push(step + '.1');
+ }).then(function() {
messages.push(step + '.2');
});
- });
- })
- });
- return waitForIdle().then(function() {
- assert.deepEqual(
- ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
- messages);
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
});
- });
- /** See https://github.com/SeleniumHQ/selenium/issues/444 */
- it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3', function() {
- var messages = [];
- flow.execute(function() {
- return promise.fulfilled(['a', 'b', 'c', 'd']);
- }, 'start').then(function(steps) {
- steps.forEach(function(step) {
- promise.fulfilled(step)
- .then(function(){})
- .then(function() {
- messages.push(step + '.1');
- return flow.execute(function() {}, step + '.1');
- }).then(function() {
- flow.execute(function() {}, step + '.2').then(function(text) {
- messages.push(step + '.2');
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function() {
+ messages.push(step + '.1');
+ }).then(function() {
+ flow.execute(function() {}, step + '.2').then(function() {
+ messages.push(step + '.2');
+ });
});
- });
- })
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
});
- return waitForIdle().then(function() {
- assert.deepEqual(
- ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
- messages);
+
+ /** See https://github.com/SeleniumHQ/selenium/issues/444 */
+ it('testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3', function() {
+ var messages = [];
+ flow.execute(function() {
+ return promise.fulfilled(['a', 'b', 'c', 'd']);
+ }, 'start').then(function(steps) {
+ steps.forEach(function(step) {
+ promise.fulfilled(step)
+ .then(function(){})
+ .then(function() {
+ messages.push(step + '.1');
+ return flow.execute(function() {}, step + '.1');
+ }).then(function() {
+ flow.execute(function() {}, step + '.2').then(function(text) {
+ messages.push(step + '.2');
+ });
+ });
+ })
+ });
+ return waitForIdle().then(function() {
+ assert.deepEqual(
+ ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'],
+ messages);
+ });
});
- });
- /** See https://github.com/SeleniumHQ/selenium/issues/363 */
- it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
- scheduleAction('a', () => promise.delayed(10));
- schedule('b');
- setTimeout(() => schedule('c'), 0);
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
+ scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ setTimeout(() => schedule('c'), 0);
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'c', 'b');
+ });
});
- });
- /** See https://github.com/SeleniumHQ/selenium/issues/363 */
- it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
- scheduleAction('a', () => promise.delayed(10));
- schedule('b');
- schedule('c');
- setTimeout(function() {
- schedule('d');
- scheduleAction('e', () => promise.delayed(10));
- schedule('f');
- }, 0);
-
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f');
- });
- });
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2', function() {
+ scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ schedule('c');
+ setTimeout(function() {
+ schedule('d');
+ scheduleAction('e', () => promise.delayed(10));
+ schedule('f');
+ }, 0);
- /** See https://github.com/SeleniumHQ/selenium/issues/363 */
- it('testCanSynchronizeTasksFromAdjacentTaskQueues', function() {
- var task1 = scheduleAction('a', () => promise.delayed(10));
- schedule('b');
- setTimeout(function() {
- scheduleAction('c', () => task1);
- schedule('d');
- }, 0);
-
- return waitForIdle().then(function() {
- assertFlowHistory('a', 'c', 'd', 'b');
+ return waitForIdle().then(function() {
+ assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f');
+ });
});
- });
- describe('testCancellingAScheduledTask', function() {
- it('1', function() {
- var called = false;
- var task1 = scheduleAction('a', () => called = true);
- task1.cancel('no soup for you');
+ /** See https://github.com/SeleniumHQ/selenium/issues/363 */
+ it('testCanSynchronizeTasksFromAdjacentTaskQueues', function() {
+ var task1 = scheduleAction('a', () => promise.delayed(10));
+ schedule('b');
+ setTimeout(function() {
+ scheduleAction('c', () => task1);
+ schedule('d');
+ }, 0);
return waitForIdle().then(function() {
- assert.ok(!called);
- assertFlowHistory();
- return task1.catch(function(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('no soup for you', e.message);
- });
+ assertFlowHistory('a', 'c', 'd', 'b');
});
});
- it('2', function() {
- schedule('a');
- var called = false;
- var task2 = scheduleAction('b', () => called = true);
- schedule('c');
+ describe('testCancellingAScheduledTask', function() {
+ it('1', function() {
+ var called = false;
+ var task1 = scheduleAction('a', () => called = true);
+ task1.cancel('no soup for you');
- task2.cancel('no soup for you');
-
- return waitForIdle().then(function() {
- assert.ok(!called);
- assertFlowHistory('a', 'c');
- return task2.catch(function(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('no soup for you', e.message);
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory();
+ return task1.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
});
});
- });
- it('3', function() {
- var called = false;
- var task = scheduleAction('a', () => called = true);
- task.cancel(new StubError);
+ it('2', function() {
+ schedule('a');
+ var called = false;
+ var task2 = scheduleAction('b', () => called = true);
+ schedule('c');
- return waitForIdle().then(function() {
- assert.ok(!called);
- assertFlowHistory();
- return task.catch(function(e) {
- assert.ok(e instanceof promise.CancellationError);
+ task2.cancel('no soup for you');
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory('a', 'c');
+ return task2.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
});
});
- });
- it('4', function() {
- var seen = [];
- var task = scheduleAction('a', () => seen.push(1))
- .then(() => seen.push(2))
- .then(() => seen.push(3))
- .then(() => seen.push(4))
- .then(() => seen.push(5));
- task.cancel(new StubError);
+ it('3', function() {
+ var called = false;
+ var task = scheduleAction('a', () => called = true);
+ task.cancel(new StubError);
- return waitForIdle().then(function() {
- assert.deepEqual([], seen);
- assertFlowHistory();
- return task.catch(function(e) {
- assert.ok(e instanceof promise.CancellationError);
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory();
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
});
});
- });
- it('fromWithinAnExecutingTask', function() {
- var called = false;
- var task;
- scheduleAction('a', function() {
- task.cancel('no soup for you');
+ it('4', function() {
+ var seen = [];
+ var task = scheduleAction('a', () => seen.push(1))
+ .then(() => seen.push(2))
+ .then(() => seen.push(3))
+ .then(() => seen.push(4))
+ .then(() => seen.push(5));
+ task.cancel(new StubError);
+
+ return waitForIdle().then(function() {
+ assert.deepEqual([], seen);
+ assertFlowHistory();
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ });
+ });
});
- task = scheduleAction('b', () => called = true);
- schedule('c');
- return waitForIdle().then(function() {
- assert.ok(!called);
- assertFlowHistory('a', 'c');
- return task.catch(function(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('no soup for you', e.message);
+ it('fromWithinAnExecutingTask', function() {
+ var called = false;
+ var task;
+ scheduleAction('a', function() {
+ task.cancel('no soup for you');
+ });
+ task = scheduleAction('b', () => called = true);
+ schedule('c');
+
+ return waitForIdle().then(function() {
+ assert.ok(!called);
+ assertFlowHistory('a', 'c');
+ return task.catch(function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
});
});
});
- });
- it('testCancellingAPendingTask', function() {
- var order = [];
- var unresolved = promise.defer();
+ it('testCancellingAPendingTask', function() {
+ var order = [];
+ var unresolved = promise.defer();
- var innerTask;
- var outerTask = scheduleAction('a', function() {
- order.push(1);
+ var innerTask;
+ var outerTask = scheduleAction('a', function() {
+ order.push(1);
- // Schedule a task that will never finish.
- innerTask = scheduleAction('a.1', function() {
- return unresolved.promise;
- });
+ // Schedule a task that will never finish.
+ innerTask = scheduleAction('a.1', function() {
+ return unresolved.promise;
+ });
- // Since the outerTask is cancelled below, innerTask should be cancelled
- // with a DiscardedTaskError, which means its callbacks are silently
- // dropped - so this should never execute.
- innerTask.catch(function(e) {
- order.push(2);
+ // Since the outerTask is cancelled below, innerTask should be cancelled
+ // with a DiscardedTaskError, which means its callbacks are silently
+ // dropped - so this should never execute.
+ innerTask.catch(function(e) {
+ order.push(2);
+ });
});
- });
- schedule('b');
+ schedule('b');
- outerTask.catch(function(e) {
- order.push(3);
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('no soup for you', e.message);
- });
+ outerTask.catch(function(e) {
+ order.push(3);
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
- unresolved.promise.catch(function(e) {
- order.push(4);
- assert.ok(e instanceof promise.CancellationError);
- });
+ unresolved.promise.catch(function(e) {
+ order.push(4);
+ assert.ok(e instanceof promise.CancellationError);
+ });
- return timeout(10).then(function() {
- assert.deepEqual([1], order);
- assert.ok(unresolved.promise.isPending());
+ return timeout(10).then(function() {
+ assert.deepEqual([1], order);
- outerTask.cancel('no soup for you');
- return waitForIdle();
- }).then(function() {
- assertFlowHistory('a', 'a.1', 'b');
- assert.deepEqual([1, 3, 4], order);
+ outerTask.cancel('no soup for you');
+ return waitForIdle();
+ }).then(function() {
+ assertFlowHistory('a', 'a.1', 'b');
+ assert.deepEqual([1, 3, 4], order);
+ });
});
- });
- it('testCancellingAPendingPromiseCallback', function() {
- var called = false;
+ it('testCancellingAPendingPromiseCallback', function() {
+ var called = false;
- var root = promise.fulfilled();
- root.then(function() {
- cb2.cancel('no soup for you');
- });
+ var root = promise.fulfilled();
+ root.then(function() {
+ cb2.cancel('no soup for you');
+ });
- var cb2 = root.then(fail, fail); // These callbacks should never be called.
- cb2.then(fail, function(e) {
- called = true;
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('no soup for you', e.message);
- });
+ var cb2 = root.then(fail, fail); // These callbacks should never be called.
+ cb2.then(fail, function(e) {
+ called = true;
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('no soup for you', e.message);
+ });
- return waitForIdle().then(function() {
- assert.ok(called);
+ return waitForIdle().then(function() {
+ assert.ok(called);
+ });
});
- });
- describe('testResetFlow', function() {
- it('1', function() {
- var called = 0;
- var task = flow.execute(() => called++);
- task.finally(() => called++);
+ describe('testResetFlow', function() {
+ it('1', function() {
+ var called = 0;
+ var task = flow.execute(() => called++);
+ task.finally(() => called++);
- return new Promise(function(fulfill) {
- flow.once('reset', fulfill);
- flow.reset();
+ return new Promise(function(fulfill) {
+ flow.once('reset', fulfill);
+ flow.reset();
- }).then(function() {
- assert.equal(0, called);
- assert.ok(!task.isPending());
- return task;
+ }).then(function() {
+ assert.equal(0, called);
+ return task;
- }).then(fail, function(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('ControlFlow was reset', e.message);
+ }).then(fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('ControlFlow was reset', e.message);
+ });
});
- });
- it('2', function() {
- var called = 0;
- var task1 = flow.execute(() => called++);
- task1.finally(() => called++);
+ it('2', function() {
+ var called = 0;
+ var task1 = flow.execute(() => called++);
+ task1.finally(() => called++);
- var task2 = flow.execute(() => called++);
- task2.finally(() => called++);
+ var task2 = flow.execute(() => called++);
+ task2.finally(() => called++);
- var task3 = flow.execute(() => called++);
- task3.finally(() => called++);
+ var task3 = flow.execute(() => called++);
+ task3.finally(() => called++);
- return new Promise(function(fulfill) {
- flow.once('reset', fulfill);
- flow.reset();
+ return new Promise(function(fulfill) {
+ flow.once('reset', fulfill);
+ flow.reset();
- }).then(function() {
- assert.equal(0, called);
- assert.ok(!task1.isPending());
- assert.ok(!task2.isPending());
- assert.ok(!task3.isPending());
+ }).then(function() {
+ assert.equal(0, called);
+ });
});
});
- });
- describe('testPromiseFulfilledInsideTask', function() {
- it('1', function() {
- var order = [];
+ describe('testPromiseFulfilledInsideTask', function() {
+ it('1', function() {
+ var order = [];
- flow.execute(function() {
- var d = promise.defer();
+ flow.execute(function() {
+ var d = promise.defer();
- d.promise.then(() => order.push('a'));
- d.promise.then(() => order.push('b'));
- d.promise.then(() => order.push('c'));
- d.fulfill();
+ d.promise.then(() => order.push('a'));
+ d.promise.then(() => order.push('b'));
+ d.promise.then(() => order.push('c'));
+ d.fulfill();
- flow.execute(() => order.push('d'));
+ flow.execute(() => order.push('d'));
- }).then(() => order.push('fin'));
+ }).then(() => order.push('fin'));
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
+ });
});
- });
- it('2', function() {
- var order = [];
+ it('2', function() {
+ var order = [];
- flow.execute(function() {
- flow.execute(() => order.push('a'));
- flow.execute(() => order.push('b'));
+ flow.execute(function() {
+ flow.execute(() => order.push('a'));
+ flow.execute(() => order.push('b'));
+
+ var d = promise.defer();
+ d.promise.then(() => order.push('c'));
+ d.promise.then(() => order.push('d'));
+ d.fulfill();
+ flow.execute(() => order.push('e'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'fin'], order);
+ });
+ });
+
+ it('3', function() {
+ var order = [];
var d = promise.defer();
d.promise.then(() => order.push('c'));
d.promise.then(() => order.push('d'));
- d.fulfill();
- flow.execute(() => order.push('e'));
+ flow.execute(function() {
+ flow.execute(() => order.push('a'));
+ flow.execute(() => order.push('b'));
- }).then(() => order.push('fin'));
+ d.promise.then(() => order.push('e'));
+ d.fulfill();
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'fin'], order);
+ flow.execute(() => order.push('f'));
+
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order);
+ });
});
- });
- it('3', function() {
- var order = [];
- var d = promise.defer();
- d.promise.then(() => order.push('c'));
- d.promise.then(() => order.push('d'));
+ it('4', function() {
+ var order = [];
+ var d = promise.defer();
+ d.promise.then(() => order.push('a'));
+ d.promise.then(() => order.push('b'));
- flow.execute(function() {
- flow.execute(() => order.push('a'));
- flow.execute(() => order.push('b'));
+ flow.execute(function() {
+ flow.execute(function() {
+ order.push('c');
+ flow.execute(() => order.push('d'));
+ d.promise.then(() => order.push('e'));
+ });
+ flow.execute(() => order.push('f'));
- d.promise.then(() => order.push('e'));
- d.fulfill();
+ d.promise.then(() => order.push('g'));
+ d.fulfill();
- flow.execute(() => order.push('f'));
+ flow.execute(() => order.push('h'));
- }).then(() => order.push('fin'));
+ }).then(() => order.push('fin'));
- return waitForIdle().then(function() {
- assert.deepEqual(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order);
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order);
+ });
});
});
- it('4', function() {
- var order = [];
- var d = promise.defer();
- d.promise.then(() => order.push('a'));
- d.promise.then(() => order.push('b'));
+ describe('testSettledPromiseCallbacksInsideATask', function() {
+ it('1', function() {
+ var order = [];
+ var p = promise.fulfilled();
- flow.execute(function() {
flow.execute(function() {
- order.push('c');
- flow.execute(() => order.push('d'));
- d.promise.then(() => order.push('e'));
+ flow.execute(() => order.push('a'));
+ p.then(() => order.push('b'));
+ flow.execute(() => order.push('c'));
+ p.then(() => order.push('d'));
+ }).then(() => order.push('fin'));
+
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
});
- flow.execute(() => order.push('f'));
-
- d.promise.then(() => order.push('g'));
- d.fulfill();
+ });
- flow.execute(() => order.push('h'));
+ it('2', function() {
+ var order = [];
- }).then(() => order.push('fin'));
+ flow.execute(function() {
+ flow.execute(() => order.push('a'))
+ .then( () => order.push('c'));
+ flow.execute(() => order.push('b'));
+ }).then(() => order.push('fin'));
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order);
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'c', 'b', 'fin'], order);
+ });
});
});
- });
- describe('testSettledPromiseCallbacksInsideATask', function() {
- it('1', function() {
+ it('testTasksDoNotWaitForNewlyCreatedPromises', function() {
var order = [];
- var p = promise.fulfilled();
flow.execute(function() {
- flow.execute(() => order.push('a'));
- p.then(() => order.push('b'));
- flow.execute(() => order.push('c'));
- p.then(() => order.push('d'));
- }).then(() => order.push('fin'));
-
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
- });
- });
+ var d = promise.defer();
- it('2', function() {
- var order = [];
+ // This is a normal promise, not a task, so the task for this callback is
+ // considered volatile. Volatile tasks should be skipped when they reach
+ // the front of the task queue.
+ d.promise.then(() => order.push('a'));
- flow.execute(function() {
- flow.execute(() => order.push('a'))
- .then( () => order.push('c'));
flow.execute(() => order.push('b'));
+ flow.execute(function() {
+ flow.execute(() => order.push('c'));
+ d.promise.then(() => order.push('d'));
+ d.fulfill();
+ });
+ flow.execute(() => order.push('e'));
+
}).then(() => order.push('fin'));
return waitForIdle().then(function() {
- assert.deepEqual(['a', 'c', 'b', 'fin'], order);
+ assert.deepEqual(['b', 'a', 'c', 'd', 'e', 'fin'], order);
});
});
- });
-
- it('testTasksDoNotWaitForNewlyCreatedPromises', function() {
- var order = [];
- flow.execute(function() {
- var d = promise.defer();
-
- // This is a normal promise, not a task, so the task for this callback is
- // considered volatile. Volatile tasks should be skipped when they reach
- // the front of the task queue.
- d.promise.then(() => order.push('a'));
-
- flow.execute(() => order.push('b'));
- flow.execute(function() {
- flow.execute(() => order.push('c'));
- d.promise.then(() => order.push('d'));
- d.fulfill();
+ it('testCallbackDependenciesDoNotDeadlock', function() {
+ var order = [];
+ var root = promise.defer();
+ var dep = promise.fulfilled().then(function() {
+ order.push('a');
+ return root.promise.then(function() {
+ order.push('b');
+ });
});
- flow.execute(() => order.push('e'));
-
- }).then(() => order.push('fin'));
+ // This callback depends on |dep|, which depends on another callback
+ // attached to |root| via a chain.
+ root.promise.then(function() {
+ order.push('c');
+ return dep.then(() => order.push('d'));
+ }).then(() => order.push('fin'));
- return waitForIdle().then(function() {
- assert.deepEqual(['b', 'a', 'c', 'd', 'e', 'fin'], order);
- });
- });
+ setTimeout(() => root.fulfill(), 20);
- it('testCallbackDependenciesDoNotDeadlock', function() {
- var order = [];
- var root = promise.defer();
- var dep = promise.fulfilled().then(function() {
- order.push('a');
- return root.promise.then(function() {
- order.push('b');
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
});
});
- // This callback depends on |dep|, which depends on another callback
- // attached to |root| via a chain.
- root.promise.then(function() {
- order.push('c');
- return dep.then(() => order.push('d'));
- }).then(() => order.push('fin'));
-
- setTimeout(() => root.fulfill(), 20);
-
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c', 'd', 'fin'], order);
- });
});
});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_generator_test.js b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js
index 5fdff9f80..b3388da78 100644
--- a/node_modules/selenium-webdriver/test/lib/promise_generator_test.js
+++ b/node_modules/selenium-webdriver/test/lib/promise_generator_test.js
@@ -19,237 +19,165 @@
const assert = require('assert');
const promise = require('../../lib/promise');
+const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
describe('promise.consume()', function() {
- it('requires inputs to be generator functions', function() {
- assert.throws(function() {
- promise.consume(function() {});
+ promiseManagerSuite(() => {
+ it('requires inputs to be generator functions', function() {
+ assert.throws(function() {
+ promise.consume(function() {});
+ });
});
- });
- it('handles a basic generator with no yielded promises', function() {
- var values = [];
- return promise.consume(function* () {
- var i = 0;
- while (i < 4) {
- i = yield i + 1;
- values.push(i);
- }
- }).then(function() {
- assert.deepEqual([1, 2, 3, 4], values);
+ it('handles a basic generator with no yielded promises', function() {
+ var values = [];
+ return promise.consume(function* () {
+ var i = 0;
+ while (i < 4) {
+ i = yield i + 1;
+ values.push(i);
+ }
+ }).then(function() {
+ assert.deepEqual([1, 2, 3, 4], values);
+ });
});
- });
- it('handles a promise yielding generator', function() {
- var values = [];
- return promise.consume(function* () {
- var i = 0;
- while (i < 4) {
- // Test that things are actually async here.
- setTimeout(function() {
- values.push(i * 2);
- }, 10);
-
- yield promise.delayed(10).then(function() {
- values.push(i++);
- });
- }
- }).then(function() {
- assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values);
+ it('handles a promise yielding generator', function() {
+ var values = [];
+ return promise.consume(function* () {
+ var i = 0;
+ while (i < 4) {
+ // Test that things are actually async here.
+ setTimeout(function() {
+ values.push(i * 2);
+ }, 10);
+
+ yield promise.delayed(10).then(function() {
+ values.push(i++);
+ });
+ }
+ }).then(function() {
+ assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values);
+ });
});
- });
- it('assignemnts to yielded promises get fulfilled value', function() {
- return promise.consume(function* () {
- var p = promise.fulfilled(2);
- var x = yield p;
- assert.equal(2, x);
+ it('assignments to yielded promises get fulfilled value', function() {
+ return promise.consume(function* () {
+ let x = yield Promise.resolve(2);
+ assert.equal(2, x);
+ });
});
- });
- it('is possible to cancel promise generators', function() {
- var values = [];
- var p = promise.consume(function* () {
- var i = 0;
- while (i < 3) {
- yield promise.delayed(100).then(function() {
- values.push(i++);
- });
- }
- });
- return promise.delayed(75).then(function() {
- p.cancel();
- return p.catch(function() {
- return promise.delayed(300);
+ it('uses final return value as fulfillment value', function() {
+ return promise.consume(function* () {
+ yield 1;
+ yield 2;
+ return 3;
+ }).then(function(value) {
+ assert.equal(3, value);
});
- }).then(function() {
- assert.deepEqual([0], values);
});
- });
- it('uses final return value as fulfillment value', function() {
- return promise.consume(function* () {
- yield 1;
- yield 2;
- return 3;
- }).then(function(value) {
- assert.equal(3, value);
+ it('throws rejected promise errors within the generator', function() {
+ var values = [];
+ return promise.consume(function* () {
+ values.push('a');
+ var e = Error('stub error');
+ try {
+ yield Promise.reject(e);
+ values.push('b');
+ } catch (ex) {
+ assert.equal(e, ex);
+ values.push('c');
+ }
+ values.push('d');
+ }).then(function() {
+ assert.deepEqual(['a', 'c', 'd'], values);
+ });
});
- });
- it('throws rejected promise errors within the generator', function() {
- var values = [];
- return promise.consume(function* () {
- values.push('a');
+ it('aborts the generator if there is an unhandled rejection', function() {
+ var values = [];
var e = Error('stub error');
- try {
+ return promise.consume(function* () {
+ values.push(1);
yield promise.rejected(e);
- values.push('b');
- } catch (ex) {
- assert.equal(e, ex);
- values.push('c');
- }
- values.push('d');
- }).then(function() {
- assert.deepEqual(['a', 'c', 'd'], values);
- });
- });
-
- it('aborts the generator if there is an unhandled rejection', function() {
- var values = [];
- var e = Error('stub error');
- return promise.consume(function* () {
- values.push(1);
- yield promise.rejected(e);
- values.push(2);
- }).catch(function() {
- assert.deepEqual([1], values);
- });
- });
-
- it('yield waits for promises', function() {
- var values = [];
- var d = promise.defer();
-
- setTimeout(function() {
- assert.deepEqual([1], values);
- d.fulfill(2);
- }, 100);
-
- return promise.consume(function* () {
- values.push(1);
- values.push((yield d.promise), 3);
- }).then(function() {
- assert.deepEqual([1, 2, 3], values);
- });
- });
-
- it('accepts custom scopes', function() {
- return promise.consume(function* () {
- return this.name;
- }, {name: 'Bob'}).then(function(value) {
- assert.equal('Bob', value);
+ values.push(2);
+ }).catch(function() {
+ assert.deepEqual([1], values);
+ });
});
- });
- it('accepts initial generator arguments', function() {
- return promise.consume(function* (a, b) {
- assert.equal('red', a);
- assert.equal('apples', b);
- }, null, 'red', 'apples');
- });
-
- it('executes generator within the control flow', function() {
- var promises = [
- promise.defer(),
- promise.defer()
- ];
- var values = [];
-
- setTimeout(function() {
- assert.deepEqual([], values);
- promises[0].fulfill(1);
- }, 100);
-
- setTimeout(function() {
- assert.deepEqual([1], values);
- promises[1].fulfill(2);
- }, 200);
-
- return promise.controlFlow().execute(function* () {
- values.push(yield promises[0].promise);
- values.push(yield promises[1].promise);
- values.push('fin');
- }).then(function() {
- assert.deepEqual([1, 2, 'fin'], values);
- });
- });
-
- it('handles tasks scheduled in generator', function() {
- var flow = promise.controlFlow();
- return flow.execute(function* () {
- var x = yield flow.execute(function() {
- return promise.delayed(10).then(function() {
- return 1;
- });
+ it('yield waits for promises', function() {
+ let values = [];
+ let blocker = promise.delayed(100).then(() => {
+ assert.deepEqual([1], values);
+ return 2;
});
- var y = yield flow.execute(function() {
- return 2;
+ return promise.consume(function* () {
+ values.push(1);
+ values.push(yield blocker, 3);
+ }).then(function() {
+ assert.deepEqual([1, 2, 3], values);
});
+ });
- return x + y;
- }).then(function(value) {
- assert.equal(3, value);
+ it('accepts custom scopes', function() {
+ return promise.consume(function* () {
+ return this.name;
+ }, {name: 'Bob'}).then(function(value) {
+ assert.equal('Bob', value);
+ });
});
- });
- it('blocks the control flow while processing generator', function() {
- var values = [];
- return promise.controlFlow().wait(function* () {
- yield values.push(1);
- values.push(yield promise.delayed(10).then(function() {
- return 2;
- }));
- yield values.push(3);
- return values.length === 6;
- }, 250).then(function() {
- assert.deepEqual([1, 2, 3, 1, 2, 3], values);
+ it('accepts initial generator arguments', function() {
+ return promise.consume(function* (a, b) {
+ assert.equal('red', a);
+ assert.equal('apples', b);
+ }, null, 'red', 'apples');
});
});
- it('ControlFlow.wait() will timeout on long generator', function() {
- var values = [];
- return promise.controlFlow().wait(function* () {
- var i = 0;
- while (i < 3) {
- yield promise.delayed(100).then(function() {
- values.push(i++);
+ enablePromiseManager(() => {
+ it('is possible to cancel promise generators', function() {
+ var values = [];
+ var p = promise.consume(function* () {
+ var i = 0;
+ while (i < 3) {
+ yield promise.delayed(100).then(function() {
+ values.push(i++);
+ });
+ }
+ });
+ return promise.delayed(75).then(function() {
+ p.cancel();
+ return p.catch(function() {
+ return promise.delayed(300);
});
- }
- }, 75).catch(function() {
- assert.deepEqual(
- [0, 1, 2], values, 'Should complete one loop of wait condition');
+ }).then(function() {
+ assert.deepEqual([0], values);
+ });
});
- });
- describe('generators in promise callbacks', function() {
- it('works with no initial value', function() {
+ it('executes generator within the control flow', function() {
var promises = [
- promise.defer(),
- promise.defer()
+ promise.defer(),
+ promise.defer()
];
var values = [];
setTimeout(function() {
+ assert.deepEqual([], values);
promises[0].fulfill(1);
- }, 50);
+ }, 100);
setTimeout(function() {
+ assert.deepEqual([1], values);
promises[1].fulfill(2);
- }, 100);
+ }, 200);
- return promise.fulfilled().then(function*() {
+ return promise.controlFlow().execute(function* () {
values.push(yield promises[0].promise);
values.push(yield promises[1].promise);
values.push('fin');
@@ -258,49 +186,123 @@ describe('promise.consume()', function() {
});
});
- it('starts the generator with promised value', function() {
- var promises = [
- promise.defer(),
- promise.defer()
- ];
- var values = [];
+ it('handles tasks scheduled in generator', function() {
+ var flow = promise.controlFlow();
+ return flow.execute(function* () {
+ var x = yield flow.execute(function() {
+ return promise.delayed(10).then(function() {
+ return 1;
+ });
+ });
- setTimeout(function() {
- promises[0].fulfill(1);
- }, 50);
+ var y = yield flow.execute(function() {
+ return 2;
+ });
- setTimeout(function() {
- promises[1].fulfill(2);
- }, 100);
+ return x + y;
+ }).then(function(value) {
+ assert.equal(3, value);
+ });
+ });
- return promise.fulfilled(3).then(function*(value) {
- var p1 = yield promises[0].promise;
- var p2 = yield promises[1].promise;
- values.push(value + p1);
- values.push(value + p2);
- values.push('fin');
- }).then(function() {
- assert.deepEqual([4, 5, 'fin'], values);
+ it('blocks the control flow while processing generator', function() {
+ var values = [];
+ return promise.controlFlow().wait(function* () {
+ yield values.push(1);
+ values.push(yield promise.delayed(10).then(function() {
+ return 2;
+ }));
+ yield values.push(3);
+ return values.length === 6;
+ }, 250).then(function() {
+ assert.deepEqual([1, 2, 3, 1, 2, 3], values);
});
});
- it('throws yielded rejections within the generator callback', function() {
- var d = promise.defer();
- var e = Error('stub');
+ it('ControlFlow.wait() will timeout on long generator', function() {
+ var values = [];
+ return promise.controlFlow().wait(function* () {
+ var i = 0;
+ while (i < 3) {
+ yield promise.delayed(100).then(function() {
+ values.push(i++);
+ });
+ }
+ }, 75).catch(function() {
+ assert.deepEqual(
+ [0, 1, 2], values, 'Should complete one loop of wait condition');
+ });
+ });
- setTimeout(function() {
- d.reject(e);
- }, 50);
+ describe('generators in promise callbacks', function() {
+ it('works with no initial value', function() {
+ var promises = [
+ promise.defer(),
+ promise.defer()
+ ];
+ var values = [];
- return promise.fulfilled().then(function*() {
- var threw = false;
- try {
- yield d.promise;
- } catch (ex) {
- threw = true;
- assert.equal(e, ex);
- }
- assert.ok(threw);
+ setTimeout(function() {
+ promises[0].fulfill(1);
+ }, 50);
+
+ setTimeout(function() {
+ promises[1].fulfill(2);
+ }, 100);
+
+ return promise.fulfilled().then(function*() {
+ values.push(yield promises[0].promise);
+ values.push(yield promises[1].promise);
+ values.push('fin');
+ }).then(function() {
+ assert.deepEqual([1, 2, 'fin'], values);
+ });
+ });
+
+ it('starts the generator with promised value', function() {
+ var promises = [
+ promise.defer(),
+ promise.defer()
+ ];
+ var values = [];
+
+ setTimeout(function() {
+ promises[0].fulfill(1);
+ }, 50);
+
+ setTimeout(function() {
+ promises[1].fulfill(2);
+ }, 100);
+
+ return promise.fulfilled(3).then(function*(value) {
+ var p1 = yield promises[0].promise;
+ var p2 = yield promises[1].promise;
+ values.push(value + p1);
+ values.push(value + p2);
+ values.push('fin');
+ }).then(function() {
+ assert.deepEqual([4, 5, 'fin'], values);
+ });
+ });
+
+ it('throws yielded rejections within the generator callback', function() {
+ var d = promise.defer();
+ var e = Error('stub');
+
+ setTimeout(function() {
+ d.reject(e);
+ }, 50);
+
+ return promise.fulfilled().then(function*() {
+ var threw = false;
+ try {
+ yield d.promise;
+ } catch (ex) {
+ threw = true;
+ assert.equal(e, ex);
+ }
+ assert.ok(threw);
+ });
});
});
});
diff --git a/node_modules/selenium-webdriver/test/lib/promise_test.js b/node_modules/selenium-webdriver/test/lib/promise_test.js
index 51554ecd9..96d2ccc22 100644
--- a/node_modules/selenium-webdriver/test/lib/promise_test.js
+++ b/node_modules/selenium-webdriver/test/lib/promise_test.js
@@ -21,6 +21,7 @@ const assert = require('assert');
const testutil = require('./testutil');
const promise = require('../../lib/promise');
+const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
// Aliases for readability.
const NativePromise = Promise;
@@ -36,1032 +37,1054 @@ describe('promise', function() {
var app, uncaughtExceptions;
beforeEach(function setUp() {
- promise.LONG_STACK_TRACES = false;
- uncaughtExceptions = [];
+ if (promise.USE_PROMISE_MANAGER) {
+ promise.LONG_STACK_TRACES = false;
+ uncaughtExceptions = [];
- app = promise.controlFlow();
- app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
- (e) => uncaughtExceptions.push(e));
+ app = promise.controlFlow();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
+ (e) => uncaughtExceptions.push(e));
+ }
});
afterEach(function tearDown() {
- app.reset();
- promise.setDefaultFlow(new promise.ControlFlow);
- assert.deepEqual([], uncaughtExceptions,
- 'Did not expect any uncaught exceptions');
- promise.LONG_STACK_TRACES = false;
+ if (promise.USE_PROMISE_MANAGER) {
+ app.reset();
+ promise.setDefaultFlow(new promise.ControlFlow);
+ assert.deepEqual([], uncaughtExceptions,
+ 'Did not expect any uncaught exceptions');
+ promise.LONG_STACK_TRACES = false;
+ }
});
const assertIsPromise = (p) => assert.ok(promise.isPromise(p));
const assertNotPromise = (v) => assert.ok(!promise.isPromise(v));
+ function defer() {
+ let d = {};
+ let promise = new Promise((resolve, reject) => {
+ Object.assign(d, {resolve, reject});
+ });
+ d.promise = promise;
+ return d;
+ }
+
function createRejectedPromise(reason) {
- var p = promise.rejected(reason);
- p.catch(function() {});
+ var p = Promise.reject(reason);
+ p.catch(function() {}); // Silence unhandled rejection handlers.
return p;
}
- it('testCanDetectPromiseLikeObjects', function() {
- assertIsPromise(new promise.Promise(function(fulfill) {
- fulfill();
- }));
- assertIsPromise(new promise.Deferred().promise);
- assertIsPromise({then:function() {}});
-
- assertNotPromise(new promise.Deferred());
- assertNotPromise(undefined);
- assertNotPromise(null);
- assertNotPromise('');
- assertNotPromise(true);
- assertNotPromise(false);
- assertNotPromise(1);
- assertNotPromise({});
- assertNotPromise({then:1});
- assertNotPromise({then:true});
- assertNotPromise({then:''});
- });
-
- describe('then', function() {
- it('returnsOwnPromiseIfNoCallbacksWereGiven', function() {
- var deferred = new promise.Deferred();
- assert.equal(deferred.promise, deferred.promise.then());
- assert.equal(deferred.promise, deferred.promise.catch());
- assert.equal(deferred.promise, promise.when(deferred.promise));
- });
-
- it('stillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen', function() {
- promise.rejected(new StubError).then();
- var handler = callbackHelper(assertIsStubError);
-
- // so tearDown() doesn't throw
- app.removeAllListeners();
- app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
- return NativePromise.resolve()
- // Macro yield so the uncaught exception has a chance to trigger.
- .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
- .then(() => handler.assertCalled());
- });
- });
+ enablePromiseManager(() => {
+ it('testCanDetectPromiseLikeObjects', function() {
+ assertIsPromise(new promise.Promise(function(fulfill) {
+ fulfill();
+ }));
+ assertIsPromise(new promise.Deferred().promise);
+ assertIsPromise(Promise.resolve(123));
+ assertIsPromise({then:function() {}});
+
+ assertNotPromise(new promise.Deferred());
+ assertNotPromise(undefined);
+ assertNotPromise(null);
+ assertNotPromise('');
+ assertNotPromise(true);
+ assertNotPromise(false);
+ assertNotPromise(1);
+ assertNotPromise({});
+ assertNotPromise({then:1});
+ assertNotPromise({then:true});
+ assertNotPromise({then:''});
+ });
+
+ describe('then', function() {
+ it('returnsOwnPromiseIfNoCallbacksWereGiven', function() {
+ var deferred = new promise.Deferred();
+ assert.equal(deferred.promise, deferred.promise.then());
+ assert.equal(deferred.promise, deferred.promise.catch());
+ assert.equal(deferred.promise, promise.when(deferred.promise));
+ });
- describe('finally', function() {
- it('nonFailingCallbackDoesNotSuppressOriginalError', function() {
- var done = callbackHelper(assertIsStubError);
- return promise.rejected(new StubError).
- finally(function() {}).
- catch(done).
- finally(done.assertCalled);
+ it('stillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen', function() {
+ promise.rejected(new StubError).then();
+ var handler = callbackHelper(assertIsStubError);
+
+ // so tearDown() doesn't throw
+ app.removeAllListeners();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
+ return NativePromise.resolve()
+ // Macro yield so the uncaught exception has a chance to trigger.
+ .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
+ .then(() => handler.assertCalled());
+ });
});
- it('failingCallbackSuppressesOriginalError', function() {
- var done = callbackHelper(assertIsStubError);
- return promise.rejected(new Error('original')).
- finally(throwStubError).
- catch(done).
- finally(done.assertCalled);
- });
+ describe('finally', function() {
+ it('nonFailingCallbackDoesNotSuppressOriginalError', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.rejected(new StubError).
+ finally(function() {}).
+ catch(done).
+ finally(done.assertCalled);
+ });
- it('callbackThrowsAfterFulfilledPromise', function() {
- var done = callbackHelper(assertIsStubError);
- return promise.fulfilled().
- finally(throwStubError).
- catch(done).
- finally(done.assertCalled);
- });
+ it('failingCallbackSuppressesOriginalError', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.rejected(new Error('original')).
+ finally(throwStubError).
+ catch(done).
+ finally(done.assertCalled);
+ });
- it('callbackReturnsRejectedPromise', function() {
- var done = callbackHelper(assertIsStubError);
- return promise.fulfilled().
- finally(function() {
- return promise.rejected(new StubError);
- }).
- catch(done).
- finally(done.assertCalled);
- });
- });
+ it('callbackThrowsAfterFulfilledPromise', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.fulfilled().
+ finally(throwStubError).
+ catch(done).
+ finally(done.assertCalled);
+ });
- describe('cancel', function() {
- it('passesTheCancellationReasonToReject', function() {
- var d = new promise.Deferred();
- var res = d.promise.then(assert.fail, function(e) {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('because i said so', e.message);
+ it('callbackReturnsRejectedPromise', function() {
+ var done = callbackHelper(assertIsStubError);
+ return promise.fulfilled().
+ finally(function() {
+ return promise.rejected(new StubError);
+ }).
+ catch(done).
+ finally(done.assertCalled);
});
- d.promise.cancel('because i said so');
- return res;
});
- describe('can cancel original promise from its child;', function() {
- it('child created by then()', function() {
+ describe('cancel', function() {
+ it('passesTheCancellationReasonToReject', function() {
var d = new promise.Deferred();
- var p = d.promise.then(assert.fail, function(e) {
+ var res = d.promise.then(assert.fail, function(e) {
assert.ok(e instanceof promise.CancellationError);
assert.equal('because i said so', e.message);
- return 123;
});
-
- p.cancel('because i said so');
- return p.then(v => assert.equal(123, v));
+ d.promise.cancel('because i said so');
+ return res;
});
- it('child linked by resolving with parent', function() {
- let parent = promise.defer();
- let child = new promise.Promise(resolve => resolve(parent.promise));
- child.cancel('all done');
-
- return parent.promise.then(
- () => assert.fail('expected a cancellation'),
- e => {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('all done', e.message);
- });
- });
-
- it('grand child through thenable chain', function() {
- let p = new promise.Promise(function() {/* never resolve*/});
-
- let noop = function() {};
- let gc = p.then(noop).then(noop).then(noop);
- gc.cancel('stop!');
-
- return p.then(
- () => assert.fail('expected to be cancelled'),
- (e) => {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('stop!', e.message);
- });
- });
-
- it('grand child through thenable chain started at resolve', function() {
- function noop() {}
-
- let parent = promise.defer();
- let child = new promise.Promise(resolve => resolve(parent.promise));
- let grandChild = child.then(noop).then(noop).then(noop);
- grandChild.cancel('all done');
-
- return parent.promise.then(
- () => assert.fail('expected a cancellation'),
- e => {
- assert.ok(e instanceof promise.CancellationError);
- assert.equal('all done', e.message);
- });
- });
-
- it('"parent" is a Thenable', function() {
- function noop() {}
+ describe('can cancel original promise from its child;', function() {
+ it('child created by then()', function() {
+ var d = new promise.Deferred();
+ var p = d.promise.then(assert.fail, function(e) {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('because i said so', e.message);
+ return 123;
+ });
- class FakeThenable {
- constructor(p) {
- this.promise = p;
- }
+ p.cancel('because i said so');
+ return p.then(v => assert.equal(123, v));
+ });
- cancel(reason) {
- this.promise.cancel(reason);
- }
+ it('child linked by resolving with parent', function() {
+ let parent = promise.defer();
+ let child = new promise.Promise(resolve => resolve(parent.promise));
+ child.cancel('all done');
- then(cb, eb) {
- let result = this.promise.then(cb, eb);
- return new FakeThenable(result);
- }
- }
- promise.Thenable.addImplementation(FakeThenable);
+ return parent.promise.then(
+ () => assert.fail('expected a cancellation'),
+ e => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('all done', e.message);
+ });
+ });
- let root = new promise.Promise(noop);
- let thenable = new FakeThenable(root);
- assert.ok(promise.Thenable.isImplementation(thenable));
+ it('grand child through thenable chain', function() {
+ let p = new promise.Promise(function() {/* never resolve*/});
- let child = new promise.Promise(resolve => resolve(thenable));
- assert.ok(child instanceof promise.Promise);
- child.cancel('stop!');
+ let noop = function() {};
+ let gc = p.then(noop).then(noop).then(noop);
+ gc.cancel('stop!');
- function assertStopped(p) {
return p.then(
- () => assert.fail('not stopped!'),
+ () => assert.fail('expected to be cancelled'),
(e) => {
assert.ok(e instanceof promise.CancellationError);
assert.equal('stop!', e.message);
});
- }
+ });
- return assertStopped(child).then(() => assertStopped(root));
- });
- });
+ it('grand child through thenable chain started at resolve', function() {
+ function noop() {}
- it('canCancelATimeout', function() {
- var p = promise.delayed(25)
- .then(assert.fail, (e) => e instanceof promise.CancellationError);
- setTimeout(() => p.cancel(), 20);
- p.cancel();
- return p;
- });
+ let parent = promise.defer();
+ let child = new promise.Promise(resolve => resolve(parent.promise));
+ let grandChild = child.then(noop).then(noop).then(noop);
+ grandChild.cancel('all done');
- it('can cancel timeout from grandchild', function() {
- });
-
- it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() {
- var p = promise.fulfilled(123);
- p.cancel();
- return p.then((v) => assert.equal(123, v));
- });
+ return parent.promise.then(
+ () => assert.fail('expected a cancellation'),
+ e => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('all done', e.message);
+ });
+ });
- it('cancelIsANoopOnceAPromiseHasBeenRejected', function() {
- var p = promise.rejected(new StubError);
- p.cancel();
+ it('"parent" is a CancellableThenable', function() {
+ function noop() {}
- var pair = callbackPair(null, assertIsStubError);
- return p.then(assert.fail, assertIsStubError);
- });
+ class FakeThenable {
+ constructor(p) {
+ this.promise = p;
+ }
- it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() {
- var d = promise.defer();
- var p = d.promise.then();
+ cancel(reason) {
+ this.promise.cancel(reason);
+ }
- d.fulfill();
- p.cancel(); // This should not throw.
- return p; // This should not trigger a failure.
- });
- });
+ then(cb, eb) {
+ let result = this.promise.then(cb, eb);
+ return new FakeThenable(result);
+ }
+ }
+ promise.CancellableThenable.addImplementation(FakeThenable);
+
+ let root = new promise.Promise(noop);
+ let thenable = new FakeThenable(root);
+ assert.ok(promise.Thenable.isImplementation(thenable));
+ assert.ok(promise.CancellableThenable.isImplementation(thenable));
+
+ let child = new promise.Promise(resolve => resolve(thenable));
+ assert.ok(child instanceof promise.Promise);
+ child.cancel('stop!');
+
+ function assertStopped(p) {
+ return p.then(
+ () => assert.fail('not stopped!'),
+ (e) => {
+ assert.ok(e instanceof promise.CancellationError);
+ assert.equal('stop!', e.message);
+ });
+ }
- describe('when', function() {
- it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() {
- var ret = promise.when('abc');
- assertIsPromise(ret);
- return ret.then((value) => assert.equal('abc', value));
- });
+ return assertStopped(child).then(() => assertStopped(root));
+ });
+ });
- it('PassesRawErrorsToCallbacks', function() {
- var error = new Error('boo!');
- return promise.when(error, function(value) {
- assert.equal(error, value);
+ it('canCancelATimeout', function() {
+ var p = promise.delayed(25)
+ .then(assert.fail, (e) => e instanceof promise.CancellationError);
+ setTimeout(() => p.cancel(), 20);
+ p.cancel();
+ return p;
});
- });
- it('WaitsForValueToBeResolvedBeforeInvokingCallback', function() {
- var d = new promise.Deferred(), callback;
- let result = promise.when(d.promise, callback = callbackHelper(function(value) {
- assert.equal('hi', value);
- }));
- callback.assertNotCalled();
- d.fulfill('hi');
- return result.then(callback.assertCalled);
- });
- });
+ it('can cancel timeout from grandchild', function() {
+ });
- it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() {
- promise.rejected(new StubError);
- var handler = callbackHelper(assertIsStubError);
+ it('cancelIsANoopOnceAPromiseHasBeenFulfilled', function() {
+ var p = promise.fulfilled(123);
+ p.cancel();
+ return p.then((v) => assert.equal(123, v));
+ });
- // so tearDown() doesn't throw
- app.removeAllListeners();
- app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
+ it('cancelIsANoopOnceAPromiseHasBeenRejected', function() {
+ var p = promise.rejected(new StubError);
+ p.cancel();
- return NativePromise.resolve()
- // Macro yield so the uncaught exception has a chance to trigger.
- .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
- .then(handler.assertCalled);
- });
+ var pair = callbackPair(null, assertIsStubError);
+ return p.then(assert.fail, assertIsStubError);
+ });
- it('cannotResolveADeferredWithItself', function() {
- var deferred = new promise.Deferred();
- assert.throws(() => deferred.fulfill(deferred));
- assert.throws(() => deferred.reject(deferred));
- });
+ it('noopCancelTriggeredOnCallbackOfResolvedPromise', function() {
+ var d = promise.defer();
+ var p = d.promise.then();
- describe('fullyResolved', function() {
- it('primitives', function() {
- function runTest(value) {
- var callback, errback;
- return promise.fullyResolved(value)
- .then((resolved) => assert.equal(value, resolved));
- }
- return runTest(true)
- .then(() => runTest(function() {}))
- .then(() => runTest(null))
- .then(() => runTest(123))
- .then(() => runTest('foo bar'))
- .then(() => runTest(undefined));
+ d.fulfill();
+ p.cancel(); // This should not throw.
+ return p; // This should not trigger a failure.
+ });
});
+ });
- it('arrayOfPrimitives', function() {
- var fn = function() {};
- var array = [true, fn, null, 123, '', undefined, 1];
- return promise.fullyResolved(array).then(function(resolved) {
- assert.equal(array, resolved);
- assert.deepEqual([true, fn, null, 123, '', undefined, 1],
- resolved);
+ promiseManagerSuite(() => {
+ describe('when', function() {
+ it('ReturnsAResolvedPromiseIfGivenANonPromiseValue', function() {
+ var ret = promise.when('abc');
+ assertIsPromise(ret);
+ return ret.then((value) => assert.equal('abc', value));
});
- });
- it('nestedArrayOfPrimitives', function() {
- var fn = function() {};
- var array = [true, [fn, null, 123], '', undefined];
- return promise.fullyResolved(array)
- .then(function(resolved) {
- assert.equal(array, resolved);
- assert.deepEqual([true, [fn, null, 123], '', undefined], resolved);
- assert.deepEqual([fn, null, 123], resolved[1]);
- });
- });
+ it('PassesRawErrorsToCallbacks', function() {
+ var error = new Error('boo!');
+ return promise.when(error, function(value) {
+ assert.equal(error, value);
+ });
+ });
- it('arrayWithPromisedPrimitive', function() {
- return promise.fullyResolved([promise.fulfilled(123)])
- .then(function(resolved) {
- assert.deepEqual([123], resolved);
- });
+ it('WaitsForValueToBeResolvedBeforeInvokingCallback', function() {
+ let d = defer();
+ let callback;
+ let result = promise.when(d.promise, callback = callbackHelper(function(value) {
+ assert.equal('hi', value);
+ }));
+ callback.assertNotCalled();
+ d.resolve('hi');
+ return result.then(callback.assertCalled);
+ });
});
- it('promiseResolvesToPrimitive', function() {
- return promise.fullyResolved(promise.fulfilled(123))
- .then((resolved) => assert.equal(123, resolved));
- });
+ describe('fullyResolved', function() {
+ it('primitives', function() {
+ function runTest(value) {
+ var callback, errback;
+ return promise.fullyResolved(value)
+ .then((resolved) => assert.equal(value, resolved));
+ }
+ return runTest(true)
+ .then(() => runTest(function() {}))
+ .then(() => runTest(null))
+ .then(() => runTest(123))
+ .then(() => runTest('foo bar'))
+ .then(() => runTest(undefined));
+ });
- it('promiseResolvesToArray', function() {
- var fn = function() {};
- var array = [true, [fn, null, 123], '', undefined];
- var aPromise = promise.fulfilled(array);
+ it('arrayOfPrimitives', function() {
+ var fn = function() {};
+ var array = [true, fn, null, 123, '', undefined, 1];
+ return promise.fullyResolved(array).then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, fn, null, 123, '', undefined, 1],
+ resolved);
+ });
+ });
- var result = promise.fullyResolved(aPromise);
- return result.then(function(resolved) {
- assert.equal(array, resolved);
- assert.deepEqual([true, [fn, null, 123], '', undefined],
- resolved);
- assert.deepEqual([fn, null, 123], resolved[1]);
+ it('nestedArrayOfPrimitives', function() {
+ var fn = function() {};
+ var array = [true, [fn, null, 123], '', undefined];
+ return promise.fullyResolved(array)
+ .then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, [fn, null, 123], '', undefined], resolved);
+ assert.deepEqual([fn, null, 123], resolved[1]);
+ });
});
- });
- it('promiseResolvesToArrayWithPromises', function() {
- var nestedPromise = promise.fulfilled(123);
- var aPromise = promise.fulfilled([true, nestedPromise]);
- return promise.fullyResolved(aPromise)
- .then(function(resolved) {
- assert.deepEqual([true, 123], resolved);
- });
- });
+ it('arrayWithPromisedPrimitive', function() {
+ return promise.fullyResolved([Promise.resolve(123)])
+ .then(function(resolved) {
+ assert.deepEqual([123], resolved);
+ });
+ });
- it('rejectsIfArrayPromiseRejects', function() {
- var nestedPromise = createRejectedPromise(new StubError);
- var aPromise = promise.fulfilled([true, nestedPromise]);
+ it('promiseResolvesToPrimitive', function() {
+ return promise.fullyResolved(Promise.resolve(123))
+ .then((resolved) => assert.equal(123, resolved));
+ });
- var pair = callbackPair(null, assertIsStubError);
- return promise.fullyResolved(aPromise)
- .then(assert.fail, assertIsStubError);
- });
+ it('promiseResolvesToArray', function() {
+ var fn = function() {};
+ var array = [true, [fn, null, 123], '', undefined];
+ var aPromise = Promise.resolve(array);
+
+ var result = promise.fullyResolved(aPromise);
+ return result.then(function(resolved) {
+ assert.equal(array, resolved);
+ assert.deepEqual([true, [fn, null, 123], '', undefined],
+ resolved);
+ assert.deepEqual([fn, null, 123], resolved[1]);
+ });
+ });
- it('rejectsOnFirstArrayRejection', function() {
- var e1 = new Error('foo');
- var e2 = new Error('bar');
- var aPromise = promise.fulfilled([
- createRejectedPromise(e1),
- createRejectedPromise(e2)
- ]);
-
- return promise.fullyResolved(aPromise)
- .then(assert.fail, function(error) {
- assert.strictEqual(e1, error);
- });
- });
+ it('promiseResolvesToArrayWithPromises', function() {
+ var nestedPromise = Promise.resolve(123);
+ var aPromise = Promise.resolve([true, nestedPromise]);
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.deepEqual([true, 123], resolved);
+ });
+ });
- it('rejectsIfNestedArrayPromiseRejects', function() {
- var aPromise = promise.fulfilled([
- promise.fulfilled([
- createRejectedPromise(new StubError)
- ])
- ]);
+ it('rejectsIfArrayPromiseRejects', function() {
+ var nestedPromise = createRejectedPromise(new StubError);
+ var aPromise = Promise.resolve([true, nestedPromise]);
- return promise.fullyResolved(aPromise)
- .then(assert.fail, assertIsStubError);
- });
+ var pair = callbackPair(null, assertIsStubError);
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
- it('simpleHash', function() {
- var hash = {'a': 123};
- return promise.fullyResolved(hash)
- .then(function(resolved) {
- assert.strictEqual(hash, resolved);
- assert.deepEqual(hash, {'a': 123});
- });
- });
+ it('rejectsOnFirstArrayRejection', function() {
+ var e1 = new Error('foo');
+ var e2 = new Error('bar');
+ var aPromise = Promise.resolve([
+ createRejectedPromise(e1),
+ createRejectedPromise(e2)
+ ]);
+
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, function(error) {
+ assert.strictEqual(e1, error);
+ });
+ });
- it('nestedHash', function() {
- var nestedHash = {'foo':'bar'};
- var hash = {'a': 123, 'b': nestedHash};
+ it('rejectsIfNestedArrayPromiseRejects', function() {
+ var aPromise = Promise.resolve([
+ Promise.resolve([
+ createRejectedPromise(new StubError)
+ ])
+ ]);
- return promise.fullyResolved(hash)
- .then(function(resolved) {
- assert.strictEqual(hash, resolved);
- assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved);
- assert.strictEqual(nestedHash, resolved['b']);
- });
- });
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
+ });
- it('promiseResolvesToSimpleHash', function() {
- var hash = {'a': 123};
- var aPromise = promise.fulfilled(hash);
+ it('simpleHash', function() {
+ var hash = {'a': 123};
+ return promise.fullyResolved(hash)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.deepEqual(hash, {'a': 123});
+ });
+ });
- return promise.fullyResolved(aPromise)
- .then((resolved) => assert.strictEqual(hash, resolved));
- });
+ it('nestedHash', function() {
+ var nestedHash = {'foo':'bar'};
+ var hash = {'a': 123, 'b': nestedHash};
- it('promiseResolvesToNestedHash', function() {
- var nestedHash = {'foo':'bar'};
- var hash = {'a': 123, 'b': nestedHash};
- var aPromise = promise.fulfilled(hash);
+ return promise.fullyResolved(hash)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.deepEqual({'a': 123, 'b': {'foo': 'bar'}}, resolved);
+ assert.strictEqual(nestedHash, resolved['b']);
+ });
+ });
- return promise.fullyResolved(aPromise)
- .then(function(resolved) {
- assert.strictEqual(hash, resolved);
- assert.strictEqual(nestedHash, resolved['b']);
- assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}});
- });
- });
+ it('promiseResolvesToSimpleHash', function() {
+ var hash = {'a': 123};
+ var aPromise = Promise.resolve(hash);
- it('promiseResolvesToHashWithPromises', function() {
- var aPromise = promise.fulfilled({
- 'a': promise.fulfilled(123)
+ return promise.fullyResolved(aPromise)
+ .then((resolved) => assert.strictEqual(hash, resolved));
});
- return promise.fullyResolved(aPromise)
- .then(function(resolved) {
- assert.deepEqual({'a': 123}, resolved);
- });
- });
+ it('promiseResolvesToNestedHash', function() {
+ var nestedHash = {'foo':'bar'};
+ var hash = {'a': 123, 'b': nestedHash};
+ var aPromise = Promise.resolve(hash);
- it('rejectsIfHashPromiseRejects', function() {
- var aPromise = promise.fulfilled({
- 'a': createRejectedPromise(new StubError)
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.strictEqual(hash, resolved);
+ assert.strictEqual(nestedHash, resolved['b']);
+ assert.deepEqual(hash, {'a': 123, 'b': {'foo': 'bar'}});
+ });
});
- return promise.fullyResolved(aPromise)
- .then(assert.fail, assertIsStubError);
- });
+ it('promiseResolvesToHashWithPromises', function() {
+ var aPromise = Promise.resolve({
+ 'a': Promise.resolve(123)
+ });
- it('rejectsIfNestedHashPromiseRejects', function() {
- var aPromise = promise.fulfilled({
- 'a': {'b': createRejectedPromise(new StubError)}
+ return promise.fullyResolved(aPromise)
+ .then(function(resolved) {
+ assert.deepEqual({'a': 123}, resolved);
+ });
});
- return promise.fullyResolved(aPromise)
- .then(assert.fail, assertIsStubError);
- });
-
- it('instantiatedObject', function() {
- function Foo() {
- this.bar = 'baz';
- }
- var foo = new Foo;
+ it('rejectsIfHashPromiseRejects', function() {
+ var aPromise = Promise.resolve({
+ 'a': createRejectedPromise(new StubError)
+ });
- return promise.fullyResolved(foo).then(function(resolvedFoo) {
- assert.equal(foo, resolvedFoo);
- assert.ok(resolvedFoo instanceof Foo);
- assert.deepEqual(new Foo, resolvedFoo);
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
});
- });
- it('withEmptyArray', function() {
- return promise.fullyResolved([]).then(function(resolved) {
- assert.deepEqual([], resolved);
- });
- });
+ it('rejectsIfNestedHashPromiseRejects', function() {
+ var aPromise = Promise.resolve({
+ 'a': {'b': createRejectedPromise(new StubError)}
+ });
- it('withEmptyHash', function() {
- return promise.fullyResolved({}).then(function(resolved) {
- assert.deepEqual({}, resolved);
+ return promise.fullyResolved(aPromise)
+ .then(assert.fail, assertIsStubError);
});
- });
- it('arrayWithPromisedHash', function() {
- var obj = {'foo': 'bar'};
- var array = [promise.fulfilled(obj)];
+ it('instantiatedObject', function() {
+ function Foo() {
+ this.bar = 'baz';
+ }
+ var foo = new Foo;
- return promise.fullyResolved(array).then(function(resolved) {
- assert.deepEqual(resolved, [obj]);
+ return promise.fullyResolved(foo).then(function(resolvedFoo) {
+ assert.equal(foo, resolvedFoo);
+ assert.ok(resolvedFoo instanceof Foo);
+ assert.deepEqual(new Foo, resolvedFoo);
+ });
});
- });
- });
- describe('checkedNodeCall', function() {
- it('functionThrows', function() {
- return promise.checkedNodeCall(throwStubError)
- .then(assert.fail, assertIsStubError);
- });
+ it('withEmptyArray', function() {
+ return promise.fullyResolved([]).then(function(resolved) {
+ assert.deepEqual([], resolved);
+ });
+ });
- it('functionReturnsAnError', function() {
- return promise.checkedNodeCall(function(callback) {
- callback(new StubError);
- }).then(assert.fail, assertIsStubError);
- });
+ it('withEmptyHash', function() {
+ return promise.fullyResolved({}).then(function(resolved) {
+ assert.deepEqual({}, resolved);
+ });
+ });
- it('functionReturnsSuccess', function() {
- var success = 'success!';
- return promise.checkedNodeCall(function(callback) {
- callback(null, success);
- }).then((value) => assert.equal(success, value));
- });
+ it('arrayWithPromisedHash', function() {
+ var obj = {'foo': 'bar'};
+ var array = [Promise.resolve(obj)];
- it('functionReturnsAndThrows', function() {
- var error = new Error('boom');
- var error2 = new Error('boom again');
- return promise.checkedNodeCall(function(callback) {
- callback(error);
- throw error2;
- }).then(assert.fail, (e) => assert.equal(error, e));
- });
-
- it('functionThrowsAndReturns', function() {
- var error = new Error('boom');
- var error2 = new Error('boom again');
- return promise.checkedNodeCall(function(callback) {
- setTimeout(() => callback(error), 10);
- throw error2;
- }).then(assert.fail, (e) => assert.equal(error2, e));
+ return promise.fullyResolved(array).then(function(resolved) {
+ assert.deepEqual(resolved, [obj]);
+ });
+ });
});
- });
- describe('all', function() {
- it('(base case)', function() {
- let defer = [promise.defer(), promise.defer()];
- var a = [
- 0, 1,
- defer[0].promise,
- defer[1].promise,
- 4, 5, 6
- ];
- delete a[5];
+ describe('checkedNodeCall', function() {
+ it('functionThrows', function() {
+ return promise.checkedNodeCall(throwStubError)
+ .then(assert.fail, assertIsStubError);
+ });
- var pair = callbackPair(function(value) {
- assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value);
+ it('functionReturnsAnError', function() {
+ return promise.checkedNodeCall(function(callback) {
+ callback(new StubError);
+ }).then(assert.fail, assertIsStubError);
});
- var result = promise.all(a).then(pair.callback, pair.errback);
- pair.assertNeither();
+ it('functionReturnsSuccess', function() {
+ var success = 'success!';
+ return promise.checkedNodeCall(function(callback) {
+ callback(null, success);
+ }).then((value) => assert.equal(success, value));
+ });
- defer[0].fulfill(2);
- pair.assertNeither();
+ it('functionReturnsAndThrows', function() {
+ var error = new Error('boom');
+ var error2 = new Error('boom again');
+ return promise.checkedNodeCall(function(callback) {
+ callback(error);
+ throw error2;
+ }).then(assert.fail, (e) => assert.equal(error, e));
+ });
- defer[1].fulfill(3);
- return result.then(() => pair.assertCallback());
+ it('functionThrowsAndReturns', function() {
+ var error = new Error('boom');
+ var error2 = new Error('boom again');
+ return promise.checkedNodeCall(function(callback) {
+ setTimeout(() => callback(error), 10);
+ throw error2;
+ }).then(assert.fail, (e) => assert.equal(error2, e));
+ });
});
- it('empty array', function() {
- return promise.all([]).then((a) => assert.deepEqual([], a));
- });
+ describe('all', function() {
+ it('(base case)', function() {
+ let deferredObjs = [defer(), defer()];
+ var a = [
+ 0, 1,
+ deferredObjs[0].promise,
+ deferredObjs[1].promise,
+ 4, 5, 6
+ ];
+ delete a[5];
- it('usesFirstRejection', function() {
- let defer = [promise.defer(), promise.defer()];
- let a = [defer[0].promise, defer[1].promise];
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([0, 1, 2, 3, 4, undefined, 6], value);
+ });
- var result = promise.all(a).then(assert.fail, assertIsStubError);
- defer[1].reject(new StubError);
- setTimeout(() => defer[0].reject(Error('ignored')), 0);
- return result;
- });
- });
+ var result = promise.all(a).then(pair.callback, pair.errback);
+ pair.assertNeither();
+
+ deferredObjs[0].resolve(2);
+ pair.assertNeither();
- describe('map', function() {
- it('(base case)', function() {
- var a = [1, 2, 3];
- return promise.map(a, function(value, index, a2) {
- assert.equal(a, a2);
- assert.equal('number', typeof index, 'not a number');
- return value + 1;
- }).then(function(value) {
- assert.deepEqual([2, 3, 4], value);
+ deferredObjs[1].resolve(3);
+ return result.then(() => pair.assertCallback());
});
- });
- it('omitsDeleted', function() {
- var a = [0, 1, 2, 3, 4, 5, 6];
- delete a[1];
- delete a[3];
- delete a[4];
- delete a[6];
+ it('empty array', function() {
+ return promise.all([]).then((a) => assert.deepEqual([], a));
+ });
- var expected = [0, 1, 4, 9, 16, 25, 36];
- delete expected[1];
- delete expected[3];
- delete expected[4];
- delete expected[6];
+ it('usesFirstRejection', function() {
+ let deferredObjs = [defer(), defer()];
+ let a = [deferredObjs[0].promise, deferredObjs[1].promise];
- return promise.map(a, function(value) {
- return value * value;
- }).then(function(value) {
- assert.deepEqual(expected, value);
+ var result = promise.all(a).then(assert.fail, assertIsStubError);
+ deferredObjs[1].reject(new StubError);
+ setTimeout(() => deferredObjs[0].reject(Error('ignored')), 0);
+ return result;
});
});
- it('emptyArray', function() {
- return promise.map([], function(value) {
- return value + 1;
- }).then(function(value) {
- assert.deepEqual([], value);
+ describe('map', function() {
+ it('(base case)', function() {
+ var a = [1, 2, 3];
+ return promise.map(a, function(value, index, a2) {
+ assert.equal(a, a2);
+ assert.equal('number', typeof index, 'not a number');
+ return value + 1;
+ }).then(function(value) {
+ assert.deepEqual([2, 3, 4], value);
+ });
});
- });
- it('inputIsPromise', function() {
- var input = promise.defer();
- var result = promise.map(input.promise, function(value) {
- return value + 1;
+ it('omitsDeleted', function() {
+ var a = [0, 1, 2, 3, 4, 5, 6];
+ delete a[1];
+ delete a[3];
+ delete a[4];
+ delete a[6];
+
+ var expected = [0, 1, 4, 9, 16, 25, 36];
+ delete expected[1];
+ delete expected[3];
+ delete expected[4];
+ delete expected[6];
+
+ return promise.map(a, function(value) {
+ return value * value;
+ }).then(function(value) {
+ assert.deepEqual(expected, value);
+ });
});
- var pair = callbackPair(function(value) {
- assert.deepEqual([2, 3, 4], value);
+ it('emptyArray', function() {
+ return promise.map([], function(value) {
+ return value + 1;
+ }).then(function(value) {
+ assert.deepEqual([], value);
+ });
});
- result = result.then(pair.callback, pair.errback);
- setTimeout(function() {
- pair.assertNeither();
- input.fulfill([1, 2, 3]);
- }, 10);
+ it('inputIsPromise', function() {
+ var input = defer();
+ var result = promise.map(input.promise, function(value) {
+ return value + 1;
+ });
- return result;
- });
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2, 3, 4], value);
+ });
+ result = result.then(pair.callback, pair.errback);
- it('waitsForFunctionResultToResolve', function() {
- var innerResults = [
- promise.defer(),
- promise.defer()
- ];
+ setTimeout(function() {
+ pair.assertNeither();
+ input.resolve([1, 2, 3]);
+ }, 10);
- var result = promise.map([1, 2], function(value, index) {
- return innerResults[index].promise;
+ return result;
});
- var pair = callbackPair(function(value) {
- assert.deepEqual(['a', 'b'], value);
- });
- result = result.then(pair.callback, pair.errback);
+ it('waitsForFunctionResultToResolve', function() {
+ var innerResults = [
+ defer(),
+ defer()
+ ];
- return NativePromise.resolve()
- .then(function() {
- pair.assertNeither();
- innerResults[0].fulfill('a');
- })
- .then(function() {
- pair.assertNeither();
- innerResults[1].fulfill('b');
- return result;
- })
- .then(pair.assertCallback);
- });
-
- it('rejectsPromiseIfFunctionThrows', function() {
- return promise.map([1], throwStubError)
- .then(assert.fail, assertIsStubError);
- });
+ var result = promise.map([1, 2], function(value, index) {
+ return innerResults[index].promise;
+ });
- it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
- return promise.map([1], function() {
- return promise.rejected(new StubError);
- }).then(assert.fail, assertIsStubError);
- });
+ var pair = callbackPair(function(value) {
+ assert.deepEqual(['a', 'b'], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ innerResults[0].resolve('a');
+ })
+ .then(function() {
+ pair.assertNeither();
+ innerResults[1].resolve('b');
+ return result;
+ })
+ .then(pair.assertCallback);
+ });
- it('stopsCallingFunctionIfPreviousIterationFailed', function() {
- var count = 0;
- return promise.map([1, 2, 3, 4], function() {
- count++;
- if (count == 3) {
- throw new StubError;
- }
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.equal(3, count);
+ it('rejectsPromiseIfFunctionThrows', function() {
+ return promise.map([1], throwStubError)
+ .then(assert.fail, assertIsStubError);
});
- });
- it('rejectsWithFirstRejectedPromise', function() {
- var innerResult = [
- promise.fulfilled(),
- createRejectedPromise(new StubError),
- createRejectedPromise(Error('should be ignored'))
- ];
- var count = 0;
- return promise.map([1, 2, 3, 4], function(value, index) {
- count += 1;
- return innerResult[index];
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.equal(2, count);
+ it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
+ return promise.map([1], function() {
+ return createRejectedPromise(new StubError);
+ }).then(assert.fail, assertIsStubError);
});
- });
- it('preservesOrderWhenMapReturnsPromise', function() {
- var deferreds = [
- promise.defer(),
- promise.defer(),
- promise.defer(),
- promise.defer()
- ];
- var result = promise.map(deferreds, function(value) {
- return value.promise;
+ it('stopsCallingFunctionIfPreviousIterationFailed', function() {
+ var count = 0;
+ return promise.map([1, 2, 3, 4], function() {
+ count++;
+ if (count == 3) {
+ throw new StubError;
+ }
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(3, count);
+ });
});
- var pair = callbackPair(function(value) {
- assert.deepEqual([0, 1, 2, 3], value);
+ it('rejectsWithFirstRejectedPromise', function() {
+ var innerResult = [
+ Promise.resolve(),
+ createRejectedPromise(new StubError),
+ createRejectedPromise(Error('should be ignored'))
+ ];
+ var count = 0;
+ return promise.map([1, 2, 3, 4], function(value, index) {
+ count += 1;
+ return innerResult[index];
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(2, count);
+ });
});
- result = result.then(pair.callback, pair.errback);
- return NativePromise.resolve()
- .then(function() {
- pair.assertNeither();
- for (let i = deferreds.length; i > 0; i -= 1) {
- deferreds[i - 1].fulfill(i - 1);
- }
- return result;
- }).then(pair.assertCallback);
- });
- });
+ it('preservesOrderWhenMapReturnsPromise', function() {
+ var deferreds = [
+ defer(),
+ defer(),
+ defer(),
+ defer()
+ ];
+ var result = promise.map(deferreds, function(value) {
+ return value.promise;
+ });
- describe('filter', function() {
- it('basicFiltering', function() {
- var a = [0, 1, 2, 3];
- return promise.filter(a, function(val, index, a2) {
- assert.equal(a, a2);
- assert.equal('number', typeof index, 'not a number');
- return val > 1;
- }).then(function(val) {
- assert.deepEqual([2, 3], val);
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([0, 1, 2, 3], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return Promise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ for (let i = deferreds.length; i > 0; i -= 1) {
+ deferreds[i - 1].resolve(i - 1);
+ }
+ return result;
+ }).then(pair.assertCallback);
});
});
- it('omitsDeleted', function() {
- var a = [0, 1, 2, 3, 4, 5, 6];
- delete a[3];
- delete a[4];
-
- return promise.filter(a, function(value) {
- return value > 1 && value < 6;
- }).then(function(val) {
- assert.deepEqual([2, 5], val);
+ describe('filter', function() {
+ it('basicFiltering', function() {
+ var a = [0, 1, 2, 3];
+ return promise.filter(a, function(val, index, a2) {
+ assert.equal(a, a2);
+ assert.equal('number', typeof index, 'not a number');
+ return val > 1;
+ }).then(function(val) {
+ assert.deepEqual([2, 3], val);
+ });
});
- });
- it('preservesInputs', function() {
- var a = [0, 1, 2, 3];
+ it('omitsDeleted', function() {
+ var a = [0, 1, 2, 3, 4, 5, 6];
+ delete a[3];
+ delete a[4];
- return promise.filter(a, function(value, i, a2) {
- assert.equal(a, a2);
- // Even if a function modifies the input array, the original value
- // should be inserted into the new array.
- a2[i] = a2[i] - 1;
- return a2[i] >= 1;
- }).then(function(val) {
- assert.deepEqual([2, 3], val);
+ return promise.filter(a, function(value) {
+ return value > 1 && value < 6;
+ }).then(function(val) {
+ assert.deepEqual([2, 5], val);
+ });
});
- });
- it('inputIsPromise', function() {
- var input = promise.defer();
- var result = promise.filter(input.promise, function(value) {
- return value > 1 && value < 3;
+ it('preservesInputs', function() {
+ var a = [0, 1, 2, 3];
+
+ return promise.filter(a, function(value, i, a2) {
+ assert.equal(a, a2);
+ // Even if a function modifies the input array, the original value
+ // should be inserted into the new array.
+ a2[i] = a2[i] - 1;
+ return a2[i] >= 1;
+ }).then(function(val) {
+ assert.deepEqual([2, 3], val);
+ });
});
- var pair = callbackPair(function(value) {
- assert.deepEqual([2], value);
+ it('inputIsPromise', function() {
+ var input = defer();
+ var result = promise.filter(input.promise, function(value) {
+ return value > 1 && value < 3;
+ });
+
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ input.resolve([1, 2, 3]);
+ return result;
+ })
+ .then(pair.assertCallback);
});
- result = result.then(pair.callback, pair.errback);
- return NativePromise.resolve()
- .then(function() {
- pair.assertNeither();
- input.fulfill([1, 2, 3]);
- return result;
- })
- .then(pair.assertCallback);
- });
- it('waitsForFunctionResultToResolve', function() {
- var innerResults = [
- promise.defer(),
- promise.defer()
- ];
+ it('waitsForFunctionResultToResolve', function() {
+ var innerResults = [
+ defer(),
+ defer()
+ ];
- var result = promise.filter([1, 2], function(value, index) {
- return innerResults[index].promise;
- });
+ var result = promise.filter([1, 2], function(value, index) {
+ return innerResults[index].promise;
+ });
- var pair = callbackPair(function(value) {
- assert.deepEqual([2], value);
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ innerResults[0].resolve(false);
+ })
+ .then(function() {
+ pair.assertNeither();
+ innerResults[1].resolve(true);
+ return result;
+ })
+ .then(pair.assertCallback);
});
- result = result.then(pair.callback, pair.errback);
- return NativePromise.resolve()
- .then(function() {
- pair.assertNeither();
- innerResults[0].fulfill(false);
- })
- .then(function() {
- pair.assertNeither();
- innerResults[1].fulfill(true);
- return result;
- })
- .then(pair.assertCallback);
- });
- it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
- return promise.filter([1], function() {
- return promise.rejected(new StubError);
- }).then(assert.fail, assertIsStubError);
- });
+ it('rejectsPromiseIfFunctionReturnsRejectedPromise', function() {
+ return promise.filter([1], function() {
+ return createRejectedPromise(new StubError);
+ }).then(assert.fail, assertIsStubError);
+ });
- it('stopsCallingFunctionIfPreviousIterationFailed', function() {
- var count = 0;
- return promise.filter([1, 2, 3, 4], function() {
- count++;
- if (count == 3) {
- throw new StubError;
- }
- }).then(assert.fail, function(e) {
- assertIsStubError(e);
- assert.equal(3, count);
+ it('stopsCallingFunctionIfPreviousIterationFailed', function() {
+ var count = 0;
+ return promise.filter([1, 2, 3, 4], function() {
+ count++;
+ if (count == 3) {
+ throw new StubError;
+ }
+ }).then(assert.fail, function(e) {
+ assertIsStubError(e);
+ assert.equal(3, count);
+ });
});
- });
- it('rejectsWithFirstRejectedPromise', function() {
- var innerResult = [
- promise.fulfilled(),
- createRejectedPromise(new StubError),
- createRejectedPromise(Error('should be ignored'))
- ];
-
- return promise.filter([1, 2, 3, 4], function(value, index) {
- assert.ok(index < innerResult.length);
- return innerResult[index];
- }).then(assert.fail, assertIsStubError);
- });
+ it('rejectsWithFirstRejectedPromise', function() {
+ var innerResult = [
+ Promise.resolve(),
+ createRejectedPromise(new StubError),
+ createRejectedPromise(Error('should be ignored'))
+ ];
- it('preservesOrderWhenFilterReturnsPromise', function() {
- var deferreds = [
- promise.defer(),
- promise.defer(),
- promise.defer(),
- promise.defer()
- ];
- var result = promise.filter([0, 1, 2, 3], function(value, index) {
- return deferreds[index].promise;
+ return promise.filter([1, 2, 3, 4], function(value, index) {
+ assert.ok(index < innerResult.length);
+ return innerResult[index];
+ }).then(assert.fail, assertIsStubError);
});
- var pair = callbackPair(function(value) {
- assert.deepEqual([1, 2], value);
- });
- result = result.then(pair.callback, pair.errback);
+ it('preservesOrderWhenFilterReturnsPromise', function() {
+ var deferreds = [
+ defer(),
+ defer(),
+ defer(),
+ defer()
+ ];
+ var result = promise.filter([0, 1, 2, 3], function(value, index) {
+ return deferreds[index].promise;
+ });
- return NativePromise.resolve()
- .then(function() {
- pair.assertNeither();
- for (let i = deferreds.length - 1; i >= 0; i -= 1) {
- deferreds[i].fulfill(i > 0 && i < 3);
- }
- return result;
- }).then(pair.assertCallback);
+ var pair = callbackPair(function(value) {
+ assert.deepEqual([1, 2], value);
+ });
+ result = result.then(pair.callback, pair.errback);
+
+ return NativePromise.resolve()
+ .then(function() {
+ pair.assertNeither();
+ for (let i = deferreds.length - 1; i >= 0; i -= 1) {
+ deferreds[i].resolve(i > 0 && i < 3);
+ }
+ return result;
+ }).then(pair.assertCallback);
+ });
});
});
- it('testAddThenableImplementation', function() {
- function tmp() {}
- assert.ok(!promise.Thenable.isImplementation(new tmp()));
- promise.Thenable.addImplementation(tmp);
- assert.ok(promise.Thenable.isImplementation(new tmp()));
-
- class tmpClass {}
- assert.ok(!promise.Thenable.isImplementation(new tmpClass()));
- promise.Thenable.addImplementation(tmpClass);
- assert.ok(promise.Thenable.isImplementation(new tmpClass()));
- });
-
- describe('testLongStackTraces', function() {
- beforeEach(() => promise.LONG_STACK_TRACES = false);
- afterEach(() => promise.LONG_STACK_TRACES = false);
+ enablePromiseManager(() => {
+ it('firesUncaughtExceptionEventIfRejectionNeverHandled', function() {
+ promise.rejected(new StubError);
+ var handler = callbackHelper(assertIsStubError);
- it('doesNotAppendStackIfFeatureDisabled', function() {
- promise.LONG_STACK_TRACES = false;
+ // so tearDown() doesn't throw
+ app.removeAllListeners();
+ app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler);
- var error = Error('hello');
- var originalStack = error.stack;
- return promise.rejected(error).
- then(fail).
- then(fail).
- then(fail).
- then(fail, function(e) {
- assert.equal(error, e);
- assert.equal(originalStack, e.stack);
- });
+ return NativePromise.resolve()
+ // Macro yield so the uncaught exception has a chance to trigger.
+ .then(() => new NativePromise(resolve => setTimeout(resolve, 0)))
+ .then(handler.assertCalled);
});
- function getStackMessages(error) {
- return error.stack.split(/\n/).filter(function(line) {
- return /^From: /.test(line);
+ it('cannotResolveADeferredWithItself', function() {
+ var deferred = new promise.Deferred();
+ assert.throws(() => deferred.fulfill(deferred));
+ assert.throws(() => deferred.reject(deferred));
+ });
+
+ describe('testLongStackTraces', function() {
+ beforeEach(() => promise.LONG_STACK_TRACES = false);
+ afterEach(() => promise.LONG_STACK_TRACES = false);
+
+ it('doesNotAppendStackIfFeatureDisabled', function() {
+ promise.LONG_STACK_TRACES = false;
+
+ var error = Error('hello');
+ var originalStack = error.stack;
+ return promise.rejected(error).
+ then(fail).
+ then(fail).
+ then(fail).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ assert.equal(originalStack, e.stack);
+ });
});
- }
- it('appendsInitialPromiseCreation_resolverThrows', function() {
- promise.LONG_STACK_TRACES = true;
+ function getStackMessages(error) {
+ return error.stack.split(/\n/).filter(function(line) {
+ return /^From: /.test(line);
+ });
+ }
- var error = Error('hello');
- var originalStack = '(placeholder; will be overwritten later)';
+ it('appendsInitialPromiseCreation_resolverThrows', function() {
+ promise.LONG_STACK_TRACES = true;
- return new promise.Promise(function() {
- try {
- throw error;
- } catch (e) {
- originalStack = e.stack;
- throw e;
- }
- }).then(fail, function(e) {
- assert.strictEqual(error, e);
- if (typeof originalStack !== 'string') {
- return;
- }
- assert.notEqual(originalStack, e.stack);
- assert.equal(e.stack.indexOf(originalStack), 0,
- 'should start with original stack');
- assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
+
+ return new promise.Promise(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).then(fail, function(e) {
+ assert.strictEqual(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ });
});
- });
- it('appendsInitialPromiseCreation_rejectCalled', function() {
- promise.LONG_STACK_TRACES = true;
+ it('appendsInitialPromiseCreation_rejectCalled', function() {
+ promise.LONG_STACK_TRACES = true;
- var error = Error('hello');
- var originalStack = error.stack;
+ var error = Error('hello');
+ var originalStack = error.stack;
- return new promise.Promise(function(_, reject) {
- reject(error);
- }).then(fail, function(e) {
- assert.equal(error, e);
- if (typeof originalStack !== 'string') {
- return;
- }
- assert.notEqual(originalStack, e.stack);
- assert.equal(e.stack.indexOf(originalStack), 0,
- 'should start with original stack');
- assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ return new promise.Promise(function(_, reject) {
+ reject(error);
+ }).then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual(['From: ManagedPromise: new'], getStackMessages(e));
+ });
});
- });
- it('appendsEachStepToRejectionError', function() {
- promise.LONG_STACK_TRACES = true;
+ it('appendsEachStepToRejectionError', function() {
+ promise.LONG_STACK_TRACES = true;
- var error = Error('hello');
- var originalStack = '(placeholder; will be overwritten later)';
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
- return new promise.Promise(function() {
- try {
- throw error;
- } catch (e) {
- originalStack = e.stack;
- throw e;
- }
- }).
- then(fail).
- catch(function(e) { throw e; }).
- then(fail).
- catch(function(e) { throw e; }).
- then(fail, function(e) {
- assert.equal(error, e);
- if (typeof originalStack !== 'string') {
- return;
- }
- assert.notEqual(originalStack, e.stack);
- assert.equal(e.stack.indexOf(originalStack), 0,
- 'should start with original stack');
- assert.deepEqual([
- 'From: ManagedPromise: new',
- 'From: Promise: then',
- 'From: Promise: catch',
- 'From: Promise: then',
- 'From: Promise: catch',
- ], getStackMessages(e));
+ return new promise.Promise(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).
+ then(fail).
+ catch(function(e) { throw e; }).
+ then(fail).
+ catch(function(e) { throw e; }).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual([
+ 'From: ManagedPromise: new',
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ ], getStackMessages(e));
+ });
});
- });
- it('errorOccursInCallbackChain', function() {
- promise.LONG_STACK_TRACES = true;
-
- var error = Error('hello');
- var originalStack = '(placeholder; will be overwritten later)';
-
- return promise.fulfilled().
- then(function() {}).
- then(function() {}).
- then(function() {
- try {
- throw error;
- } catch (e) {
- originalStack = e.stack;
- throw e;
- }
- }).
- catch(function(e) { throw e; }).
- then(fail, function(e) {
- assert.equal(error, e);
- if (typeof originalStack !== 'string') {
- return;
- }
- assert.notEqual(originalStack, e.stack);
- assert.equal(e.stack.indexOf(originalStack), 0,
- 'should start with original stack');
- assert.deepEqual([
- 'From: Promise: then',
- 'From: Promise: catch',
- ], getStackMessages(e));
- });
+ it('errorOccursInCallbackChain', function() {
+ promise.LONG_STACK_TRACES = true;
+
+ var error = Error('hello');
+ var originalStack = '(placeholder; will be overwritten later)';
+
+ return promise.fulfilled().
+ then(function() {}).
+ then(function() {}).
+ then(function() {
+ try {
+ throw error;
+ } catch (e) {
+ originalStack = e.stack;
+ throw e;
+ }
+ }).
+ catch(function(e) { throw e; }).
+ then(fail, function(e) {
+ assert.equal(error, e);
+ if (typeof originalStack !== 'string') {
+ return;
+ }
+ assert.notEqual(originalStack, e.stack);
+ assert.equal(e.stack.indexOf(originalStack), 0,
+ 'should start with original stack');
+ assert.deepEqual([
+ 'From: Promise: then',
+ 'From: Promise: catch',
+ ], getStackMessages(e));
+ });
+ });
});
});
+
+ it('testAddThenableImplementation', function() {
+ function tmp() {}
+ assert.ok(!promise.Thenable.isImplementation(new tmp()));
+ promise.Thenable.addImplementation(tmp);
+ assert.ok(promise.Thenable.isImplementation(new tmp()));
+
+ class tmpClass {}
+ assert.ok(!promise.Thenable.isImplementation(new tmpClass()));
+ promise.Thenable.addImplementation(tmpClass);
+ assert.ok(promise.Thenable.isImplementation(new tmpClass()));
+ });
});
diff --git a/node_modules/selenium-webdriver/test/lib/until_test.js b/node_modules/selenium-webdriver/test/lib/until_test.js
index d3e81ec18..31b2b32ad 100644
--- a/node_modules/selenium-webdriver/test/lib/until_test.js
+++ b/node_modules/selenium-webdriver/test/lib/until_test.js
@@ -84,7 +84,7 @@ describe('until', function() {
it('byWebElementPromise', function() {
executor.on(CommandName.SWITCH_TO_FRAME, () => true);
var el = new webdriver.WebElementPromise(driver,
- promise.fulfilled(new webdriver.WebElement(driver, {ELEMENT: 1234})));
+ Promise.resolve(new webdriver.WebElement(driver, {ELEMENT: 1234})));
return driver.wait(until.ableToSwitchToFrame(el), 100);
});
diff --git a/node_modules/selenium-webdriver/test/lib/webdriver_test.js b/node_modules/selenium-webdriver/test/lib/webdriver_test.js
index d2d87ca3e..0a124530e 100644
--- a/node_modules/selenium-webdriver/test/lib/webdriver_test.js
+++ b/node_modules/selenium-webdriver/test/lib/webdriver_test.js
@@ -29,6 +29,8 @@ const Key = require('../../lib/input').Key;
const logging = require('../../lib/logging');
const Session = require('../../lib/session').Session;
const promise = require('../../lib/promise');
+const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
+const until = require('../../lib/until');
const Alert = require('../../lib/webdriver').Alert;
const AlertPromise = require('../../lib/webdriver').AlertPromise;
const UnhandledAlertError = require('../../lib/webdriver').UnhandledAlertError;
@@ -73,6 +75,9 @@ describe('WebDriver', function() {
});
afterEach(function tearDown() {
+ if (!promise.USE_PROMISE_MANAGER) {
+ return;
+ }
return waitForIdle(flow).then(function() {
assert.deepEqual([], uncaughtExceptions);
flow.reset();
@@ -83,7 +88,19 @@ describe('WebDriver', function() {
uncaughtExceptions.push(e);
}
+ function defer() {
+ let d = {};
+ let promise = new Promise((resolve, reject) => {
+ Object.assign(d, {resolve, reject});
+ });
+ d.promise = promise;
+ return d;
+ }
+
function waitForIdle(opt_flow) {
+ if (!promise.USE_PROMISE_MANAGER) {
+ return Promise.resolve();
+ }
var theFlow = opt_flow || flow;
return new Promise(function(fulfill, reject) {
if (theFlow.isIdle()) {
@@ -300,19 +317,21 @@ describe('WebDriver', function() {
return waitForIdle(driver.controlFlow());
});
- it('canAttachInCustomFlow', function() {
- let executor = new FakeExecutor().
- expect(CName.DESCRIBE_SESSION).
- withParameters({'sessionId': SESSION_ID}).
- andReturnSuccess({}).
- end();
+ enablePromiseManager(() => {
+ it('canAttachInCustomFlow', function() {
+ let executor = new FakeExecutor().
+ expect(CName.DESCRIBE_SESSION).
+ withParameters({'sessionId': SESSION_ID}).
+ andReturnSuccess({}).
+ end();
- var otherFlow = new promise.ControlFlow();
- var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow);
- assert.equal(otherFlow, driver.controlFlow());
- assert.notEqual(otherFlow, promise.controlFlow());
+ var otherFlow = new promise.ControlFlow();
+ var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow);
+ assert.equal(otherFlow, driver.controlFlow());
+ assert.notEqual(otherFlow, promise.controlFlow());
- return waitForIdle(otherFlow);
+ return waitForIdle(otherFlow);
+ });
});
});
@@ -345,7 +364,7 @@ describe('WebDriver', function() {
return driver.getSession().then(v => assert.strictEqual(v, aSession));
});
- it('handles desired and requried capabilities', function() {
+ it('handles desired and required capabilities', function() {
let aSession = new Session(SESSION_ID, {'browserName': 'firefox'});
let executor = new FakeExecutor().
expect(CName.NEW_SESSION).
@@ -374,6 +393,23 @@ describe('WebDriver', function() {
return driver.getSession().then(fail, assertIsStubError);
});
+ it('invokes quit callback if it fails to create a session', function() {
+ let called = false;
+ let executor = new FakeExecutor()
+ .expect(CName.NEW_SESSION)
+ .withParameters({'desiredCapabilities': {'browserName': 'firefox'}})
+ .andReturnError(new StubError())
+ .end();
+
+ var driver =
+ WebDriver.createSession(executor, {'browserName': 'firefox'},
+ null, null, () => called = true);
+ return driver.getSession().then(fail, err => {
+ assert.ok(called);
+ assertIsStubError(err);
+ });
+ });
+
it('usesActiveFlowByDefault', function() {
let executor = new FakeExecutor().
expect(CName.NEW_SESSION).
@@ -387,26 +423,60 @@ describe('WebDriver', function() {
return waitForIdle(driver.controlFlow());
});
- it('canCreateInCustomFlow', function() {
- let executor = new FakeExecutor().
- expect(CName.NEW_SESSION).
- withParameters({'desiredCapabilities': {}}).
- andReturnSuccess({}).
- end();
+ enablePromiseManager(() => {
+ it('canCreateInCustomFlow', function() {
+ let executor = new FakeExecutor().
+ expect(CName.NEW_SESSION).
+ withParameters({'desiredCapabilities': {}}).
+ andReturnSuccess({}).
+ end();
- var otherFlow = new promise.ControlFlow();
- var driver = WebDriver.createSession(executor, {}, otherFlow);
- assert.equal(otherFlow, driver.controlFlow());
- assert.notEqual(otherFlow, promise.controlFlow());
+ var otherFlow = new promise.ControlFlow();
+ var driver = WebDriver.createSession(executor, {}, otherFlow);
+ assert.equal(otherFlow, driver.controlFlow());
+ assert.notEqual(otherFlow, promise.controlFlow());
+
+ return waitForIdle(otherFlow);
+ });
- return waitForIdle(otherFlow);
+ describe('creation failures bubble up in control flow', function() {
+ function runTest(...args) {
+ let executor = new FakeExecutor()
+ .expect(CName.NEW_SESSION)
+ .withParameters({'desiredCapabilities': {'browserName': 'firefox'}})
+ .andReturnError(new StubError())
+ .end();
+
+ WebDriver.createSession(
+ executor, {'browserName': 'firefox'}, ...args);
+ return waitForAbort().then(assertIsStubError);
+ }
+
+ it('no onQuit callback', () => runTest());
+ it('has onQuit callback', () => runTest(null, null, function() {}));
+
+ it('onQuit callback failure suppress creation failure', function() {
+ let e = new Error('hi!');
+ let executor = new FakeExecutor()
+ .expect(CName.NEW_SESSION)
+ .withParameters({'desiredCapabilities': {'browserName': 'firefox'}})
+ .andReturnError(new StubError())
+ .end();
+
+ WebDriver.createSession(
+ executor, {'browserName': 'firefox'}, null, null,
+ () => {throw e});
+ return waitForAbort().then(err => assert.strictEqual(err, e));
+ });
+ });
});
});
it('testDoesNotExecuteCommandIfSessionDoesNotResolve', function() {
var session = Promise.reject(new StubError);
- new FakeExecutor().createDriver(session).getTitle();
- return waitForAbort().then(assertIsStubError);
+ return new FakeExecutor().createDriver(session)
+ .getTitle()
+ .then(_ => assert.fail('should have failed'), assertIsStubError);
});
it('testCommandReturnValuesArePassedToFirstCallback', function() {
@@ -415,9 +485,8 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- return driver.getTitle().then(function(title) {
- assert.equal('Google Search', title);
- });
+ return driver.getTitle()
+ .then(title => assert.equal('Google Search', title));
});
it('testStopsCommandExecutionWhenAnErrorOccurs', function() {
@@ -431,11 +500,11 @@ describe('WebDriver', function() {
andReturnError(e).
end();
- var driver = executor.createDriver();
- driver.switchTo().window('foo');
- driver.getTitle(); // mock should blow if this gets executed
-
- return waitForAbort().then(v => assert.strictEqual(v, e));
+ let driver = executor.createDriver();
+ return driver.switchTo().window('foo')
+ .then(
+ _ => driver.getTitle(), // mock should blow if this gets executed
+ v => assert.strictEqual(v, e));
});
it('testCanSuppressCommandFailures', function() {
@@ -469,8 +538,9 @@ describe('WebDriver', function() {
andReturnError(e).
end();
- executor.createDriver().switchTo().window('foo');
- return waitForAbort().then(v => assert.strictEqual(v, e));
+ return executor.createDriver()
+ .switchTo().window('foo')
+ .then(_ => assert.fail(), v => assert.strictEqual(v, e));
});
it('testErrbacksThatReturnErrorsStillSwitchToCallbackChain', function() {
@@ -486,7 +556,7 @@ describe('WebDriver', function() {
var driver = executor.createDriver();
return driver.switchTo().window('foo').
catch(function() { return new StubError; });
- then(assertIsStubError);
+ then(assertIsStubError, () => assert.fail());
});
it('testErrbacksThrownCanOverrideOriginalError', function() {
@@ -499,27 +569,9 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- driver.switchTo().window('foo').catch(throwStubError);
-
- return waitForAbort().then(assertIsStubError);
- });
-
- it('testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted', function() {
- var driver = new FakeExecutor().createDriver();
- delete driver.session_;
- assert.throws(() => driver.get('http://www.google.com'));
- });
-
- it('testDeletesSessionIdAfterQuitting', function() {
- var driver;
- let executor = new FakeExecutor().
- expect(CName.QUIT).
- end();
-
- driver = executor.createDriver();
- return driver.quit().then(function() {
- assert.equal(void 0, driver.session_);
- });
+ return driver.switchTo().window('foo')
+ .catch(throwStubError)
+ .then(assert.fail, assertIsStubError);
});
it('testReportsErrorWhenExecutingCommandsAfterExecutingAQuit', function() {
@@ -527,14 +579,15 @@ describe('WebDriver', function() {
expect(CName.QUIT).
end();
- var driver = executor.createDriver();
- driver.quit();
- driver.get('http://www.google.com');
- return waitForAbort().
- then(expectedError(
- error.NoSuchSessionError,
- 'This driver instance does not have a valid session ID ' +
- '(did you call WebDriver.quit()?) and may no longer be used.'));
+ let verifyError = expectedError(
+ error.NoSuchSessionError,
+ 'This driver instance does not have a valid session ID ' +
+ '(did you call WebDriver.quit()?) and may no longer be used.');
+
+ let driver = executor.createDriver();
+ return driver.quit()
+ .then(_ => driver.get('http://www.google.com'))
+ .then(assert.fail, verifyError);
});
it('testCallbackCommandsExecuteBeforeNextCommand', function() {
@@ -556,114 +609,29 @@ describe('WebDriver', function() {
return waitForIdle();
});
- it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() {
- let executor = new FakeExecutor().
- expect(CName.GET_TITLE).
- expect(CName.GET_CURRENT_URL).
- expect(CName.GET_CURRENT_WINDOW_HANDLE).
- expect(CName.CLOSE).
- expect(CName.QUIT).
- end();
-
- var driver = executor.createDriver();
- driver.getTitle().
- // Everything in this callback...
- then(function() {
- driver.getCurrentUrl();
- driver.getWindowHandle();
- }).
- // ...should execute before everything in this callback.
- then(function() {
- driver.close();
- });
- // This should execute after everything above
- driver.quit();
-
- return waitForIdle();
- });
-
- describe('nestedCommandFailures', function() {
- it('bubbleUpToGlobalHandlerIfUnsuppressed', function() {
- let e = new error.NoSuchWindowError('window not found');
- let executor = new FakeExecutor().
- expect(CName.GET_TITLE).
- expect(CName.SWITCH_TO_WINDOW, {
- 'name': 'foo',
- 'handle': 'foo'
- }).
- andReturnError(e).
- end();
-
- var driver = executor.createDriver();
- driver.getTitle().then(function() {
- driver.switchTo().window('foo');
- });
-
- return waitForAbort().then(v => assert.strictEqual(v, e));
- });
-
- it('canBeSuppressWhenTheyOccur', function() {
+ enablePromiseManager(() => {
+ it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() {
let executor = new FakeExecutor().
expect(CName.GET_TITLE).
- expect(CName.SWITCH_TO_WINDOW, {
- 'name': 'foo',
- 'handle': 'foo'
- }).
- andReturnError(new error.NoSuchWindowError('window not found')).
+ expect(CName.GET_CURRENT_URL).
+ expect(CName.GET_CURRENT_WINDOW_HANDLE).
expect(CName.CLOSE).
- end();
-
- var driver = executor.createDriver();
- driver.getTitle().then(function() {
- driver.switchTo().window('foo').catch(function() {});
- });
- driver.close();
-
- return waitForIdle();
- });
-
- it('bubbleUpThroughTheFrameStack', function() {
- let e = new error.NoSuchWindowError('window not found');
- let executor = new FakeExecutor().
- expect(CName.GET_TITLE).
- expect(CName.SWITCH_TO_WINDOW, {
- 'name': 'foo',
- 'handle': 'foo'
- }).
- andReturnError(e).
+ expect(CName.QUIT).
end();
var driver = executor.createDriver();
driver.getTitle().
+ // Everything in this callback...
then(function() {
- return driver.switchTo().window('foo');
- }).
- catch(v => assert.strictEqual(v, e));
-
- return waitForIdle();
- });
-
- it('canBeCaughtAndSuppressed', function() {
- let executor = new FakeExecutor().
- expect(CName.GET_TITLE).
- expect(CName.GET_CURRENT_URL).
- expect(CName.SWITCH_TO_WINDOW, {
- 'name': 'foo',
- 'handle': 'foo'
+ driver.getCurrentUrl();
+ driver.getWindowHandle();
}).
- andReturnError(new StubError()).
- expect(CName.CLOSE).
- end();
-
- var driver = executor.createDriver();
- driver.getTitle().then(function() {
- driver.getCurrentUrl().
- then(function() {
- return driver.switchTo().window('foo');
- }).
- catch(function() {});
- driver.close();
- });
+ // ...should execute before everything in this callback.
+ then(function() {
+ driver.close();
+ });
+ // This should execute after everything above
+ driver.quit();
return waitForIdle();
});
@@ -678,18 +646,16 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- driver.getTitle().
+ return driver.getTitle().
then(function() {
return driver.getCurrentUrl();
}).
then(function(value) {
assert.equal('http://www.google.com', value);
});
- return waitForIdle();
});
it('fromAnErrbackSuppressesTheError', function() {
- var count = 0;
let executor = new FakeExecutor().
expect(CName.SWITCH_TO_WINDOW, {
'name': 'foo',
@@ -701,19 +667,12 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- driver.switchTo().window('foo').
+ return driver.switchTo().window('foo').
catch(function(e) {
assertIsStubError(e);
- count += 1;
return driver.getCurrentUrl();
}).
- then(function(url) {
- count += 1;
- assert.equal('http://www.google.com', url);
- });
- return waitForIdle().then(function() {
- assert.equal(2, count);
- });
+ then(url => assert.equal('http://www.google.com', url));
});
});
@@ -725,23 +684,25 @@ describe('WebDriver', function() {
});
});
- it('executionOrderWithCustomFunctions', function() {
- var msg = [];
- let executor = new FakeExecutor().
- expect(CName.GET_TITLE).andReturnSuccess('cheese ').
- expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty').
- end();
+ enablePromiseManager(() => {
+ it('executionOrderWithCustomFunctions', function() {
+ var msg = [];
+ let executor = new FakeExecutor().
+ expect(CName.GET_TITLE).andReturnSuccess('cheese ').
+ expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty').
+ end();
- var driver = executor.createDriver();
+ var driver = executor.createDriver();
- var pushMsg = msg.push.bind(msg);
- driver.getTitle().then(pushMsg);
- driver.call(() => 'is ').then(pushMsg);
- driver.getCurrentUrl().then(pushMsg);
- driver.call(() => '!').then(pushMsg);
+ var pushMsg = msg.push.bind(msg);
+ driver.getTitle().then(pushMsg);
+ driver.call(() => 'is ').then(pushMsg);
+ driver.getCurrentUrl().then(pushMsg);
+ driver.call(() => '!').then(pushMsg);
- return waitForIdle().then(function() {
- assert.equal('cheese is tasty!', msg.join(''));
+ return waitForIdle().then(function() {
+ assert.equal('cheese is tasty!', msg.join(''));
+ });
});
});
@@ -756,7 +717,7 @@ describe('WebDriver', function() {
});
it('passingPromisedArgumentsToACustomFunction', function() {
- var promisedArg = promise.fulfilled(2);
+ var promisedArg = Promise.resolve(2);
var add = function(a, b) {
return a + b;
};
@@ -834,30 +795,32 @@ describe('WebDriver', function() {
}).then(fail, assertIsStubError);
});
- it('doesNotCompleteUntilReturnedPromiseIsResolved', function() {
- var order = [];
- var driver = new FakeExecutor().createDriver();
+ enablePromiseManager(() => {
+ it('doesNotCompleteUntilReturnedPromiseIsResolved', function() {
+ var order = [];
+ var driver = new FakeExecutor().createDriver();
- var d = promise.defer();
- d.promise.then(function() {
- order.push('b');
- });
+ var d = promise.defer();
+ d.promise.then(function() {
+ order.push('b');
+ });
- driver.call(function() {
- order.push('a');
- return d.promise;
- });
- driver.call(function() {
- order.push('c');
- });
+ driver.call(function() {
+ order.push('a');
+ return d.promise;
+ });
+ driver.call(function() {
+ order.push('c');
+ });
- // timeout to ensure the first function starts its execution before we
- // trigger d's callbacks.
- return new Promise(f => setTimeout(f, 0)).then(function() {
- assert.deepEqual(['a'], order);
- d.fulfill();
- return waitForIdle().then(function() {
- assert.deepEqual(['a', 'b', 'c'], order);
+ // timeout to ensure the first function starts its execution before we
+ // trigger d's callbacks.
+ return new Promise(f => setTimeout(f, 0)).then(function() {
+ assert.deepEqual(['a'], order);
+ d.fulfill();
+ return waitForIdle().then(function() {
+ assert.deepEqual(['a', 'b', 'c'], order);
+ });
});
});
});
@@ -878,36 +841,58 @@ describe('WebDriver', function() {
});
describe('nestedCommands', function() {
- it('commandExecutionOrder', function() {
- var msg = [];
- var driver = new FakeExecutor().createDriver();
- driver.call(msg.push, msg, 'a');
- driver.call(function() {
- driver.call(msg.push, msg, 'c');
+ enablePromiseManager(() => {
+ it('commandExecutionOrder', function() {
+ var msg = [];
+ var driver = new FakeExecutor().createDriver();
+ driver.call(msg.push, msg, 'a');
driver.call(function() {
- driver.call(msg.push, msg, 'e');
- driver.call(msg.push, msg, 'f');
+ driver.call(msg.push, msg, 'c');
+ driver.call(function() {
+ driver.call(msg.push, msg, 'e');
+ driver.call(msg.push, msg, 'f');
+ });
+ driver.call(msg.push, msg, 'd');
+ });
+ driver.call(msg.push, msg, 'b');
+ return waitForIdle().then(function() {
+ assert.equal('acefdb', msg.join(''));
});
- driver.call(msg.push, msg, 'd');
- });
- driver.call(msg.push, msg, 'b');
- return waitForIdle().then(function() {
- assert.equal('acefdb', msg.join(''));
});
- });
- it('basicUsage', function() {
- var msg = [];
- var driver = new FakeExecutor().createDriver();
- var pushMsg = msg.push.bind(msg);
- driver.call(() => 'cheese ').then(pushMsg);
- driver.call(function() {
- driver.call(() => 'is ').then(pushMsg);
- driver.call(() => 'tasty').then(pushMsg);
+ it('basicUsage', function() {
+ var msg = [];
+ var driver = new FakeExecutor().createDriver();
+ var pushMsg = msg.push.bind(msg);
+ driver.call(() => 'cheese ').then(pushMsg);
+ driver.call(function() {
+ driver.call(() => 'is ').then(pushMsg);
+ driver.call(() => 'tasty').then(pushMsg);
+ });
+ driver.call(() => '!').then(pushMsg);
+ return waitForIdle().then(function() {
+ assert.equal('cheese is tasty!', msg.join(''));
+ });
});
- driver.call(() => '!').then(pushMsg);
- return waitForIdle().then(function() {
- assert.equal('cheese is tasty!', msg.join(''));
+
+ it('normalCommandAfterNestedCommandThatReturnsAnAction', function() {
+ var msg = [];
+ let executor = new FakeExecutor().
+ expect(CName.CLOSE).
+ end();
+ var driver = executor.createDriver();
+ driver.call(function() {
+ return driver.call(function() {
+ msg.push('a');
+ return driver.call(() => 'foobar');
+ });
+ });
+ driver.close().then(function() {
+ msg.push('b');
+ });
+ return waitForIdle().then(function() {
+ assert.equal('ab', msg.join(''));
+ });
});
});
@@ -922,26 +907,6 @@ describe('WebDriver', function() {
});
});
- it('normalCommandAfterNestedCommandThatReturnsAnAction', function() {
- var msg = [];
- let executor = new FakeExecutor().
- expect(CName.CLOSE).
- end();
- var driver = executor.createDriver();
- driver.call(function() {
- return driver.call(function() {
- msg.push('a');
- return driver.call(() => 'foobar');
- });
- });
- driver.close().then(function() {
- msg.push('b');
- });
- return waitForIdle().then(function() {
- assert.equal('ab', msg.join(''));
- });
- });
-
it('errorsBubbleUp_caught', function() {
var driver = new FakeExecutor().createDriver();
var result = driver.call(function() {
@@ -954,12 +919,12 @@ describe('WebDriver', function() {
it('errorsBubbleUp_uncaught', function() {
var driver = new FakeExecutor().createDriver();
- driver.call(function() {
+ return driver.call(function() {
return driver.call(function() {
return driver.call(throwStubError);
});
- });
- return waitForAbort().then(assertIsStubError);
+ })
+ .then(_ => assert.fail('should have failed'), assertIsStubError);
});
it('canScheduleCommands', function() {
@@ -980,29 +945,27 @@ describe('WebDriver', function() {
});
describe('WebElementPromise', function() {
+ let driver = new FakeExecutor().createDriver();
+
it('resolvesWhenUnderlyingElementDoes', function() {
- var el = new WebElement({}, {'ELEMENT': 'foo'});
- return new WebElementPromise({}, promise.fulfilled(el)).then(function(e) {
- assert.strictEqual(e, el);
- });
+ let el = new WebElement(driver, {'ELEMENT': 'foo'});
+ return new WebElementPromise(driver, Promise.resolve(el))
+ .then(e => assert.strictEqual(e, el));
});
it('resolvesBeforeCallbacksOnWireValueTrigger', function() {
- var el = new promise.Deferred();
+ var el = defer();
- var element = new WebElementPromise({}, el.promise);
+ var element = new WebElementPromise(driver, el.promise);
var messages = [];
- element.then(function() {
- messages.push('element resolved');
- });
- element.getId().then(function() {
- messages.push('wire value resolved');
- });
+ let steps = [
+ element.then(_ => messages.push('element resolved')),
+ element.getId().then(_ => messages.push('wire value resolved'))
+ ];
- assert.deepEqual([], messages);
- el.fulfill(new WebElement({}, {'ELEMENT': 'foo'}));
- return waitForIdle().then(function() {
+ el.resolve(new WebElement(driver, {'ELEMENT': 'foo'}));
+ return Promise.all(steps).then(function() {
assert.deepEqual([
'element resolved',
'wire value resolved'
@@ -1011,7 +974,8 @@ describe('WebDriver', function() {
});
it('isRejectedIfUnderlyingIdIsRejected', function() {
- var element = new WebElementPromise({}, promise.rejected(new StubError));
+ let element =
+ new WebElementPromise(driver, Promise.reject(new StubError));
return element.then(fail, assertIsStubError);
});
});
@@ -1207,7 +1171,7 @@ describe('WebDriver', function() {
it('failsIfArgumentIsARejectedPromise', function() {
let executor = new FakeExecutor();
- var arg = promise.rejected(new StubError);
+ var arg = Promise.reject(new StubError);
arg.catch(function() {}); // Suppress default handler.
var driver = executor.createDriver();
@@ -1218,7 +1182,7 @@ describe('WebDriver', function() {
describe('executeAsyncScript', function() {
it('failsIfArgumentIsARejectedPromise', function() {
- var arg = promise.rejected(new StubError);
+ var arg = Promise.reject(new StubError);
arg.catch(function() {}); // Suppress default handler.
var driver = new FakeExecutor().createDriver();
@@ -1236,9 +1200,8 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- var element = driver.findElement(By.id('foo'));
- element.click(); // This should never execute.
- return waitForAbort().then(assertIsStubError);
+ return driver.findElement(By.id('foo'))
+ .then(assert.fail, assertIsStubError);
});
it('elementNotFoundInACallback', function() {
@@ -1249,11 +1212,9 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- promise.fulfilled().then(function() {
- var element = driver.findElement(By.id('foo'));
- return element.click(); // Should not execute.
- });
- return waitForAbort().then(assertIsStubError);
+ return Promise.resolve()
+ .then(_ => driver.findElement(By.id('foo')))
+ .then(assert.fail, assertIsStubError);
});
it('elementFound', function() {
@@ -1261,7 +1222,7 @@ describe('WebDriver', function() {
expect(CName.FIND_ELEMENT,
{using: 'css selector', value: '*[id="foo"]'}).
andReturnSuccess(WebElement.buildId('bar')).
- expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}).
andReturnSuccess().
end();
@@ -1276,7 +1237,7 @@ describe('WebDriver', function() {
expect(CName.FIND_ELEMENT,
{using: 'css selector', value: '*[id="foo"]'}).
andReturnSuccess(WebElement.buildId('bar')).
- expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}).
andReturnSuccess().
end();
@@ -1294,7 +1255,7 @@ describe('WebDriver', function() {
'args': []
}).
andReturnSuccess(WebElement.buildId('bar')).
- expect(CName.CLICK_ELEMENT, {'id': 'bar'}).
+ expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('bar')}).
end();
var driver = executor.createDriver();
@@ -1310,12 +1271,12 @@ describe('WebDriver', function() {
end();
var driver = executor.createDriver();
- var element = driver.findElement(By.js('return 123'));
- element.click(); // Should not execute.
- return waitForAbort().then(function(e) {
- assertIsInstance(TypeError, e);
- assert.equal('Custom locator did not return a WebElement', e.message);
- });
+ return driver.findElement(By.js('return 123'))
+ .then(assert.fail, function(e) {
+ assertIsInstance(TypeError, e);
+ assert.equal(
+ 'Custom locator did not return a WebElement', e.message);
+ });
});
it('byJs_canPassArguments', function() {
@@ -1325,7 +1286,7 @@ describe('WebDriver', function() {
'script': script,
'args': ['div']
}).
- andReturnSuccess({'ELEMENT':'one'}).
+ andReturnSuccess(WebElement.buildId('one')).
end();
var driver = executor.createDriver();
driver.findElement(By.js(script, 'div'));
@@ -1338,7 +1299,7 @@ describe('WebDriver', function() {
andReturnSuccess([
WebElement.buildId('foo'),
WebElement.buildId('bar')]).
- expect(CName.CLICK_ELEMENT, {'id': 'foo'}).
+ expect(CName.CLICK_ELEMENT, {'id': WebElement.buildId('foo')}).
andReturnSuccess().
end();
@@ -1347,19 +1308,17 @@ describe('WebDriver', function() {
assert.equal(driver, d);
return d.findElements(By.tagName('a'));
});
- element.click();
- return waitForIdle();
+ return element.click();
});
it('customLocatorThrowsIfresultIsNotAWebElement', function() {
var driver = new FakeExecutor().createDriver();
- driver.findElement(function() {
- return 1;
- });
- return waitForAbort().then(function(e) {
- assertIsInstance(TypeError, e);
- assert.equal('Custom locator did not return a WebElement', e.message);
- });
+ return driver.findElement(_ => 1)
+ .then(assert.fail, function(e) {
+ assertIsInstance(TypeError, e);
+ assert.equal(
+ 'Custom locator did not return a WebElement', e.message);
+ });
});
});
@@ -1486,7 +1445,8 @@ describe('WebDriver', function() {
it('convertsVarArgsIntoStrings_simpleArgs', function() {
let executor = new FakeExecutor().
expect(CName.SEND_KEYS_TO_ELEMENT,
- {'id': 'one', 'value':'12abc3'.split('')}).
+ {'id': WebElement.buildId('one'),
+ 'value':'12abc3'.split('')}).
andReturnSuccess().
end();
@@ -1502,16 +1462,17 @@ describe('WebDriver', function() {
{'using':'css selector', 'value':'*[id="foo"]'}).
andReturnSuccess(WebElement.buildId('one')).
expect(CName.SEND_KEYS_TO_ELEMENT,
- {'id':'one', 'value':'abc123def'.split('')}).
+ {'id':WebElement.buildId('one'),
+ 'value':'abc123def'.split('')}).
andReturnSuccess().
end();
var driver = executor.createDriver();
var element = driver.findElement(By.id('foo'));
- element.sendKeys(
- promise.fulfilled('abc'), 123,
- promise.fulfilled('def'));
- return waitForIdle();
+ return element.sendKeys(
+ Promise.resolve('abc'),
+ 123,
+ Promise.resolve('def'));
});
it('sendKeysWithAFileDetector', function() {
@@ -1520,7 +1481,8 @@ describe('WebDriver', function() {
{'using':'css selector', 'value':'*[id="foo"]'}).
andReturnSuccess(WebElement.buildId('one')).
expect(CName.SEND_KEYS_TO_ELEMENT,
- {'id': 'one', 'value':'modified/path'.split('')}).
+ {'id': WebElement.buildId('one'),
+ 'value':'modified/path'.split('')}).
andReturnSuccess().
end();
@@ -1528,13 +1490,11 @@ describe('WebDriver', function() {
let handleFile = function(d, path) {
assert.strictEqual(driver, d);
assert.equal(path, 'original/path');
- return promise.fulfilled('modified/path');
+ return Promise.resolve('modified/path');
};
driver.setFileDetector({handleFile});
- var element = driver.findElement(By.id('foo'));
- element.sendKeys('original/', 'path');
- return waitForIdle();
+ return driver.findElement(By.id('foo')).sendKeys('original/', 'path');
});
});
@@ -1554,7 +1514,7 @@ describe('WebDriver', function() {
return waitForIdle();
});
- it('should propogate exceptions', function() {
+ it('should propagate exceptions', function() {
let e = new error.NoSuchWindowError('window not found');
let executor = new FakeExecutor().
expect(CName.SWITCH_TO_WINDOW).
@@ -1565,21 +1525,21 @@ describe('WebDriver', function() {
andReturnError(e).
end();
- executor.createDriver().switchTo().window('foo');
- return waitForAbort().then(v => assert.strictEqual(v, e));
+ return executor.createDriver()
+ .switchTo().window('foo')
+ .then(assert.fail, v => assert.strictEqual(v, e));
});
});
});
describe('elementEquality', function() {
it('isReflexive', function() {
- var a = new WebElement({}, 'foo');
+ var a = new WebElement(new FakeExecutor().createDriver(), 'foo');
return WebElement.equals(a, a).then(assert.ok);
});
it('failsIfAnInputElementCouldNotBeFound', function() {
- var id = promise.rejected(new StubError);
- id.catch(function() {}); // Suppress default handler.
+ let id = Promise.reject(new StubError);
var driver = new FakeExecutor().createDriver();
var a = new WebElement(driver, 'foo');
@@ -1590,71 +1550,134 @@ describe('WebDriver', function() {
});
describe('waiting', function() {
- it('waitSucceeds', function() {
- let executor = new FakeExecutor().
- expect(CName.FIND_ELEMENTS,
- {using: 'css selector', value: '*[id="foo"]'}).
- andReturnSuccess([]).
- times(2).
- expect(CName.FIND_ELEMENTS,
- {using: 'css selector', value: '*[id="foo"]'}).
- andReturnSuccess([WebElement.buildId('bar')]).
- end();
+ describe('supports custom wait functions', function() {
+ it('waitSucceeds', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ times(2).
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([WebElement.buildId('bar')]).
+ end();
- var driver = executor.createDriver();
- driver.wait(function() {
- return driver.findElements(By.id('foo')).then(els => els.length > 0);
- }, 200);
- return waitForIdle();
+ var driver = executor.createDriver();
+ driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 200);
+ return waitForIdle();
+ });
+
+ it('waitTimesout_timeoutCaught', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ anyTimes().
+ end();
+
+ var driver = executor.createDriver();
+ return driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 25).then(fail, function(e) {
+ assert.equal('Wait timed out after ',
+ e.message.substring(0, 'Wait timed out after '.length));
+ });
+ });
+
+ enablePromiseManager(() => {
+ it('waitTimesout_timeoutNotCaught', function() {
+ let executor = new FakeExecutor().
+ expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'}).
+ andReturnSuccess([]).
+ anyTimes().
+ end();
+
+ var driver = executor.createDriver();
+ driver.wait(function() {
+ return driver.findElements(By.id('foo')).then(els => els.length > 0);
+ }, 25);
+ return waitForAbort().then(function(e) {
+ assert.equal('Wait timed out after ',
+ e.message.substring(0, 'Wait timed out after '.length));
+ });
+ });
+ });
});
- it('waitTimesout_timeoutCaught', function() {
- let executor = new FakeExecutor().
- expect(CName.FIND_ELEMENTS,
- {using: 'css selector', value: '*[id="foo"]'}).
- andReturnSuccess([]).
- anyTimes().
- end();
+ describe('supports condition objects', function() {
+ it('wait succeeds', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess([])
+ .times(2)
+ .expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess([WebElement.buildId('bar')])
+ .end();
- var driver = executor.createDriver();
- return driver.wait(function() {
- return driver.findElements(By.id('foo')).then(els => els.length > 0);
- }, 25).then(fail, function(e) {
- assert.equal('Wait timed out after ',
- e.message.substring(0, 'Wait timed out after '.length));
+ let driver = executor.createDriver();
+ return driver.wait(until.elementLocated(By.id('foo')), 200);
+ });
+
+ it('wait times out', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENTS,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess([])
+ .anyTimes()
+ .end();
+
+ let driver = executor.createDriver();
+ return driver.wait(until.elementLocated(By.id('foo')), 5)
+ .then(fail, err => assert.ok(err instanceof error.TimeoutError));
});
});
- it('waitTimesout_timeoutNotCaught', function() {
- let executor = new FakeExecutor().
- expect(CName.FIND_ELEMENTS,
- {using: 'css selector', value: '*[id="foo"]'}).
- andReturnSuccess([]).
- anyTimes().
- end();
+ describe('supports promise objects', function() {
+ it('wait succeeds', function() {
+ let promise = new Promise(resolve => {
+ setTimeout(() => resolve(1), 10);
+ });
- var driver = executor.createDriver();
- driver.wait(function() {
- return driver.findElements(By.id('foo')).then(els => els.length > 0);
- }, 25);
- return waitForAbort().then(function(e) {
- assert.equal('Wait timed out after ',
- e.message.substring(0, 'Wait timed out after '.length));
+ let driver = new FakeExecutor().createDriver();
+ return driver.wait(promise, 200).then(v => assert.equal(v, 1));
+ });
+
+ it('wait times out', function() {
+ let promise = new Promise(resolve => {/* never resolves */});
+
+ let driver = new FakeExecutor().createDriver();
+ return driver.wait(promise, 5)
+ .then(fail, err => assert.ok(err instanceof error.TimeoutError));
+ });
+
+ it('wait fails if promise is rejected', function() {
+ let err = Error('boom');
+ let driver = new FakeExecutor().createDriver();
+ return driver.wait(Promise.reject(err), 5)
+ .then(fail, e => assert.strictEqual(e, err));
});
});
+
+ it('fails if not supported condition type provided', function() {
+ let driver = new FakeExecutor().createDriver();
+ assert.throws(() => driver.wait({}, 5), TypeError);
+ });
});
describe('alert handling', function() {
it('alertResolvesWhenPromisedTextResolves', function() {
- var deferredText = new promise.Deferred();
+ let driver = new FakeExecutor().createDriver();
+ let deferredText = defer();
- var alert = new AlertPromise({}, deferredText.promise);
- assert.ok(alert.isPending());
+ let alert = new AlertPromise(driver, deferredText.promise);
- deferredText.fulfill(new Alert({}, 'foo'));
- return alert.getText().then(function(text) {
- assert.equal('foo', text);
- });
+ deferredText.resolve(new Alert(driver, 'foo'));
+ return alert.getText().then(text => assert.equal(text, 'foo'));
});
it('cannotSwitchToAlertThatIsNotPresent', function() {
@@ -1664,32 +1687,33 @@ describe('WebDriver', function() {
.andReturnError(e)
.end();
- var driver = executor.createDriver();
- var alert = driver.switchTo().alert();
- alert.dismiss(); // Should never execute.
- return waitForAbort().then(v => assert.strictEqual(v, e));
+ return executor.createDriver()
+ .switchTo().alert()
+ .then(assert.fail, v => assert.strictEqual(v, e));
});
- it('alertsBelongToSameFlowAsParentDriver', function() {
- let executor = new FakeExecutor()
- .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello')
- .end();
+ enablePromiseManager(() => {
+ it('alertsBelongToSameFlowAsParentDriver', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello')
+ .end();
- var driver = executor.createDriver();
- var otherFlow = new promise.ControlFlow();
- otherFlow.execute(function() {
- driver.switchTo().alert().then(function() {
- assert.strictEqual(
- driver.controlFlow(), promise.controlFlow(),
- 'Alert should belong to the same flow as its parent driver');
+ var driver = executor.createDriver();
+ var otherFlow = new promise.ControlFlow();
+ otherFlow.execute(function() {
+ driver.switchTo().alert().then(function() {
+ assert.strictEqual(
+ driver.controlFlow(), promise.controlFlow(),
+ 'Alert should belong to the same flow as its parent driver');
+ });
});
- });
- assert.notEqual(otherFlow, driver.controlFlow);
- return Promise.all([
- waitForIdle(otherFlow),
- waitForIdle(driver.controlFlow())
- ]);
+ assert.notEqual(otherFlow, driver.controlFlow);
+ return Promise.all([
+ waitForIdle(otherFlow),
+ waitForIdle(driver.controlFlow())
+ ]);
+ });
});
it('commandsFailIfAlertNotPresent', function() {
@@ -1715,26 +1739,28 @@ describe('WebDriver', function() {
});
});
- it('testWebElementsBelongToSameFlowAsParentDriver', function() {
- let executor = new FakeExecutor()
- .expect(CName.FIND_ELEMENT,
- {using: 'css selector', value: '*[id="foo"]'})
- .andReturnSuccess(WebElement.buildId('abc123'))
- .end();
+ enablePromiseManager(() => {
+ it('testWebElementsBelongToSameFlowAsParentDriver', function() {
+ let executor = new FakeExecutor()
+ .expect(CName.FIND_ELEMENT,
+ {using: 'css selector', value: '*[id="foo"]'})
+ .andReturnSuccess(WebElement.buildId('abc123'))
+ .end();
- var driver = executor.createDriver();
- var otherFlow = new promise.ControlFlow();
- otherFlow.execute(function() {
- driver.findElement({id: 'foo'}).then(function() {
- assert.equal(driver.controlFlow(), promise.controlFlow());
+ var driver = executor.createDriver();
+ var otherFlow = new promise.ControlFlow();
+ otherFlow.execute(function() {
+ driver.findElement({id: 'foo'}).then(function() {
+ assert.equal(driver.controlFlow(), promise.controlFlow());
+ });
});
- });
- assert.notEqual(otherFlow, driver.controlFlow);
- return Promise.all([
- waitForIdle(otherFlow),
- waitForIdle(driver.controlFlow())
- ]);
+ assert.notEqual(otherFlow, driver.controlFlow);
+ return Promise.all([
+ waitForIdle(otherFlow),
+ waitForIdle(driver.controlFlow())
+ ]);
+ });
});
it('testFetchingLogs', function() {
@@ -1763,7 +1789,7 @@ describe('WebDriver', function() {
});
it('testCommandsFailIfInitialSessionCreationFailed', function() {
- var session = promise.rejected(new StubError);
+ var session = Promise.reject(new StubError);
var driver = new FakeExecutor().createDriver(session);
var navigateResult = driver.get('some-url').then(fail, assertIsStubError);
@@ -1811,10 +1837,10 @@ describe('WebDriver', function() {
describe('actions()', function() {
it('failsIfInitialDriverCreationFailed', function() {
- let session = promise.rejected(new StubError);
- session.catch(function() {});
- return new FakeExecutor().
- createDriver(session).
+ let session = Promise.reject(new StubError('no session for you'));
+ let driver = new FakeExecutor().createDriver(session);
+ driver.getSession().catch(function() {});
+ return driver.
actions().
mouseDown().
mouseUp().
@@ -1900,10 +1926,10 @@ describe('WebDriver', function() {
describe('touchActions()', function() {
it('failsIfInitialDriverCreationFailed', function() {
- let session = promise.rejected(new StubError);
- session.catch(function() {});
- return new FakeExecutor().
- createDriver(session).
+ let session = Promise.reject(new StubError);
+ let driver = new FakeExecutor().createDriver(session);
+ driver.getSession().catch(function() {});
+ return driver.
touchActions().
scroll({x: 3, y: 4}).
perform().
@@ -1937,8 +1963,8 @@ describe('WebDriver', function() {
it('canUseGeneratorsWithWebDriverCall', function() {
return driver.call(function* () {
- var x = yield promise.fulfilled(1);
- var y = yield promise.fulfilled(2);
+ var x = yield Promise.resolve(1);
+ var y = yield Promise.resolve(2);
return x + y;
}).then(function(value) {
assert.deepEqual(3, value);
@@ -1947,7 +1973,7 @@ describe('WebDriver', function() {
it('canDefineScopeOnGeneratorCall', function() {
return driver.call(function* () {
- var x = yield promise.fulfilled(1);
+ var x = yield Promise.resolve(1);
return this.name + x;
}, {name: 'Bob'}).then(function(value) {
assert.deepEqual('Bob1', value);
@@ -1956,8 +1982,8 @@ describe('WebDriver', function() {
it('canSpecifyArgsOnGeneratorCall', function() {
return driver.call(function* (a, b) {
- var x = yield promise.fulfilled(1);
- var y = yield promise.fulfilled(2);
+ var x = yield Promise.resolve(1);
+ var y = yield Promise.resolve(2);
return [x + y, a, b];
}, null, 'abc', 123).then(function(value) {
assert.deepEqual([3, 'abc', 123], value);
@@ -1992,6 +2018,8 @@ describe('WebDriver', function() {
});
describe('wire format', function() {
+ const FAKE_DRIVER = new FakeExecutor().createDriver();
+
describe('can serialize', function() {
function runSerializeTest(input, want) {
let executor = new FakeExecutor().
@@ -2035,14 +2063,15 @@ describe('WebDriver', function() {
it('WebElement', function() {
return runSerializeTest(
- new WebElement({}, 'fefifofum'),
+ new WebElement(FAKE_DRIVER, 'fefifofum'),
WebElement.buildId('fefifofum'));
});
it('WebElementPromise', function() {
return runSerializeTest(
new WebElementPromise(
- {}, promise.fulfilled(new WebElement({}, 'fefifofum'))),
+ FAKE_DRIVER,
+ Promise.resolve(new WebElement(FAKE_DRIVER, 'fefifofum'))),
WebElement.buildId('fefifofum'));
});
@@ -2053,14 +2082,15 @@ describe('WebDriver', function() {
it('with WebElement', function() {
return runSerializeTest(
- [new WebElement({}, 'fefifofum')],
+ [new WebElement(FAKE_DRIVER, 'fefifofum')],
[WebElement.buildId('fefifofum')]);
});
it('with WebElementPromise', function() {
return runSerializeTest(
[new WebElementPromise(
- {}, promise.fulfilled(new WebElement({}, 'fefifofum')))],
+ FAKE_DRIVER,
+ Promise.resolve(new WebElement(FAKE_DRIVER, 'fefifofum')))],
[WebElement.buildId('fefifofum')]);
});
@@ -2070,7 +2100,7 @@ describe('WebDriver', function() {
[123, {'foo': 'bar'}]
];
- var element = new WebElement({}, 'fefifofum');
+ var element = new WebElement(FAKE_DRIVER, 'fefifofum');
var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]];
return runSerializeTest(input, expected);
});
@@ -2114,7 +2144,7 @@ describe('WebDriver', function() {
'sessionId': 'foo'
};
- var element = new WebElement({}, 'fefifofum');
+ var element = new WebElement(FAKE_DRIVER, 'fefifofum');
var parameters = {
'script': 'return 1',
'args':['abc', 123, true, element, [123, {'foo': 'bar'}]],
diff --git a/node_modules/selenium-webdriver/test/logging_test.js b/node_modules/selenium-webdriver/test/logging_test.js
index fd88daa72..546879715 100644
--- a/node_modules/selenium-webdriver/test/logging_test.js
+++ b/node_modules/selenium-webdriver/test/logging_test.js
@@ -40,125 +40,123 @@ test.suite(function(env) {
driver = null;
});
- test.afterEach(function() {
+ test.afterEach(function*() {
if (driver) {
- driver.quit();
+ return driver.quit();
}
});
- test.it('can be disabled', function() {
+ test.it('can be disabled', function*() {
var prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.OFF);
- driver = env.builder()
+ driver = yield env.builder()
.setLoggingPrefs(prefs)
.build();
- driver.get(dataUrl(
+ yield driver.get(dataUrl(
'<!DOCTYPE html><script>',
'console.info("hello");',
'console.warn("this is a warning");',
'console.error("and this is an error");',
'</script>'));
- driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
- assert(entries.length).equalTo(0);
- });
+ return driver.manage().logs().get(logging.Type.BROWSER)
+ .then(entries => assert(entries.length).equalTo(0));
});
// Firefox does not capture JS error console log messages.
test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
- it('can be turned down', function() {
+ it('can be turned down', function*() {
var prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.SEVERE);
- driver = env.builder()
+ driver = yield env.builder()
.setLoggingPrefs(prefs)
.build();
- driver.get(dataUrl(
+ yield driver.get(dataUrl(
'<!DOCTYPE html><script>',
'console.info("hello");',
'console.warn("this is a warning");',
'console.error("and this is an error");',
'</script>'));
- driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
- assert(entries.length).equalTo(1);
- assert(entries[0].level.name).equalTo('SEVERE');
- assert(entries[0].message).endsWith('and this is an error');
- });
+ return driver.manage().logs().get(logging.Type.BROWSER)
+ .then(function(entries) {
+ assert(entries.length).equalTo(1);
+ assert(entries[0].level.name).equalTo('SEVERE');
+ assert(entries[0].message).matches(/.*\"?and this is an error\"?/);
+ });
});
// Firefox does not capture JS error console log messages.
test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
- it('can be made verbose', function() {
+ it('can be made verbose', function*() {
var prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
- driver = env.builder()
+ driver = yield env.builder()
.setLoggingPrefs(prefs)
.build();
- driver.get(dataUrl(
+ yield driver.get(dataUrl(
'<!DOCTYPE html><script>',
'console.debug("hello");',
'console.warn("this is a warning");',
'console.error("and this is an error");',
'</script>'));
- driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
- assert(entries.length).equalTo(3);
- assert(entries[0].level.name).equalTo('DEBUG');
- assert(entries[0].message).endsWith('hello');
-
- assert(entries[1].level.name).equalTo('WARNING');
- assert(entries[1].message).endsWith('this is a warning');
-
- assert(entries[2].level.name).equalTo('SEVERE');
- assert(entries[2].message).endsWith('and this is an error');
- });
+ return driver.manage().logs().get(logging.Type.BROWSER)
+ .then(function(entries) {
+ assert(entries.length).equalTo(3);
+ assert(entries[0].level.name).equalTo('DEBUG');
+ assert(entries[0].message).matches(/.*\"?hello\"?/);
+
+ assert(entries[1].level.name).equalTo('WARNING');
+ assert(entries[1].message).matches(/.*\"?this is a warning\"?/);
+
+ assert(entries[2].level.name).equalTo('SEVERE');
+ assert(entries[2].message).matches(/.*\"?and this is an error\"?/);
+ });
});
// Firefox does not capture JS error console log messages.
test.ignore(env.browsers(Browser.FIREFOX, 'legacy-firefox')).
- it('clears records after retrieval', function() {
+ it('clears records after retrieval', function*() {
var prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
- driver = env.builder()
+ driver = yield env.builder()
.setLoggingPrefs(prefs)
.build();
- driver.get(dataUrl(
+ yield driver.get(dataUrl(
'<!DOCTYPE html><script>',
'console.debug("hello");',
'console.warn("this is a warning");',
'console.error("and this is an error");',
'</script>'));
- driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
- assert(entries.length).equalTo(3);
- });
- driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) {
- assert(entries.length).equalTo(0);
- });
+ yield driver.manage().logs().get(logging.Type.BROWSER)
+ .then(entries => assert(entries.length).equalTo(3));
+ return driver.manage().logs().get(logging.Type.BROWSER)
+ .then(entries => assert(entries.length).equalTo(0));
});
- test.it('does not mix log types', function() {
+ test.it('does not mix log types', function*() {
var prefs = new logging.Preferences();
prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
prefs.setLevel(logging.Type.DRIVER, logging.Level.SEVERE);
- driver = env.builder()
+ driver = yield env.builder()
.setLoggingPrefs(prefs)
.build();
- driver.get(dataUrl(
+ yield driver.get(dataUrl(
'<!DOCTYPE html><script>',
'console.debug("hello");',
'console.warn("this is a warning");',
'console.error("and this is an error");',
'</script>'));
- driver.manage().logs().get(logging.Type.DRIVER).then(function(entries) {
- assert(entries.length).equalTo(0);
- });
+ return driver.manage().logs().get(logging.Type.DRIVER)
+ .then(entries => assert(entries.length).equalTo(0));
});
});
diff --git a/node_modules/selenium-webdriver/test/net/portprober_test.js b/node_modules/selenium-webdriver/test/net/portprober_test.js
index 03a2f7a10..668c4ae0e 100644
--- a/node_modules/selenium-webdriver/test/net/portprober_test.js
+++ b/node_modules/selenium-webdriver/test/net/portprober_test.js
@@ -17,11 +17,10 @@
'use strict';
-var assert = require('assert'),
- net = require('net');
+const assert = require('assert');
+const net = require('net');
-var promise = require('../..').promise,
- portprober = require('../../net/portprober');
+const portprober = require('../../net/portprober');
describe('isFree', function() {
@@ -42,12 +41,12 @@ describe('isFree', function() {
server.listen(0, function() {
var port = server.address().port;
assertPortNotfree(port).then(function() {
- var done = promise.defer();
- server.close(function() {
- server = null;
- done.fulfill(assertPortIsFree(port));
+ return new Promise(resolve => {
+ server.close(function() {
+ server = null;
+ resolve(assertPortIsFree(port));
+ });
});
- return done.promise;
}).then(function() { done(); }, done);
});
});
@@ -57,12 +56,12 @@ describe('isFree', function() {
server.listen(0, host, function() {
var port = server.address().port;
assertPortNotfree(port, host).then(function() {
- var done = promise.defer();
- server.close(function() {
- server = null;
- done.fulfill(assertPortIsFree(port, host));
+ return new Promise(resolve => {
+ server.close(function() {
+ server = null;
+ resolve(assertPortIsFree(port, host));
+ });
});
- return done.promise;
}).then(function() { done(); }, done);
});
});
@@ -86,12 +85,12 @@ describe('findFreePort', function() {
portprober.findFreePort().then(function(port) {
server.listen(port, function() {
assertPortNotfree(port).then(function() {
- var done = promise.defer();
- server.close(function() {
- server = null;
- done.fulfill(assertPortIsFree(port));
+ return new Promise(resolve => {
+ server.close(function() {
+ server = null;
+ resolve(assertPortIsFree(port));
+ });
});
- return done.promise;
}).then(function() { done(); }, done);
});
});
@@ -102,12 +101,12 @@ describe('findFreePort', function() {
portprober.findFreePort(host).then(function(port) {
server.listen(port, host, function() {
assertPortNotfree(port, host).then(function() {
- var done = promise.defer();
- server.close(function() {
- server = null;
- done.fulfill(assertPortIsFree(port, host));
+ return new Promise(resolve => {
+ server.close(function() {
+ server = null;
+ resolve(assertPortIsFree(port, host));
+ });
});
- return done.promise;
}).then(function() { done(); }, done);
});
});
diff --git a/node_modules/selenium-webdriver/test/page_loading_test.js b/node_modules/selenium-webdriver/test/page_loading_test.js
index 098460370..1f09db5b5 100644
--- a/node_modules/selenium-webdriver/test/page_loading_test.js
+++ b/node_modules/selenium-webdriver/test/page_loading_test.js
@@ -30,122 +30,136 @@ test.suite(function(env) {
var browsers = env.browsers;
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
+ });
+
+ test.beforeEach(function*() {
+ if (!driver) {
+ driver = yield env.builder().build();
+ }
});
test.after(function() {
- driver.quit();
+ if (driver) {
+ return driver.quit();
+ }
});
- test.it('should wait for document to be loaded', function() {
- driver.get(Pages.simpleTestPage);
- assert(driver.getTitle()).equalTo('Hello WebDriver');
+ test.it('should wait for document to be loaded', function*() {
+ yield driver.get(Pages.simpleTestPage);
+ return assert(driver.getTitle()).equalTo('Hello WebDriver');
});
test.it('should follow redirects sent in the http response headers',
- function() {
- driver.get(Pages.redirectPage);
- assert(driver.getTitle()).equalTo('We Arrive Here');
+ function*() {
+ yield driver.get(Pages.redirectPage);
+ return assert(driver.getTitle()).equalTo('We Arrive Here');
});
- test.it('should follow meta redirects', function() {
- driver.get(Pages.metaRedirectPage);
- assert(driver.getTitle()).equalTo('We Arrive Here');
+ test.ignore(browsers(Browser.SAFARI)).
+ it('should follow meta redirects', function*() {
+ yield driver.get(Pages.metaRedirectPage);
+ return assert(driver.getTitle()).equalTo('We Arrive Here');
});
// Skip Firefox; see https://bugzilla.mozilla.org/show_bug.cgi?id=1280300
test.ignore(browsers(Browser.FIREFOX)).
- it('should be able to get a fragment on the current page', function() {
- driver.get(Pages.xhtmlTestPage);
- driver.get(Pages.xhtmlTestPage + '#text');
- driver.findElement(By.id('id1'));
+ it('should be able to get a fragment on the current page', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield driver.get(Pages.xhtmlTestPage + '#text');
+ yield driver.findElement(By.id('id1'));
});
test.ignore(browsers(Browser.IPAD, Browser.IPHONE)).
- it('should wait for all frames to load in a frameset', function() {
- driver.get(Pages.framesetPage);
- driver.switchTo().frame(0);
+ it('should wait for all frames to load in a frameset', function*() {
+ yield driver.get(Pages.framesetPage);
+ yield driver.switchTo().frame(0);
- driver.findElement(By.css('span#pageNumber')).getText().then(function(txt) {
- assert(txt.trim()).equalTo('1');
- });
+ let txt = yield driver.findElement(By.css('span#pageNumber')).getText();
+ assert(txt.trim()).equalTo('1');
- driver.switchTo().defaultContent();
- driver.switchTo().frame(1);
- driver.findElement(By.css('span#pageNumber')).getText().then(function(txt) {
- assert(txt.trim()).equalTo('2');
- });
+ yield driver.switchTo().defaultContent();
+ yield driver.switchTo().frame(1);
+ txt = yield driver.findElement(By.css('span#pageNumber')).getText();
+
+ assert(txt.trim()).equalTo('2');
});
test.ignore(browsers(Browser.SAFARI)).
- it('should be able to navigate back in browser history', function() {
- driver.get(Pages.formPage);
+ it('should be able to navigate back in browser history', function*() {
+ yield driver.get(Pages.formPage);
- driver.findElement(By.id('imageButton')).click();
- driver.wait(until.titleIs('We Arrive Here'), 2500);
+ yield driver.findElement(By.id('imageButton')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 2500);
- driver.navigate().back();
- driver.wait(until.titleIs('We Leave From Here'), 2500);
+ yield driver.navigate().back();
+ yield driver.wait(until.titleIs('We Leave From Here'), 2500);
});
test.ignore(browsers(Browser.SAFARI)).
- it('should be able to navigate back in presence of iframes', function() {
- driver.get(Pages.xhtmlTestPage);
+ it('should be able to navigate back in presence of iframes', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- driver.findElement(By.name('sameWindow')).click();
- driver.wait(until.titleIs('This page has iframes'), 2500);
+ yield driver.findElement(By.name('sameWindow')).click();
+ yield driver.wait(until.titleIs('This page has iframes'), 2500);
- driver.navigate().back();
- driver.wait(until.titleIs('XHTML Test Page'), 2500);
+ yield driver.navigate().back();
+ yield driver.wait(until.titleIs('XHTML Test Page'), 2500);
});
test.ignore(browsers(Browser.SAFARI)).
- it('should be able to navigate forwards in browser history', function() {
- driver.get(Pages.formPage);
+ it('should be able to navigate forwards in browser history', function*() {
+ yield driver.get(Pages.formPage);
- driver.findElement(By.id('imageButton')).click();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ yield driver.findElement(By.id('imageButton')).click();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
- driver.navigate().back();
- driver.wait(until.titleIs('We Leave From Here'), 5000);
+ yield driver.navigate().back();
+ yield driver.wait(until.titleIs('We Leave From Here'), 5000);
- driver.navigate().forward();
- driver.wait(until.titleIs('We Arrive Here'), 5000);
+ yield driver.navigate().forward();
+ yield driver.wait(until.titleIs('We Arrive Here'), 5000);
});
// PhantomJS 2.0 does not properly reload pages on refresh.
test.ignore(browsers(Browser.PHANTOM_JS)).
- it('should be able to refresh a page', function() {
- driver.get(Pages.xhtmlTestPage);
+ it('should be able to refresh a page', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
- driver.navigate().refresh();
+ yield driver.navigate().refresh();
- assert(driver.getTitle()).equalTo('XHTML Test Page');
+ yield assert(driver.getTitle()).equalTo('XHTML Test Page');
});
- test.it('should return title of page if set', function() {
- driver.get(Pages.xhtmlTestPage);
- assert(driver.getTitle()).equalTo('XHTML Test Page');
+ test.it('should return title of page if set', function*() {
+ yield driver.get(Pages.xhtmlTestPage);
+ yield assert(driver.getTitle()).equalTo('XHTML Test Page');
- driver.get(Pages.simpleTestPage);
- assert(driver.getTitle()).equalTo('Hello WebDriver');
+ yield driver.get(Pages.simpleTestPage);
+ yield assert(driver.getTitle()).equalTo('Hello WebDriver');
});
- // Only implemented in Firefox.
- test.ignore(browsers(
- Browser.CHROME,
- Browser.IE,
- Browser.IPAD,
- Browser.IPHONE,
- Browser.OPERA,
- Browser.PHANTOM_JS,
- Browser.SAFARI)).
- it('should timeout if page load timeout is set', function() {
- driver.call(function() {
- driver.manage().timeouts().pageLoadTimeout(1);
- driver.get(Pages.sleepingPage + '?time=3').
- then(function() {
+ describe('timeouts', function() {
+ test.afterEach(function() {
+ let nullDriver = () => driver = null;
+ if (driver) {
+ return driver.quit().then(nullDriver, nullDriver);
+ }
+ });
+
+ // Only implemented in Firefox.
+ test.ignore(browsers(
+ Browser.CHROME,
+ Browser.IE,
+ Browser.IPAD,
+ Browser.IPHONE,
+ Browser.OPERA,
+ Browser.PHANTOM_JS)).
+ it('should timeout if page load timeout is set', function*() {
+ yield driver.manage().timeouts().pageLoadTimeout(1);
+ return driver.get(Pages.sleepingPage + '?time=3')
+ .then(function() {
throw Error('Should have timed out on page load');
}, function(e) {
if (!(e instanceof error.ScriptTimeoutError)
@@ -153,14 +167,6 @@ test.suite(function(env) {
throw Error('Unexpected error response: ' + e);
}
});
- }).then(resetPageLoad, function(err) {
- resetPageLoad().finally(function() {
- throw err;
- });
});
-
- function resetPageLoad() {
- return driver.manage().timeouts().pageLoadTimeout(-1);
- }
});
});
diff --git a/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js b/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js
index 82a814a31..8b7a99f8d 100644
--- a/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js
+++ b/node_modules/selenium-webdriver/test/phantomjs/execute_phantomjs_test.js
@@ -24,49 +24,35 @@ var test = require('../../lib/test');
test.suite(function(env) {
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
test.after(function() {
- driver.quit();
+ return driver.quit();
});
var testPageUrl =
'data:text/html,<html><h1>' + path.basename(__filename) + '</h1></html>';
test.beforeEach(function() {
- driver.get(testPageUrl);
+ return driver.get(testPageUrl);
});
describe('phantomjs.Driver', function() {
describe('#executePhantomJS()', function() {
- test.it('can execute scripts using PhantomJS API', function() {
- return driver.executePhantomJS('return this.url;').then(function(url) {
- assert.equal(testPageUrl, decodeURIComponent(url));
- });
+ test.it('can execute scripts using PhantomJS API', function*() {
+ let url = yield driver.executePhantomJS('return this.url;');
+ assert.equal(testPageUrl, decodeURIComponent(url));
});
- test.it('can execute scripts as functions', function() {
- driver.executePhantomJS(function(a, b) {
+ test.it('can execute scripts as functions', function*() {
+ let result = yield driver.executePhantomJS(function(a, b) {
return a + b;
- }, 1, 2).then(function(result) {
- assert.equal(3, result);
- });
- });
+ }, 1, 2);
- test.it('can manipulate the current page', function() {
- driver.manage().addCookie({name: 'foo', value: 'bar'});
- driver.manage().getCookie('foo').then(function(cookie) {
- assert.equal('bar', cookie.value);
- });
- driver.executePhantomJS(function() {
- this.clearCookies();
- });
- driver.manage().getCookie('foo').then(function(cookie) {
- assert.equal(null, cookie);
- });
+ assert.equal(3, result);
});
});
});
diff --git a/node_modules/selenium-webdriver/test/proxy_test.js b/node_modules/selenium-webdriver/test/proxy_test.js
index c25565b48..442ff606e 100644
--- a/node_modules/selenium-webdriver/test/proxy_test.js
+++ b/node_modules/selenium-webdriver/test/proxy_test.js
@@ -96,7 +96,7 @@ test.suite(function(env) {
var driver;
test.beforeEach(function() { driver = null; });
- test.afterEach(function() { driver && driver.quit(); });
+ test.afterEach(function() { return driver && driver.quit(); });
function createDriver(proxy) {
// For Firefox we need to explicitly enable proxies for localhost by
@@ -104,7 +104,7 @@ test.suite(function(env) {
let profile = new firefox.Profile();
profile.setPreference('network.proxy.no_proxies_on', '');
- driver = env.builder()
+ return driver = env.builder()
.setFirefoxOptions(new firefox.Options().setProfile(profile))
.setProxy(proxy)
.build();
@@ -116,14 +116,14 @@ test.suite(function(env) {
// phantomjs 1.9.1 in webdriver mode does not appear to respect proxy
// settings.
test.ignore(env.browsers(Browser.PHANTOM_JS)).
- it('can configure HTTP proxy host', function() {
- createDriver(proxy.manual({
+ it('can configure HTTP proxy host', function*() {
+ yield createDriver(proxy.manual({
http: proxyServer.host()
}));
- driver.get(helloServer.url());
- assert(driver.getTitle()).equalTo('Proxy page');
- assert(driver.findElement({tagName: 'h3'}).getText()).
+ yield driver.get(helloServer.url());
+ yield assert(driver.getTitle()).equalTo('Proxy page');
+ yield assert(driver.findElement({tagName: 'h3'}).getText()).
equalTo('This is the proxy landing page');
});
@@ -134,20 +134,20 @@ test.suite(function(env) {
Browser.FIREFOX,
'legacy-' + Browser.FIREFOX,
Browser.PHANTOM_JS)).
- it('can bypass proxy for specific hosts', function() {
- createDriver(proxy.manual({
+ it('can bypass proxy for specific hosts', function*() {
+ yield createDriver(proxy.manual({
http: proxyServer.host(),
bypass: helloServer.host()
}));
- driver.get(helloServer.url());
- assert(driver.getTitle()).equalTo('Hello');
- assert(driver.findElement({tagName: 'h3'}).getText()).
+ yield driver.get(helloServer.url());
+ yield assert(driver.getTitle()).equalTo('Hello');
+ yield assert(driver.findElement({tagName: 'h3'}).getText()).
equalTo('Hello, world!');
- driver.get(goodbyeServer.url());
- assert(driver.getTitle()).equalTo('Proxy page');
- assert(driver.findElement({tagName: 'h3'}).getText()).
+ yield driver.get(goodbyeServer.url());
+ yield assert(driver.getTitle()).equalTo('Proxy page');
+ yield assert(driver.findElement({tagName: 'h3'}).getText()).
equalTo('This is the proxy landing page');
});
@@ -159,17 +159,17 @@ test.suite(function(env) {
test.ignore(env.browsers(
Browser.IE, Browser.OPERA, Browser.PHANTOM_JS, Browser.SAFARI)).
describe('pac proxy settings', function() {
- test.it('can configure proxy through PAC file', function() {
- createDriver(proxy.pac(proxyServer.url('/proxy.pac')));
+ test.it('can configure proxy through PAC file', function*() {
+ yield createDriver(proxy.pac(proxyServer.url('/proxy.pac')));
- driver.get(helloServer.url());
- assert(driver.getTitle()).equalTo('Proxy page');
- assert(driver.findElement({tagName: 'h3'}).getText()).
+ yield driver.get(helloServer.url());
+ yield assert(driver.getTitle()).equalTo('Proxy page');
+ yield assert(driver.findElement({tagName: 'h3'}).getText()).
equalTo('This is the proxy landing page');
- driver.get(goodbyeServer.url());
- assert(driver.getTitle()).equalTo('Goodbye');
- assert(driver.findElement({tagName: 'h3'}).getText()).
+ yield driver.get(goodbyeServer.url());
+ yield assert(driver.getTitle()).equalTo('Goodbye');
+ yield assert(driver.findElement({tagName: 'h3'}).getText()).
equalTo('Goodbye, world!');
});
});
diff --git a/node_modules/selenium-webdriver/test/remote_test.js b/node_modules/selenium-webdriver/test/remote_test.js
index 5edc448bb..9b2b2eb73 100644
--- a/node_modules/selenium-webdriver/test/remote_test.js
+++ b/node_modules/selenium-webdriver/test/remote_test.js
@@ -26,6 +26,8 @@ var promise = require('../').promise,
cmd = require('../lib/command'),
remote = require('../remote');
+const {enablePromiseManager} = require('../lib/test/promise');
+
describe('DriverService', function() {
describe('start()', function() {
var service;
@@ -41,36 +43,34 @@ describe('DriverService', function() {
return service.kill();
});
- it('fails if child-process dies', function(done) {
+ it('fails if child-process dies', function() {
this.timeout(1000);
- service.start(500)
- .then(expectFailure.bind(null, done), verifyFailure.bind(null, done));
+ return service.start(500).then(expectFailure, verifyFailure);
});
- it('failures propagate through control flow if child-process dies',
- function(done) {
- this.timeout(1000);
+ enablePromiseManager(function() {
+ describe(
+ 'failures propagate through control flow if child-process dies',
+ function() {
+ it('', function() {
+ this.timeout(1000);
- promise.controlFlow().execute(function() {
- promise.controlFlow().execute(function() {
- return service.start(500);
+ return promise.controlFlow().execute(function() {
+ promise.controlFlow().execute(function() {
+ return service.start(500);
+ });
+ }).then(expectFailure, verifyFailure);
+ });
});
- })
- .then(expectFailure.bind(null, done), verifyFailure.bind(null, done));
- });
+ });
- function verifyFailure(done, e) {
- try {
- assert.ok(!(e instanceof promise.CancellationError));
- assert.equal('Server terminated early with status 1', e.message);
- done();
- } catch (ex) {
- done(ex);
- }
+ function verifyFailure(e) {
+ assert.ok(!(e instanceof promise.CancellationError));
+ assert.equal('Server terminated early with status 1', e.message);
}
- function expectFailure(done) {
- done(Error('expected to fail'));
+ function expectFailure() {
+ throw Error('expected to fail');
}
});
});
diff --git a/node_modules/selenium-webdriver/test/safari_test.js b/node_modules/selenium-webdriver/test/safari_test.js
index 41525d9cb..6032b865d 100644
--- a/node_modules/selenium-webdriver/test/safari_test.js
+++ b/node_modules/selenium-webdriver/test/safari_test.js
@@ -43,13 +43,11 @@ describe('safari.Options', function() {
logPrefs = {},
caps = webdriver.Capabilities.chrome()
.set(webdriver.Capability.PROXY, proxyPrefs)
- .set(webdriver.Capability.LOGGING_PREFS, logPrefs)
- .set('legacyDriver', true);
+ .set(webdriver.Capability.LOGGING_PREFS, logPrefs);
let options = safari.Options.fromCapabilities(caps);
assert(options.proxy_).equalTo(proxyPrefs);
assert(options.logPrefs_).equalTo(logPrefs);
- assert(options.legacyDriver_).equalTo(true);
});
});
@@ -80,13 +78,11 @@ describe('safari.Options', function() {
options
.setLoggingPrefs(loggingPrefs)
- .setProxy(proxyPrefs)
- .useLegacyDriver(true);
+ .setProxy(proxyPrefs);
let caps = options.toCapabilities();
assert(caps.get('proxy')).equalTo(proxyPrefs);
assert(caps.get('loggingPrefs')).equalTo(loggingPrefs);
- assert(caps.get('legacyDriver')).equalTo(true);
});
});
});
@@ -95,13 +91,13 @@ test.suite(function(env) {
describe('safaridriver', function() {
let service;
- test.afterEach(function() {
+ afterEach(function() {
if (service) {
return service.kill();
}
});
- test.it('can start safaridriver', function() {
+ it('can start safaridriver', function() {
service = new safari.ServiceBuilder().build();
return service.start().then(function(url) {
diff --git a/node_modules/selenium-webdriver/test/session_test.js b/node_modules/selenium-webdriver/test/session_test.js
index 1fb7475b4..546cd7fe9 100644
--- a/node_modules/selenium-webdriver/test/session_test.js
+++ b/node_modules/selenium-webdriver/test/session_test.js
@@ -27,27 +27,28 @@ test.suite(function(env) {
var browsers = env.browsers;
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
test.after(function() {
- driver.quit();
+ return driver.quit();
});
- test.it('can connect to an existing session', function() {
- driver.get(Pages.simpleTestPage);
- assert(driver.getTitle()).equalTo('Hello WebDriver');
+ test.it('can connect to an existing session', function*() {
+ yield driver.get(Pages.simpleTestPage);
+ yield assert(driver.getTitle()).equalTo('Hello WebDriver');
return driver.getSession().then(session1 => {
let driver2 = WebDriver.attachToSession(
driver.getExecutor(),
session1.getId());
- assert(driver2.getTitle()).equalTo('Hello WebDriver');
-
- let session2Id = driver2.getSession().then(s => s.getId());
- assert(session2Id).equalTo(session1.getId());
+ return assert(driver2.getTitle()).equalTo('Hello WebDriver')
+ .then(_ => {
+ let session2Id = driver2.getSession().then(s => s.getId());
+ return assert(session2Id).equalTo(session1.getId());
+ });
});
});
});
diff --git a/node_modules/selenium-webdriver/test/stale_element_test.js b/node_modules/selenium-webdriver/test/stale_element_test.js
index 6ab8de7ec..d00b5d440 100644
--- a/node_modules/selenium-webdriver/test/stale_element_test.js
+++ b/node_modules/selenium-webdriver/test/stale_element_test.js
@@ -30,31 +30,33 @@ var Browser = require('..').Browser,
test.suite(function(env) {
var driver;
- test.before(function() { driver = env.builder().build(); });
- test.after(function() { driver.quit(); });
+ test.before(function*() { driver = yield env.builder().build(); });
+ test.after(function() { return driver.quit(); });
- test.it(
+ // Element never goes stale in Safari.
+ test.ignore(env.browsers(Browser.SAFARI)).
+ it(
'dynamically removing elements from the DOM trigger a ' +
'StaleElementReferenceError',
- function() {
- driver.get(Pages.javascriptPage);
+ function*() {
+ yield driver.get(Pages.javascriptPage);
- var toBeDeleted = driver.findElement(By.id('deleted'));
- assert(toBeDeleted.isDisplayed()).isTrue();
+ var toBeDeleted = yield driver.findElement(By.id('deleted'));
+ yield assert(toBeDeleted.getTagName()).isEqualTo('p');
- driver.findElement(By.id('delete')).click();
- driver.wait(until.stalenessOf(toBeDeleted), 5000);
+ yield driver.findElement(By.id('delete')).click();
+ yield driver.wait(until.stalenessOf(toBeDeleted), 5000);
});
- test.it('an element found in a different frame is stale', function() {
- driver.get(Pages.missedJsReferencePage);
+ test.it('an element found in a different frame is stale', function*() {
+ yield driver.get(Pages.missedJsReferencePage);
- var frame = driver.findElement(By.css('iframe[name="inner"]'));
- driver.switchTo().frame(frame);
+ var frame = yield driver.findElement(By.css('iframe[name="inner"]'));
+ yield driver.switchTo().frame(frame);
- var el = driver.findElement(By.id('oneline'));
- driver.switchTo().defaultContent();
- el.getText().then(fail, function(e) {
+ var el = yield driver.findElement(By.id('oneline'));
+ yield driver.switchTo().defaultContent();
+ return el.getText().then(fail, function(e) {
assert(e).instanceOf(error.StaleElementReferenceError);
});
});
diff --git a/node_modules/selenium-webdriver/test/tag_name_test.js b/node_modules/selenium-webdriver/test/tag_name_test.js
index d5e18a9a2..b934e6bb3 100644
--- a/node_modules/selenium-webdriver/test/tag_name_test.js
+++ b/node_modules/selenium-webdriver/test/tag_name_test.js
@@ -24,11 +24,13 @@ var By = require('..').By,
test.suite(function(env) {
var driver;
- test.after(function() { driver.quit(); });
+ test.after(function() { return driver.quit(); });
- test.it('should return lower case tag name', function() {
- driver = env.builder().build();
- driver.get(test.Pages.formPage);
- assert(driver.findElement(By.id('cheese')).getTagName()).equalTo('input');
+ test.it('should return lower case tag name', function*() {
+ driver = yield env.builder().build();
+ yield driver.get(test.Pages.formPage);
+
+ let el = yield driver.findElement(By.id('cheese'));
+ return assert(el.getTagName()).equalTo('input');
});
});
diff --git a/node_modules/selenium-webdriver/test/testing/assert_test.js b/node_modules/selenium-webdriver/test/testing/assert_test.js
index 8c8848254..eaced8bf1 100644
--- a/node_modules/selenium-webdriver/test/testing/assert_test.js
+++ b/node_modules/selenium-webdriver/test/testing/assert_test.js
@@ -178,9 +178,8 @@ describe('assert', function() {
});
it('waits for promised values', function() {
- let d = Promise.defer();
- setTimeout(() => d.resolve(123), 10);
- return assert(d.promise).closeTo(124, 1);
+ let p = new Promise(resolve => setTimeout(() => resolve(123), 10));
+ return assert(p).closeTo(124, 1);
});
});
diff --git a/node_modules/selenium-webdriver/test/testing/index_test.js b/node_modules/selenium-webdriver/test/testing/index_test.js
index 31acff238..edc841bbe 100644
--- a/node_modules/selenium-webdriver/test/testing/index_test.js
+++ b/node_modules/selenium-webdriver/test/testing/index_test.js
@@ -17,8 +17,10 @@
'use strict';
-var assert = require('assert');
-var promise = require('../..').promise;
+const assert = require('assert');
+const promise = require('../..').promise;
+const {enablePromiseManager} = require('../../lib/test/promise');
+
var test = require('../../testing');
@@ -42,137 +44,181 @@ describe('Mocha Integration', function() {
afterEach(function() { assert.equal(this.x, 2); });
});
- describe('timeout handling', function() {
- describe('it does not reset the control flow on a non-timeout', function() {
- var flowReset = false;
+ enablePromiseManager(function() {
+ describe('timeout handling', function() {
+ describe('it does not reset the control flow on a non-timeout', function() {
+ var flowReset = false;
- beforeEach(function() {
- flowReset = false;
- test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
- });
+ beforeEach(function() {
+ flowReset = false;
+ test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
+ });
- test.it('', function() {
- this.timeout(100);
- return promise.delayed(50);
- });
+ test.it('', function() {
+ this.timeout(100);
+ return promise.delayed(50);
+ });
- afterEach(function() {
- assert.ok(!flowReset);
- test.controlFlow().removeListener(
- promise.ControlFlow.EventType.RESET, onreset);
- });
+ afterEach(function() {
+ assert.ok(!flowReset);
+ test.controlFlow().removeListener(
+ promise.ControlFlow.EventType.RESET, onreset);
+ });
- function onreset() {
- flowReset = true;
- }
- });
+ function onreset() {
+ flowReset = true;
+ }
+ });
- describe('it resets the control flow after a timeout' ,function() {
- var timeoutErr, flowReset;
+ describe('it resets the control flow after a timeout' ,function() {
+ var timeoutErr, flowReset;
- beforeEach(function() {
- flowReset = false;
- test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
- });
+ beforeEach(function() {
+ flowReset = false;
+ test.controlFlow().once(promise.ControlFlow.EventType.RESET, onreset);
+ });
- test.it('', function() {
- var callback = this.runnable().callback;
- var test = this;
- this.runnable().callback = function(err) {
- timeoutErr = err;
- // Reset our timeout to 0 so Mocha does not fail the test.
- test.timeout(0);
- // When we invoke the real callback, do not pass along the error so
- // Mocha does not fail the test.
- return callback.call(this);
- };
-
- test.timeout(50);
- return promise.defer().promise;
- });
+ test.it('', function() {
+ var callback = this.runnable().callback;
+ var test = this;
+ this.runnable().callback = function(err) {
+ timeoutErr = err;
+ // Reset our timeout to 0 so Mocha does not fail the test.
+ test.timeout(0);
+ // When we invoke the real callback, do not pass along the error so
+ // Mocha does not fail the test.
+ return callback.call(this);
+ };
+
+ test.timeout(50);
+ return promise.defer().promise;
+ });
- afterEach(function() {
- return Promise.resolve().then(function() {
- test.controlFlow().removeListener(
- promise.ControlFlow.EventType.RESET, onreset);
- assert.ok(flowReset, 'control flow was not reset after a timeout');
+ afterEach(function() {
+ return Promise.resolve().then(function() {
+ test.controlFlow().removeListener(
+ promise.ControlFlow.EventType.RESET, onreset);
+ assert.ok(flowReset, 'control flow was not reset after a timeout');
+ });
});
+
+ function onreset() {
+ flowReset = true;
+ }
});
+ });
+
+ describe('async "done" support', function() {
+ this.timeout(2*1000);
+
+ var waited = false;
+ var DELAY = 100; // ms enough to notice
+
+ // Each test asynchronously sets waited to true, so clear/check waited
+ // before/after:
+ beforeEach(function() {
+ waited = false;
+ });
+
+ afterEach(function() {
+ assert.strictEqual(waited, true);
+ });
+
+ // --- First, vanilla mocha "it" should support the "done" callback correctly.
+
+ // This 'it' should block until 'done' is invoked
+ it('vanilla delayed', function(done) {
+ setTimeout(function delayedVanillaTimeout() {
+ waited = true;
+ done();
+ }, DELAY);
+ });
+
+ // --- Now with the webdriver wrappers for 'it' should support the "done" callback:
+
+ test.it('delayed', function(done) {
+ assert(done);
+ assert.strictEqual(typeof done, 'function');
+ setTimeout(function delayedTimeoutCallback() {
+ waited = true;
+ done();
+ }, DELAY);
+ });
+
+ // --- And test that the webdriver wrapper for 'it' works with a returned promise, too:
+
+ test.it('delayed by promise', function() {
+ var defer = promise.defer();
+ setTimeout(function delayedPromiseCallback() {
+ waited = true;
+ defer.fulfill('ignored');
+ });
+ return defer.promise;
+ });
+ });
- function onreset() {
- flowReset = true;
- }
+ describe('ControlFlow and "done" work together', function() {
+ var flow, order;
+ before(function() {
+ order = [];
+ flow = test.controlFlow();
+ flow.execute(function() { order.push(1); });
+ });
+
+ test.it('control flow updates and async done', function(done) {
+ flow.execute(function() { order.push(2); });
+ flow.execute(function() { order.push(3); }).then(function() {
+ order.push(4);
+ });
+ done();
+ });
+
+ after(function() {
+ assert.deepEqual([1, 2, 3, 4], order);
+ });
});
});
-});
-describe('Mocha async "done" support', function() {
- this.timeout(2*1000);
-
- var waited = false;
- var DELAY = 100; // ms enough to notice
-
- // Each test asynchronously sets waited to true, so clear/check waited
- // before/after:
- beforeEach(function() {
- waited = false;
- });
-
- afterEach(function() {
- assert.strictEqual(waited, true);
- });
-
- // --- First, vanilla mocha "it" should support the "done" callback correctly.
-
- // This 'it' should block until 'done' is invoked
- it('vanilla delayed', function(done) {
- setTimeout(function delayedVanillaTimeout() {
- waited = true;
- done();
- }, DELAY);
- });
-
- // --- Now with the webdriver wrappers for 'it' should support the "done" callback:
-
- test.it('delayed', function(done) {
- assert(done);
- assert.strictEqual(typeof done, 'function');
- setTimeout(function delayedTimeoutCallback() {
- waited = true;
- done();
- }, DELAY);
- });
-
- // --- And test that the webdriver wrapper for 'it' works with a returned promise, too:
-
- test.it('delayed by promise', function() {
- var defer = promise.defer();
- setTimeout(function delayedPromiseCallback() {
- waited = true;
- defer.fulfill('ignored');
- });
- return defer.promise;
- });
+ describe('generator support', function() {
+ let arr;
-});
+ beforeEach(() => arr = []);
+ afterEach(() => assert.deepEqual(arr, [0, 1, 2, 3]));
+
+ test.it('sync generator', function* () {
+ arr.push(yield arr.length);
+ arr.push(yield arr.length);
+ arr.push(yield arr.length);
+ arr.push(yield arr.length);
+ });
+
+ test.it('async generator', function* () {
+ arr.push(yield Promise.resolve(arr.length));
+ arr.push(yield Promise.resolve(arr.length));
+ arr.push(yield Promise.resolve(arr.length));
+ arr.push(yield Promise.resolve(arr.length));
+ });
+
+ test.it('generator returns promise', function*() {
+ arr.push(yield Promise.resolve(arr.length));
+ arr.push(yield Promise.resolve(arr.length));
+ arr.push(yield Promise.resolve(arr.length));
+ setTimeout(_ => arr.push(arr.length), 10);
+ return new Promise((resolve) => setTimeout(_ => resolve(), 25));
+ });
-describe('ControlFlow and "done" work together', function() {
- var flow, order;
- before(function() {
- order = [];
- flow = test.controlFlow();
- flow.execute(function() { order.push(1); });
- });
-
- test.it('control flow updates and async done', function(done) {
- flow.execute(function() { order.push(2); });
- flow.execute(function() { order.push(3); }).then(function() {
- order.push(4);
+ describe('generator runs with proper "this" context', () => {
+ before(function() { this.values = [0, 1, 2, 3]; });
+ test.it('', function*() {
+ arr = this.values;
});
- done();
- })
+ });
- after(function() {
- assert.deepEqual([1, 2, 3, 4], order);
- })
+ it('generator function must not take a callback', function() {
+ arr = [0, 1, 2, 3]; // For teardown hook.
+ assert.throws(_ => {
+ test.it('', function*(done){});
+ }, TypeError);
+ });
+ });
});
diff --git a/node_modules/selenium-webdriver/test/upload_test.js b/node_modules/selenium-webdriver/test/upload_test.js
index 3329f7ca7..c677550fc 100644
--- a/node_modules/selenium-webdriver/test/upload_test.js
+++ b/node_modules/selenium-webdriver/test/upload_test.js
@@ -41,13 +41,13 @@ test.suite(function(env) {
})
var driver;
- test.before(function() {
- driver = env.builder().build();
+ test.before(function*() {
+ driver = yield env.builder().build();
});
test.after(function() {
if (driver) {
- driver.quit();
+ return driver.quit();
}
});
@@ -58,29 +58,29 @@ test.suite(function(env) {
// See https://github.com/ariya/phantomjs/issues/12506
Browser.PHANTOM_JS,
Browser.SAFARI)).
- it('can upload files', function() {
+ it('can upload files', function*() {
driver.setFileDetector(new remote.FileDetector);
- driver.get(Pages.uploadPage);
+ yield driver.get(Pages.uploadPage);
- var fp = driver.call(function() {
+ var fp = yield driver.call(function() {
return io.tmpFile().then(function(fp) {
fs.writeFileSync(fp, FILE_HTML);
return fp;
});
});
- driver.findElement(By.id('upload')).sendKeys(fp);
- driver.findElement(By.id('go')).click();
+ yield driver.findElement(By.id('upload')).sendKeys(fp);
+ yield driver.findElement(By.id('go')).click();
// Uploading files across a network may take a while, even if they're small.
- var label = driver.findElement(By.id('upload_label'));
- driver.wait(until.elementIsNotVisible(label),
+ var label = yield driver.findElement(By.id('upload_label'));
+ yield driver.wait(until.elementIsNotVisible(label),
10 * 1000, 'File took longer than 10 seconds to upload!');
- var frame = driver.findElement(By.id('upload_target'));
- driver.switchTo().frame(frame);
- assert(driver.findElement(By.css('body')).getText())
+ var frame = yield driver.findElement(By.id('upload_target'));
+ yield driver.switchTo().frame(frame);
+ yield assert(driver.findElement(By.css('body')).getText())
.equalTo(LOREM_IPSUM_TEXT);
});
});
diff --git a/node_modules/selenium-webdriver/test/window_test.js b/node_modules/selenium-webdriver/test/window_test.js
index 6213d19ac..73cdd802d 100644
--- a/node_modules/selenium-webdriver/test/window_test.js
+++ b/node_modules/selenium-webdriver/test/window_test.js
@@ -26,113 +26,99 @@ var Browser = require('..').Browser,
test.suite(function(env) {
var driver;
- test.before(function() { driver = env.builder().build(); });
- test.after(function() { driver.quit(); });
+ test.before(function*() { driver = yield env.builder().build(); });
+ test.after(function() { return driver.quit(); });
test.beforeEach(function() {
- driver.switchTo().defaultContent();
+ return driver.switchTo().defaultContent();
});
- test.it('can set size of the current window', function() {
- driver.get(test.Pages.echoPage);
- changeSizeBy(-20, -20);
+ test.it('can set size of the current window', function*() {
+ yield driver.get(test.Pages.echoPage);
+ yield changeSizeBy(-20, -20);
});
- test.it('can set size of the current window from frame', function() {
- driver.get(test.Pages.framesetPage);
+ test.it('can set size of the current window from frame', function*() {
+ yield driver.get(test.Pages.framesetPage);
- var frame = driver.findElement({css: 'frame[name="fourth"]'});
- driver.switchTo().frame(frame);
- changeSizeBy(-20, -20);
+ var frame = yield driver.findElement({css: 'frame[name="fourth"]'});
+ yield driver.switchTo().frame(frame);
+ yield changeSizeBy(-20, -20);
});
- test.it('can set size of the current window from iframe', function() {
- driver.get(test.Pages.iframePage);
+ test.it('can set size of the current window from iframe', function*() {
+ yield driver.get(test.Pages.iframePage);
- var frame = driver.findElement({css: 'iframe[name="iframe1-name"]'});
- driver.switchTo().frame(frame);
- changeSizeBy(-20, -20);
+ var frame = yield driver.findElement({css: 'iframe[name="iframe1-name"]'});
+ yield driver.switchTo().frame(frame);
+ yield changeSizeBy(-20, -20);
});
- test.it('can switch to a new window', function() {
- driver.get(test.Pages.xhtmlTestPage);
+ test.it('can switch to a new window', function*() {
+ yield driver.get(test.Pages.xhtmlTestPage);
- driver.getWindowHandle().then(function(handle) {
- driver.getAllWindowHandles().then(function(originalHandles) {
- driver.findElement(By.linkText("Open new window")).click();
+ let handle = yield driver.getWindowHandle();
+ let originalHandles = yield driver.getAllWindowHandles();
- driver.wait(forNewWindowToBeOpened(originalHandles), 2000);
+ yield driver.findElement(By.linkText("Open new window")).click();
+ yield driver.wait(forNewWindowToBeOpened(originalHandles), 2000);
+ yield assert(driver.getTitle()).equalTo("XHTML Test Page");
- assert(driver.getTitle()).equalTo("XHTML Test Page");
+ let newHandle = yield getNewWindowHandle(originalHandles);
- getNewWindowHandle(originalHandles).then(function(newHandle) {
- driver.switchTo().window(newHandle);
-
- assert(driver.getTitle()).equalTo("We Arrive Here")
- });
- });
- });
+ yield driver.switchTo().window(newHandle);
+ yield assert(driver.getTitle()).equalTo("We Arrive Here");
});
- // See https://github.com/mozilla/geckodriver/issues/113
- test.ignore(env.browsers(Browser.FIREFOX)).
- it('can set the window position of the current window', function() {
- driver.manage().window().getPosition().then(function(position) {
- driver.manage().window().setSize(640, 480);
- driver.manage().window().setPosition(position.x + 10, position.y + 10);
-
- // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
- if (env.currentBrowser() === Browser.PHANTOM_JS) {
- driver.manage().window().getPosition().then(function(position) {
- assert(position.x).equalTo(0);
- assert(position.y).equalTo(0);
- });
- } else {
- var dx = position.x + 10;
- var dy = position.y + 10;
- // On OSX, Safari position's the window relative to below the menubar
- // at the top of the screen, which is 23 pixels tall.
- if (env.currentBrowser() === Browser.SAFARI &&
- process.platform === 'darwin') {
- dy += 23;
- }
- }
- });
+ test.it('can set the window position of the current window', function*() {
+ let position = yield driver.manage().window().getPosition();
+
+ yield driver.manage().window().setSize(640, 480);
+ yield driver.manage().window().setPosition(position.x + 10, position.y + 10);
+
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
+ position = yield driver.manage().window().getPosition();
+ assert(position.x).equalTo(0);
+ assert(position.y).equalTo(0);
+ } else {
+ var dx = position.x + 10;
+ var dy = position.y + 10;
+ return driver.wait(forPositionToBe(dx, dy), 1000);
+ }
});
- // See https://github.com/mozilla/geckodriver/issues/113
- test.ignore(env.browsers(Browser.FIREFOX)).
- it('can set the window position from a frame', function() {
- driver.get(test.Pages.iframePage);
- driver.switchTo().frame('iframe1-name');
- driver.manage().window().getPosition().then(function(position) {
- driver.manage().window().setSize(640, 480);
- driver.manage().window().setPosition(position.x + 10, position.y + 10);
-
- // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
- if (env.currentBrowser() === Browser.PHANTOM_JS) {
- driver.manage().window().getPosition().then(function(position) {
- assert(position.x).equalTo(0);
- assert(position.y).equalTo(0);
- });
- } else {
- var dx = position.x + 10;
- var dy = position.y + 10;
- // On OSX, Safari position's the window relative to below the menubar
- // at the top of the screen, which is 23 pixels tall.
- if (env.currentBrowser() === Browser.SAFARI &&
- process.platform === 'darwin') {
- dy += 23;
- }
- driver.wait(forPositionToBe(dx, dy), 1000);
- }
- });
+ test.it('can set the window position from a frame', function*() {
+ yield driver.get(test.Pages.iframePage);
+
+ let frame = yield driver.findElement(By.name('iframe1-name'));
+ yield driver.switchTo().frame(frame);
+
+ let position = yield driver.manage().window().getPosition();
+ yield driver.manage().window().setSize(640, 480);
+ yield driver.manage().window().setPosition(position.x + 10, position.y + 10);
+
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
+ return driver.manage().window().getPosition().then(function(position) {
+ assert(position.x).equalTo(0);
+ assert(position.y).equalTo(0);
+ });
+ } else {
+ var dx = position.x + 10;
+ var dy = position.y + 10;
+ return driver.wait(forPositionToBe(dx, dy), 1000);
+ }
});
function changeSizeBy(dx, dy) {
- driver.manage().window().getSize().then(function(size) {
- driver.manage().window().setSize(size.width + dx, size.height + dy);
- driver.wait(forSizeToBe(size.width + dx, size.height + dy), 1000);
+ return driver.manage().window().getSize().then(function(size) {
+ return driver.manage().window()
+ .setSize(size.width + dx, size.height + dy)
+ .then(_ => {
+ return driver.wait(
+ forSizeToBe(size.width + dx, size.height + dy), 1000);
+ });
});
}
diff --git a/node_modules/selenium-webdriver/testing/index.js b/node_modules/selenium-webdriver/testing/index.js
index 0e9d06232..5bb82d15f 100644
--- a/node_modules/selenium-webdriver/testing/index.js
+++ b/node_modules/selenium-webdriver/testing/index.js
@@ -28,6 +28,16 @@
* - it.skip
* - xit
*
+ * Each of the wrapped functions support generator functions. If the generator
+ * {@linkplain ../lib/promise.consume yields a promise}, the test will wait
+ * for that promise to resolve before invoking the next iteration of the
+ * generator:
+ *
+ * test.it('generators', function*() {
+ * let x = yield Promise.resolve(1);
+ * assert.equal(x, 1);
+ * });
+ *
* The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
* to simplify writing asynchronous tests:
*
@@ -66,8 +76,22 @@
* function maybe() { return Math.random() < 0.5; }
*/
-var promise = require('..').promise;
-var flow = promise.controlFlow();
+'use strict';
+
+const promise = require('..').promise;
+const flow = (function() {
+ const initial = process.env['SELENIUM_PROMISE_MANAGER'];
+ try {
+ process.env['SELENIUM_PROMISE_MANAGER'] = '1';
+ return promise.controlFlow();
+ } finally {
+ if (initial === undefined) {
+ delete process.env['SELENIUM_PROMISE_MANAGER'];
+ } else {
+ process.env['SELENIUM_PROMISE_MANAGER'] = initial;
+ }
+ }
+})();
/**
@@ -119,13 +143,38 @@ function wrapArgument(value) {
* Should preserve the semantics of Mocha's Runnable.prototype.run (See
* https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
*
- * @param {Function} fn
- * @return {Function}
+ * @param {!Function} fn
+ * @return {!Function}
*/
function makeAsyncTestFn(fn) {
- var async = fn.length > 0; // if test function expects a callback, its "async"
+ const isAsync = fn.length > 0;
+ const isGenerator = promise.isGenerator(fn);
+ if (isAsync && isGenerator) {
+ throw TypeError(
+ 'generator-based tests must not take a callback; for async testing,'
+ + ' return a promise (or yield on a promise)');
+ }
var ret = /** @type {function(this: mocha.Context)}*/ (function(done) {
+ const runTest = (resolve, reject) => {
+ try {
+ if (isAsync) {
+ fn.call(this, err => err ? reject(err) : resolve());
+ } else if (isGenerator) {
+ resolve(promise.consume(fn, this));
+ } else {
+ resolve(fn.call(this));
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ };
+
+ if (!promise.USE_PROMISE_MANAGER) {
+ new Promise(runTest).then(seal(done), done);
+ return;
+ }
+
var runnable = this.runnable();
var mochaCallback = runnable.callback;
runnable.callback = function() {
@@ -133,25 +182,9 @@ function makeAsyncTestFn(fn) {
return mochaCallback.apply(this, arguments);
};
- var testFn = fn.bind(this);
flow.execute(function controlFlowExecute() {
return new promise.Promise(function(fulfill, reject) {
- if (async) {
- // If testFn is async (it expects a done callback), resolve the promise of this
- // test whenever that callback says to. Any promises returned from testFn are
- // ignored.
- testFn(function testFnDoneCallback(err) {
- if (err) {
- reject(err);
- } else {
- fulfill();
- }
- });
- } else {
- // Without a callback, testFn can return a promise, or it will
- // be assumed to have completed synchronously
- fulfill(testFn());
- }
+ return runTest(fulfill, reject);
}, flow);
}, runnable.fullTitle()).then(seal(done), done);
});
@@ -196,6 +229,45 @@ function ignore(predicateFn) {
}
+/**
+ * @param {string} name
+ * @return {!Function}
+ * @throws {TypeError}
+ */
+function getMochaGlobal(name) {
+ let fn = global[name];
+ let type = typeof fn;
+ if (type !== 'function') {
+ throw TypeError(
+ `Expected global.${name} to be a function, but is ${type}. `
+ + 'This can happen if you try using this module when running '
+ + 'with node directly instead of using the mocha executable');
+ }
+ return fn;
+}
+
+
+const WRAPPED = {
+ after: null,
+ afterEach: null,
+ before: null,
+ beforeEach: null,
+ it: null,
+ itOnly: null,
+ xit: null
+};
+
+
+function wrapIt() {
+ if (!WRAPPED.it) {
+ let it = getMochaGlobal('it');
+ WRAPPED.it = wrapped(it);
+ WRAPPED.itOnly = wrapped(it.only);
+ }
+}
+
+
+
// PUBLIC API
@@ -211,67 +283,134 @@ exports.controlFlow = function(){
/**
* Registers a new test suite.
* @param {string} name The suite name.
- * @param {function()=} fn The suite function, or {@code undefined} to define
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
* a pending test suite.
*/
-exports.describe = global.describe;
+exports.describe = function(name, opt_fn) {
+ let fn = getMochaGlobal('describe');
+ return opt_fn ? fn(name, opt_fn) : fn(name);
+};
+
/**
* Defines a suppressed test suite.
* @param {string} name The suite name.
- * @param {function()=} fn The suite function, or {@code undefined} to define
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
* a pending test suite.
*/
-exports.xdescribe = global.xdescribe;
-exports.describe.skip = global.describe.skip;
+exports.describe.skip = function(name, opt_fn) {
+ let fn = getMochaGlobal('describe');
+ return opt_fn ? fn.skip(name, opt_fn) : fn.skip(name);
+};
+
+
+/**
+ * Defines a suppressed test suite.
+ * @param {string} name The suite name.
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
+ * a pending test suite.
+ */
+exports.xdescribe = function(name, opt_fn) {
+ let fn = getMochaGlobal('xdescribe');
+ return opt_fn ? fn(name, opt_fn) : fn(name);
+};
+
/**
* Register a function to call after the current suite finishes.
* @param {function()} fn .
*/
-exports.after = wrapped(global.after);
+exports.after = function(fn) {
+ if (!WRAPPED.after) {
+ WRAPPED.after = wrapped(getMochaGlobal('after'));
+ }
+ WRAPPED.after(fn);
+};
+
/**
* Register a function to call after each test in a suite.
* @param {function()} fn .
*/
-exports.afterEach = wrapped(global.afterEach);
+exports.afterEach = function(fn) {
+ if (!WRAPPED.afterEach) {
+ WRAPPED.afterEach = wrapped(getMochaGlobal('afterEach'));
+ }
+ WRAPPED.afterEach(fn);
+};
+
/**
* Register a function to call before the current suite starts.
* @param {function()} fn .
*/
-exports.before = wrapped(global.before);
+exports.before = function(fn) {
+ if (!WRAPPED.before) {
+ WRAPPED.before = wrapped(getMochaGlobal('before'));
+ }
+ WRAPPED.before(fn);
+};
/**
* Register a function to call before each test in a suite.
* @param {function()} fn .
*/
-exports.beforeEach = wrapped(global.beforeEach);
+exports.beforeEach = function(fn) {
+ if (!WRAPPED.beforeEach) {
+ WRAPPED.beforeEach = wrapped(getMochaGlobal('beforeEach'));
+ }
+ WRAPPED.beforeEach(fn);
+};
/**
* Add a test to the current suite.
* @param {string} name The test name.
- * @param {function()=} fn The test function, or {@code undefined} to define
+ * @param {function()=} opt_fn The test function, or `undefined` to define
* a pending test case.
*/
-exports.it = wrapped(global.it);
+exports.it = function(name, opt_fn) {
+ wrapIt();
+ if (opt_fn) {
+ WRAPPED.it(name, opt_fn);
+ } else {
+ WRAPPED.it(name);
+ }
+};
/**
* An alias for {@link #it()} that flags the test as the only one that should
* be run within the current suite.
* @param {string} name The test name.
- * @param {function()=} fn The test function, or {@code undefined} to define
+ * @param {function()=} opt_fn The test function, or `undefined` to define
* a pending test case.
*/
-exports.iit = exports.it.only = wrapped(global.it.only);
+exports.it.only = function(name, opt_fn) {
+ wrapIt();
+ if (opt_fn) {
+ WRAPPED.itOnly(name, opt_fn);
+ } else {
+ WRAPPED.itOnly(name);
+ }
+};
+
/**
* Adds a test to the current suite while suppressing it so it is not run.
* @param {string} name The test name.
- * @param {function()=} fn The test function, or {@code undefined} to define
+ * @param {function()=} opt_fn The test function, or `undefined` to define
* a pending test case.
*/
-exports.xit = exports.it.skip = wrapped(global.xit);
+exports.xit = function(name, opt_fn) {
+ if (!WRAPPED.xit) {
+ WRAPPED.xit = wrapped(getMochaGlobal('xit'));
+ }
+ if (opt_fn) {
+ WRAPPED.xit(name, opt_fn);
+ } else {
+ WRAPPED.xit(name);
+ }
+};
+
+exports.it.skip = exports.xit;
exports.ignore = ignore;