From 71947565fffffebfb59d64b3976ef8e2cb98e1f1 Mon Sep 17 00:00:00 2001 From: Jason Leyba Date: Mon, 24 Oct 2016 19:39:17 -0700 Subject: [PATCH] [js] Reduce the API on promise.Thenable for compatibility with native Promises. #2969 --- javascript/node/selenium-webdriver/CHANGES.md | 4 + javascript/node/selenium-webdriver/edge.js | 7 +- .../node/selenium-webdriver/firefox/index.js | 3 +- .../node/selenium-webdriver/http/util.js | 154 +++++---- javascript/node/selenium-webdriver/ie.js | 7 +- .../node/selenium-webdriver/lib/promise.js | 294 +++++++++++------- .../node/selenium-webdriver/lib/webdriver.js | 49 +-- .../node/selenium-webdriver/remote/index.js | 14 +- javascript/node/selenium-webdriver/safari.js | 3 +- .../selenium-webdriver/test/http/util_test.js | 46 ++- .../test/lib/promise_error_test.js | 3 - .../test/lib/promise_flow_test.js | 14 +- .../test/lib/promise_test.js | 5 +- .../test/lib/webdriver_test.js | 1 - 14 files changed, 338 insertions(+), 266 deletions(-) diff --git a/javascript/node/selenium-webdriver/CHANGES.md b/javascript/node/selenium-webdriver/CHANGES.md index 0522bf5b58143..71411a48110b9 100644 --- a/javascript/node/selenium-webdriver/CHANGES.md +++ b/javascript/node/selenium-webdriver/CHANGES.md @@ -16,6 +16,10 @@ ### API Changes * Removed `safari.Options#useLegacyDriver()` + * Reduced the API on `promise.Thenable` for compatibility with native promises: + - Removed `#isPending()` + - Removed `#cancel()` + - Removed `#finally()` ## v3.0.0-beta-3 diff --git a/javascript/node/selenium-webdriver/edge.js b/javascript/node/selenium-webdriver/edge.js index 9685a2c215d42..d4b08a105ba87 100644 --- a/javascript/node/selenium-webdriver/edge.js +++ b/javascript/node/selenium-webdriver/edge.js @@ -278,11 +278,10 @@ class Driver extends webdriver.WebDriver { 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)); + this.quit = () => { + return /** @type {!promise.Thenable} */( + promise.finally(super.quit(), service.kill.bind(service))); }; } diff --git a/javascript/node/selenium-webdriver/firefox/index.js b/javascript/node/selenium-webdriver/firefox/index.js index dcb342d102440..2def764a38495 100644 --- a/javascript/node/selenium-webdriver/firefox/index.js +++ b/javascript/node/selenium-webdriver/firefox/index.js @@ -622,7 +622,8 @@ class Driver extends webdriver.WebDriver { /** @override */ this.quit = () => { - return super.quit().finally(spec.onQuit); + return /** @type {!promise.Thenable} */( + promise.finally(super.quit(), spec.onQuit)); }; } diff --git a/javascript/node/selenium-webdriver/http/util.js b/javascript/node/selenium-webdriver/http/util.js index 7564ba85e97d3..8662bed2887af 100644 --- a/javascript/node/selenium-webdriver/http/util.js +++ b/javascript/node/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/javascript/node/selenium-webdriver/ie.js b/javascript/node/selenium-webdriver/ie.js index 40095a0bf6a2e..ffd68e4c2cc92 100644 --- a/javascript/node/selenium-webdriver/ie.js +++ b/javascript/node/selenium-webdriver/ie.js @@ -420,11 +420,10 @@ class Driver extends webdriver.WebDriver { super(driver.getSession(), executor, driver.controlFlow()); - let boundQuit = this.quit.bind(this); - /** @override */ - this.quit = function() { - return boundQuit().finally(service.kill.bind(service)); + this.quit = () => { + return /** @type {!promise.Thenable} */( + promise.finally(super.quit(), service.kill.bind(service))); }; } diff --git a/javascript/node/selenium-webdriver/lib/promise.js b/javascript/node/selenium-webdriver/lib/promise.js index e3529f78c0fdc..af0b907109885 100644 --- a/javascript/node/selenium-webdriver/lib/promise.js +++ b/javascript/node/selenium-webdriver/lib/promise.js @@ -799,6 +799,37 @@ 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. + } +} /** @@ -812,19 +843,12 @@ const IMPLEMENTED_BY_SYMBOL = Symbol('promise.Thenable'); 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,29 +859,9 @@ 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. * @@ -897,44 +901,48 @@ class Thenable { * @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} + * @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)} callback The function to call when - * this promise is resolved. - * @return {!ManagedPromise} 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 +975,7 @@ const ON_CANCEL_HANDLER = new WeakMap; * fulfilled or rejected state, at which point the promise is considered * resolved. * - * @implements {Thenable} + * @implements {CancellableThenable} * @template T * @see http://promises-aplus.github.io/promises-spec/ */ @@ -1176,7 +1184,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,18 +1202,13 @@ 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; } } - /** @override */ - isPending() { - return this.state_ === PromiseState.PENDING; - } - /** @override */ then(opt_callback, opt_errback) { return this.addCallback_( @@ -1218,21 +1221,15 @@ class ManagedPromise { null, errback, 'catch', ManagedPromise.prototype.catch); } - /** @override */ + /** + * @param {function(): (R|IThenable)} callback + * @return {!ManagedPromise} + * @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 +1305,16 @@ class ManagedPromise { } } } -Thenable.addImplementation(ManagedPromise); +CancellableThenable.addImplementation(ManagedPromise); + + +/** + * @param {!ManagedPromise} promise + * @return {boolean} + */ +function isPending(promise) { + return promise.state_ === PromiseState.PENDING; +} /** @@ -1491,6 +1497,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)} callback The function to call when + * the promise is resolved. + * @return {!IThenable} 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 @@ -2510,6 +2569,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 +2752,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 +2763,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); } } @@ -2804,28 +2876,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. @@ -2983,7 +3033,7 @@ function consume(generatorFn, opt_self, var_args) { } function pump(fn, opt_arg) { - if (!deferred.promise.isPending()) { + if (!isPending(deferred.promise)) { return; // Defererd was cancelled; silently abort. } @@ -3008,6 +3058,7 @@ function consume(generatorFn, opt_self, var_args) { module.exports = { + CancellableThenable: CancellableThenable, CancellationError: CancellationError, ControlFlow: ControlFlow, Deferred: Deferred, @@ -3024,6 +3075,7 @@ module.exports = { defer: defer, delayed: delayed, filter: filter, + finally: thenFinally, fulfilled: fulfilled, fullyResolved: fullyResolved, isGenerator: isGenerator, diff --git a/javascript/node/selenium-webdriver/lib/webdriver.js b/javascript/node/selenium-webdriver/lib/webdriver.js index 1e78e870cdbf3..a9855a387fc67 100644 --- a/javascript/node/selenium-webdriver/lib/webdriver.js +++ b/javascript/node/selenium-webdriver/lib/webdriver.js @@ -482,7 +482,8 @@ class WebDriver { '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_); + return /** @type {!promise.Thenable} */( + promise.finally(result, () => delete this.session_)); } /** @@ -2207,7 +2208,7 @@ class WebElement { * return el.click(); * }); * - * @implements {promise.Thenable} + * @implements {promise.CancellableThenable} * @final */ class WebElementPromise extends WebElement { @@ -2220,11 +2221,17 @@ class WebElementPromise extends WebElement { 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); @@ -2232,9 +2239,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. @@ -2247,7 +2251,7 @@ class WebElementPromise extends WebElement { }; } } -promise.Thenable.addImplementation(WebElementPromise); +promise.CancellableThenable.addImplementation(WebElementPromise); ////////////////////////////////////////////////////////////////////////////// @@ -2358,7 +2362,7 @@ class Alert { * return alert.dismiss(); * }); * - * @implements {promise.Thenable} + * @implements {promise.CancellableThenable} * @final */ class AlertPromise extends Alert { @@ -2371,11 +2375,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); @@ -2383,9 +2393,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 @@ -2437,7 +2444,7 @@ class AlertPromise extends Alert { }; } } -promise.Thenable.addImplementation(AlertPromise); +promise.CancellableThenable.addImplementation(AlertPromise); // PUBLIC API diff --git a/javascript/node/selenium-webdriver/remote/index.js b/javascript/node/selenium-webdriver/remote/index.js index 4732ec50dbcd7..22ffb4b5579e8 100644 --- a/javascript/node/selenium-webdriver/remote/index.js +++ b/javascript/node/selenium-webdriver/remote/index.js @@ -247,15 +247,11 @@ 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))); + return httpUtil.waitForServer(serverUrl, timeout, cancelToken) + .then(_ => fulfill(serverUrl)); }); }); })); diff --git a/javascript/node/selenium-webdriver/safari.js b/javascript/node/selenium-webdriver/safari.js index 25a55859145ae..2b9af51e71dd1 100644 --- a/javascript/node/selenium-webdriver/safari.js +++ b/javascript/node/selenium-webdriver/safari.js @@ -216,7 +216,8 @@ class Driver extends webdriver.WebDriver { /** @override */ this.quit = () => { - return super.quit().finally(onQuit); + return /** @type {!promise.Thenable} */( + promise.finally(super.quit(), onQuit)); }; } } diff --git a/javascript/node/selenium-webdriver/test/http/util_test.js b/javascript/node/selenium-webdriver/test/http/util_test.js index aa7a9158a3137..0c324396c27f1 100644 --- a/javascript/node/selenium-webdriver/test/http/util_test.js +++ b/javascript/node/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,14 @@ 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 = Promise.defer(); + setTimeout(_ => cancel.resolve(), 50); + return util.waitForServer(baseUrl, 200, cancel.promise) + .then( + () => { throw Error('Did not expect to succeed!'); }, + (e) => assert.ok(e instanceof promise.CancellationError)); }); }); @@ -167,18 +163,14 @@ 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 = Promise.defer(); + setTimeout(_ => cancel.resolve(), 50); + return util.waitForUrl(baseUrl, 200, cancel.promise) + .then( + () => { throw Error('Did not expect to succeed!'); }, + (e) => assert.ok(e instanceof promise.CancellationError)); }); }); }); diff --git a/javascript/node/selenium-webdriver/test/lib/promise_error_test.js b/javascript/node/selenium-webdriver/test/lib/promise_error_test.js index e6de3cb92867c..6bc3910f09861 100644 --- a/javascript/node/selenium-webdriver/test/lib/promise_error_test.js +++ b/javascript/node/selenium-webdriver/test/lib/promise_error_test.js @@ -808,8 +808,6 @@ describe('promise error handling', function() { 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)); @@ -827,7 +825,6 @@ describe('promise error handling', function() { }); }).then(function(error) { assert.ok(error instanceof promise.CancellationError); - assert.ok(!task.isPending()); return task.catch(function(error) { assert.ok(error instanceof promise.CancellationError); }); diff --git a/javascript/node/selenium-webdriver/test/lib/promise_flow_test.js b/javascript/node/selenium-webdriver/test/lib/promise_flow_test.js index b42ac5209e00a..f9a40be045ca8 100644 --- a/javascript/node/selenium-webdriver/test/lib/promise_flow_test.js +++ b/javascript/node/selenium-webdriver/test/lib/promise_flow_test.js @@ -1184,9 +1184,7 @@ describe('promise control flow', function() { .test(e.message), 'unexpected error message: ' + e.message); }); - return waitForIdle().then(function() { - assert.ok('Promise should not be cancelled', d.promise.isPending()); - }); + return waitForIdle(); }); it('unboundedWaitOnPromiseResolution', function() { @@ -1202,7 +1200,6 @@ describe('promise control flow', function() { timeout(10).then(function() { assert.deepEqual(['a'], messages); - assert.ok(waitResult.isPending()); d.fulfill(1234); return waitResult; }).then(function() { @@ -1440,7 +1437,6 @@ describe('promise control flow', function() { let errors = Array.from(e.errors); assert.deepEqual([once, twice], errors); assertFlowHistory('one'); - assert.ok(!twoResult.isPending(), 'Did not cancel the second task'); }); }); }); @@ -1752,13 +1748,11 @@ describe('promise control flow', function() { 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); }). @@ -1777,7 +1771,6 @@ describe('promise control flow', 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() { @@ -2035,7 +2028,6 @@ describe('promise control flow', function() { return timeout(10).then(function() { assert.deepEqual([1], order); - assert.ok(unresolved.promise.isPending()); outerTask.cancel('no soup for you'); return waitForIdle(); @@ -2077,7 +2069,6 @@ describe('promise control flow', function() { }).then(function() { assert.equal(0, called); - assert.ok(!task.isPending()); return task; }).then(fail, function(e) { @@ -2103,9 +2094,6 @@ describe('promise control flow', function() { }).then(function() { assert.equal(0, called); - assert.ok(!task1.isPending()); - assert.ok(!task2.isPending()); - assert.ok(!task3.isPending()); }); }); }); diff --git a/javascript/node/selenium-webdriver/test/lib/promise_test.js b/javascript/node/selenium-webdriver/test/lib/promise_test.js index 51554ecd9df48..08f310ab445e1 100644 --- a/javascript/node/selenium-webdriver/test/lib/promise_test.js +++ b/javascript/node/selenium-webdriver/test/lib/promise_test.js @@ -207,7 +207,7 @@ describe('promise', function() { }); }); - it('"parent" is a Thenable', function() { + it('"parent" is a CancellableThenable', function() { function noop() {} class FakeThenable { @@ -224,11 +224,12 @@ describe('promise', function() { return new FakeThenable(result); } } - promise.Thenable.addImplementation(FakeThenable); + 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); diff --git a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js index d2d87ca3e09e0..e0b7fc744d809 100644 --- a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js +++ b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js @@ -1649,7 +1649,6 @@ describe('WebDriver', function() { var deferredText = new promise.Deferred(); var alert = new AlertPromise({}, deferredText.promise); - assert.ok(alert.isPending()); deferredText.fulfill(new Alert({}, 'foo')); return alert.getText().then(function(text) {