Skip to content

Commit 446f654

Browse files
committed
Force promise rejection values to be Error objects.
1 parent 1a0a010 commit 446f654

File tree

5 files changed

+72
-43
lines changed

5 files changed

+72
-43
lines changed

javascript/node/selenium-webdriver/CHANGES.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v2.45.0-dev
2+
3+
* Promise rejections are now always coerced to Error-like objects (an object
4+
with a string `message` property). We do not guarantee `instanceof Error`
5+
since the rejection value may come from another context.
6+
17
## v2.44.0
28

39
* Added the `until` module, which defines common explicit wait conditions.

javascript/node/selenium-webdriver/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "selenium-webdriver",
3-
"version": "2.44.0",
3+
"version": "2.45.0-dev",
44
"description": "The official WebDriver JavaScript bindings from the Selenium project",
55
"keywords": [
66
"automation",

javascript/webdriver/promise.js

+16-22
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ webdriver.promise.Thenable.prototype.isPending = function() {};
108108
* @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback The
109109
* function to call if this promise is successfully resolved. The function
110110
* should expect a single argument: the promise's resolved value.
111-
* @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback The
112-
* function to call if this promise is rejected. The function should expect
113-
* a single argument: the rejection reason.
111+
* @param {?(function(Error): (R|webdriver.promise.Promise.<R>))=} opt_errback
112+
* The function to call if this promise is rejected. The function should
113+
* expect a single argument: the rejection reason.
114114
* @return {!webdriver.promise.Promise.<R>} A new promise which will be
115115
* resolved with the result of the invoked callback.
116116
* @template R
@@ -136,9 +136,9 @@ webdriver.promise.Thenable.prototype.then = function(
136136
* });
137137
* </code></pre>
138138
*
139-
* @param {function(*): (R|webdriver.promise.Promise.<R>)} errback The function
140-
* to call if this promise is rejected. The function should expect a single
141-
* argument: the rejection reason.
139+
* @param {function(Error): (R|webdriver.promise.Promise.<R>)} errback The
140+
* function to call if this promise is rejected. The function should
141+
* expect a single argument: the rejection reason.
142142
* @return {!webdriver.promise.Promise.<R>} A new promise which will be
143143
* resolved with the result of the invoked callback.
144144
* @template R
@@ -429,10 +429,10 @@ webdriver.promise.Deferred = function(opt_flow) {
429429
* @param {*} newValue The deferred's new value.
430430
*/
431431
function notifyAll(newState, newValue) {
432-
if (newState === webdriver.promise.Deferred.State_.REJECTED &&
433-
// We cannot check instanceof Error since the object may have been
434-
// created in a different JS context.
435-
goog.isObject(newValue) && goog.isString(newValue.message)) {
432+
if (newState === webdriver.promise.Deferred.State_.REJECTED) {
433+
if (!webdriver.promise.isError_(newValue)) {
434+
newValue = Error(newValue ? newValue : 'Promise rejected');
435+
}
436436
newValue = flow.annotateError(/** @type {!Error} */(newValue));
437437
}
438438

@@ -484,7 +484,8 @@ webdriver.promise.Deferred = function(opt_flow) {
484484
* Registers a callback on this Deferred.
485485
*
486486
* @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback .
487-
* @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback .
487+
* @param {?(function(Error):
488+
* (R|webdriver.promise.Promise.<R>))=} opt_errback .
488489
* @return {!webdriver.promise.Promise.<R>} A new promise representing the
489490
* result of the callback.
490491
* @template R
@@ -538,8 +539,8 @@ webdriver.promise.Deferred = function(opt_flow) {
538539
/**
539540
* Rejects this promise. If the error is itself a promise, this instance will
540541
* be chained to it and be rejected with the error's resolved value.
541-
* @param {*=} opt_error The rejection reason, typically either a
542-
* {@code Error} or a {@code string}.
542+
* @param {*=} opt_error The rejection reason. If not a {@link Error}, one
543+
* will be created from the value's string representation.
543544
*/
544545
function reject(opt_error) {
545546
resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error);
@@ -622,7 +623,7 @@ webdriver.promise.Deferred.State_ = {
622623
webdriver.promise.isError_ = function(value) {
623624
return value instanceof Error ||
624625
goog.isObject(value) &&
625-
(Object.prototype.toString.call(value) === '[object Error]' ||
626+
(goog.isString(value.message) ||
626627
// A special test for goog.testing.JsUnitException.
627628
value.isJsUnitException);
628629

@@ -1514,19 +1515,12 @@ webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {
15141515
}, this);
15151516

15161517
this.trimHistory_();
1517-
var self = this;
15181518
this.runInNewFrame_(task.execute, function(result) {
15191519
markTaskComplete();
15201520
task.fulfill(result);
15211521
}, function(error) {
15221522
markTaskComplete();
1523-
1524-
if (!webdriver.promise.isError_(error) &&
1525-
!webdriver.promise.isPromise(error)) {
1526-
error = Error(error);
1527-
}
1528-
1529-
task.reject(self.annotateError(/** @type {!Error} */ (error)));
1523+
task.reject(error);
15301524
}, true);
15311525
};
15321526

javascript/webdriver/test/promise_flow_test.js

-20
Original file line numberDiff line numberDiff line change
@@ -2249,26 +2249,6 @@ function testAnnotatesRejectedPromiseErrorsWithFlowState_taskErrorBubblesUp() {
22492249
pair.assertErrback();
22502250
}
22512251

2252-
function testDoesNotAnnotatedRejectedPromisesIfGivenNonErrorValue() {
2253-
var error = {};
2254-
2255-
var pair = callbackPair(null, function(e) {
2256-
assertEquals(error, e);
2257-
for (var val in error) {
2258-
fail('Did not expect error to be modified');
2259-
}
2260-
});
2261-
2262-
webdriver.promise.createFlow(function(flow) {
2263-
var d = webdriver.promise.defer();
2264-
d.reject(error);
2265-
d.then(pair.callback, pair.errback);
2266-
});
2267-
2268-
runAndExpectSuccess();
2269-
pair.assertErrback();
2270-
}
2271-
22722252
function testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow() {
22732253
var error = Error('original message');
22742254
var originalStack = error.stack;

javascript/webdriver/test/promise_test.js

+49
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,55 @@ function testResolvingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() {
474474
}
475475

476476

477+
function testRejectForcesValueToAnError_errorInstance() {
478+
var d = webdriver.promise.defer();
479+
var callback = callbackHelper(assertIsStubError);
480+
481+
d.thenCatch(callback);
482+
d.reject(STUB_ERROR);
483+
callback.assertCalled();
484+
}
485+
486+
487+
function testRejectForcesValueToAnError_errorSubTypeInstance() {
488+
var d = webdriver.promise.defer();
489+
var e = new TypeError('hi');
490+
var callback = callbackHelper(function(actual) {
491+
assertEquals(e, actual);
492+
});
493+
494+
d.thenCatch(callback);
495+
d.reject(e);
496+
callback.assertCalled();
497+
}
498+
499+
500+
function testRejectForcesValueToAnError_customErrorInstance() {
501+
var d = webdriver.promise.defer();
502+
var e = new goog.debug.Error('hi there');
503+
var callback = callbackHelper(function(actual) {
504+
assertEquals(e, actual);
505+
});
506+
507+
d.thenCatch(callback);
508+
d.reject(e);
509+
callback.assertCalled();
510+
}
511+
512+
513+
function testRejectForcesValueToAnError_errorLike() {
514+
var d = webdriver.promise.defer();
515+
var e = {message: 'yolo'};
516+
var callback = callbackHelper(function(actual) {
517+
assertEquals(e, actual);
518+
});
519+
520+
d.thenCatch(callback);
521+
d.reject(e);
522+
callback.assertCalled();
523+
}
524+
525+
477526
function testRejectingAPromiseWithAnotherPromiseCreatesAChain_ourPromise() {
478527
var d1 = new webdriver.promise.Deferred();
479528
var d2 = new webdriver.promise.Deferred();

0 commit comments

Comments
 (0)