| //------------------------------------------------------------------------------------------------------- | |
| // Copyright (C) Microsoft. All rights reserved. | |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. | |
| //------------------------------------------------------------------------------------------------------- | |
| // ES6 Promise async tests -- verifies functionality of promise async operations | |
| function echo(str) { | |
| WScript.Echo(str); | |
| } | |
| function getAsyncFulfilledPromise(t, v, fail) { | |
| fail = fail || false; | |
| var p = new Promise( | |
| function(resolve,reject) { | |
| if (fail) { | |
| WScript.SetTimeout(function() { reject(v) }, 0); | |
| } else { | |
| WScript.SetTimeout(function() { resolve(v) }, 0); | |
| } | |
| } | |
| ); | |
| p.then( | |
| function(result) { | |
| echo(t + v + ' success: ' + result); | |
| }, | |
| function(err) { | |
| echo(t + v + ' failure: ' + err); | |
| } | |
| ); | |
| return p; | |
| } | |
| function getAsyncResolvePromise(t, v) { | |
| return getAsyncFulfilledPromise(t, v, false); | |
| } | |
| function getAsyncRejectPromise(t, v) { | |
| return getAsyncFulfilledPromise(t, v, true); | |
| } | |
| // Copy promise and attempt to call resolve handler twice. | |
| // Since we can only call the Promise.all resolve handlers once, we can tamper with the result value for this promise. | |
| function tamper(p, result, useresult) { | |
| return Object.assign(p, { | |
| then(onFulfilled, onRejected) { | |
| if (useresult) { | |
| onFulfilled(result); | |
| } else { | |
| onFulfilled(); | |
| } | |
| return Promise.prototype.then.call(this, onFulfilled, onRejected); | |
| } | |
| }); | |
| } | |
| var tests = [ | |
| { | |
| name: "Promise basic behavior", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| echo('Test #' + index + ' - Executor function called synchronously'); | |
| resolve('basic:success'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise basic error behavior", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| echo('Test #' + index + ' - Executor function called synchronously'); | |
| reject('basic:error'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with multiple then handlers", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('multithen:success'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #3 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #3 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with chained then handlers", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('chain:success1'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| return new Promise( | |
| function(resolve,reject) { | |
| resolve('chain:success2'); | |
| } | |
| ); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with a throwing executor function", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| throw 'basic:throw'; | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with a potential thenable that throws when getting the 'then' property", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('thenable.get:unused'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| return { get then() { throw 'thenable.get:error!'; } }; | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with a potential thenable that throws when calling the 'then' function", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('thenable.call:unused'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| return { then: function() { throw 'thenable.call:error!'; } }; | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with a success handler that throws when called", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('success.throw:unused'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| throw 'success.throw:error'; | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with an error handler that throws when called", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| reject('error.throw:unused'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| throw 'error.throw:error'; | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise with an executor function that creates a self-resolution error", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| WScript.SetTimeout( | |
| function() { | |
| resolve(promise); | |
| }, | |
| 0 | |
| ); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise basic catch behavior", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| reject('error'); | |
| } | |
| ); | |
| promise.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise chained catch behavior", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| reject('error1'); | |
| } | |
| ); | |
| promise.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| throw 'error2'; | |
| } | |
| ).catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise then and catch interleaved", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| reject('error1'); | |
| } | |
| ); | |
| promise.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| return 'ok'; | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| throw 'error2'; | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise identity function is used when no success handler is provided", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| resolve('success'); | |
| } | |
| ); | |
| promise.then( | |
| undefined, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #2 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise thrower function is used when no error handler is provided", | |
| body: function (index) { | |
| var promise = new Promise( | |
| function(resolve,reject) { | |
| reject('failure'); | |
| } | |
| ); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| undefined | |
| ).then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #2 called with result = ' + result); | |
| }, | |
| undefined | |
| ).catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.resolve creates a fulfilled resolved promise", | |
| body: function (index) { | |
| var promise = Promise.resolve('resolved promise result'); | |
| promise.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.resolve called with a promise returns the same promise", | |
| body: function (index) { | |
| var promise = Promise.resolve(42); | |
| var wrappedPromise = Promise.resolve(promise); | |
| if (promise !== wrappedPromise) { | |
| echo('Test #' + index + ' - Promise.resolve returns a new promise object!'); | |
| } | |
| } | |
| }, | |
| { | |
| name: "Promise.reject creates a fulfilled rejected promise", | |
| body: function (index) { | |
| var promise = Promise.reject('rejected promise result'); | |
| promise.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.reject called with a promise returns a promise for that promise", | |
| body: function (index) { | |
| var promise = Promise.reject(42); | |
| var wrappedPromise = Promise.reject(promise); | |
| if (promise === wrappedPromise) { | |
| echo('Test #' + index + ' - Promise.reject does not return a new promise object!'); | |
| } | |
| } | |
| }, | |
| { | |
| name: "Promise.race with an object containing a non-function iterator property", | |
| body: function (index) { | |
| var objectWithNonObjectIterator = { | |
| [Symbol.iterator]: 123 | |
| }; | |
| var p = Promise.race(objectWithNonObjectIterator); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race with this argument missing the resolve function", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = undefined; | |
| var p = Promise.race([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.race with this argument resolve function returning a non-object", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = function() { return undefined; }; | |
| var p = Promise.race([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.race with this argument resolve function returning an object with no then function", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = function() { return {}; }; | |
| var p = Promise.race([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.race with an object containing an iterator that throws", | |
| body: function (index) { | |
| var objectWithIterator = { | |
| [Symbol.iterator]: function() { | |
| return { | |
| next: function () { | |
| throw new TypeError('failure inside iterator'); | |
| } | |
| }; | |
| } | |
| }; | |
| var p = Promise.race(objectWithIterator); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race still returns a rejected promise if anything throws while iterating, even if resolved promises are encountered", | |
| body: function (index) { | |
| var objectWithIterator = { | |
| [Symbol.iterator]: function() { | |
| return { | |
| i: 0, | |
| next: function () { | |
| if (this.i > 2) | |
| { | |
| throw new TypeError('failure inside iterator'); | |
| } | |
| this.i++; | |
| return { | |
| done: this.i == 5, | |
| value: Promise.resolve('resolved promise completion #' + this.i) | |
| }; | |
| } | |
| }; | |
| } | |
| }; | |
| var p = Promise.race(objectWithIterator); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race fulfills with the same value as the first encountered resolved promise", | |
| body: function (index) { | |
| var promises = [ | |
| new Promise(function() {}), | |
| Promise.resolve('first promise'), | |
| Promise.resolve('second promise'), | |
| Promise.reject('third promise') | |
| ]; | |
| var p = Promise.race(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race fulfills with the same value as the first encountered resolved promise (promises complete async)", | |
| body: function (index) { | |
| var promises = [ | |
| new Promise(function() {}), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p1'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p2'), | |
| getAsyncRejectPromise('Test #' + index + ' - ', 'p3') | |
| ]; | |
| var p = Promise.race(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race fulfills with the same value as the first encountered rejected promise (promises complete async)", | |
| body: function (index) { | |
| var promises = [ | |
| new Promise(function() {}), | |
| getAsyncRejectPromise('Test #' + index + ' - ', 'p1'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p2'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p3') | |
| ]; | |
| var p = Promise.race(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.race passes each element in it's argument to Promise.resolve", | |
| body: function (index) { | |
| var promises = [ | |
| 'first promise value', | |
| 42, | |
| new TypeError('some error') | |
| ]; | |
| var p = Promise.race(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all with an object containing a non-function iterator property", | |
| body: function (index) { | |
| var objectWithNonObjectIterator = { | |
| [Symbol.iterator]: 123 | |
| }; | |
| var p = Promise.all(objectWithNonObjectIterator); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all with this argument missing the resolve function", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = undefined; | |
| var p = Promise.all([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.all with this argument resolve function returning a non-object", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = function() { return undefined; }; | |
| var p = Promise.all([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.all with this argument resolve function returning an object with no then function", | |
| body: function (index) { | |
| var _resolve = Promise.resolve; | |
| Promise.resolve = function() { return {}; }; | |
| var p = Promise.all([Promise.reject(42)]); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| Promise.resolve = _resolve; | |
| } | |
| }, | |
| { | |
| name: "Promise.all with an object containing an iterator that throws", | |
| body: function (index) { | |
| var objectWithIterator = { | |
| [Symbol.iterator]: function() { | |
| return { | |
| next: function () { | |
| throw new TypeError('failure inside iterator'); | |
| } | |
| }; | |
| } | |
| }; | |
| var p = Promise.all(objectWithIterator); | |
| p.catch( | |
| function(err) { | |
| echo('Test #' + index + ' - Catch handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all still returns a rejected promise if anything throws while iterating, even if resolved promises are encountered", | |
| body: function (index) { | |
| var objectWithIterator = { | |
| [Symbol.iterator]: function() { | |
| return { | |
| i: 0, | |
| next: function () { | |
| if (this.i > 2) | |
| { | |
| throw new TypeError('failure inside iterator'); | |
| } | |
| this.i++; | |
| return { | |
| done: this.i == 5, | |
| value: Promise.resolve('resolved promise completion #' + this.i) | |
| }; | |
| } | |
| }; | |
| } | |
| }; | |
| var p = Promise.all(objectWithIterator); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all fulfills with the same value as the first encountered rejected promise", | |
| body: function (index) { | |
| var promises = [ | |
| new Promise(function() {}), | |
| Promise.resolve('first promise'), | |
| Promise.resolve('second promise'), | |
| Promise.reject('third promise') | |
| ]; | |
| var p = Promise.all(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all fulfills with the same value as the first encountered rejected promise (async promises)", | |
| body: function (index) { | |
| var promises = [ | |
| new Promise(function() {}), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p1'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p2'), | |
| getAsyncRejectPromise('Test #' + index + ' - ', 'p3') | |
| ]; | |
| var p = Promise.all(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all fulfills when all promises in iterable fulfill", | |
| body: function (index) { | |
| var promises = [ | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p1'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p2'), | |
| getAsyncResolvePromise('Test #' + index + ' - ', 'p3') | |
| ]; | |
| var p = Promise.all(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all passes each element in the arguments to Promise.resolve", | |
| body: function (index) { | |
| var promises = [ | |
| 'success value 1', | |
| 42, | |
| new TypeError('an error') | |
| ]; | |
| var p = Promise.all(promises); | |
| p.then( | |
| function(result) { | |
| echo('Test #' + index + ' - Success handler #1 called with result = ' + result); | |
| }, | |
| function(err) { | |
| echo('Test #' + index + ' - Error handler #1 called with err = ' + err); | |
| } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all called with empty iterator, calls Promise.resolve synchronously and passes abrupt completion to reject handler", | |
| body: function (index) { | |
| function FakePromise(fn) { | |
| function resolve() { echo(`Test #${index} - resolve called`); throw new Error('oops'); } | |
| function reject(e) { echo(`Test #${index} - reject called: ${e.message}`) } | |
| fn(resolve, reject); | |
| this.then = function(onResolve, onReject) {}; | |
| } | |
| FakePromise.resolve = function() {}; | |
| Promise.all.call(FakePromise, []); | |
| } | |
| }, | |
| { | |
| name: "Promise.resolve called with a thenable calls then on the thenable", | |
| body: function (index) { | |
| var thenable = { | |
| then: function(resolve, reject) { | |
| echo('Test #' + index + ' - Promise.resolve calls thenable.then'); | |
| Promise.resolve('nested Promise.resolve call').then( | |
| function(result) { | |
| echo('Test #' + index + ' - Promise.resolve call nested within thenable.then = ' + result); | |
| } | |
| ); | |
| } | |
| }; | |
| var promise = Promise.resolve(thenable); | |
| } | |
| }, | |
| { | |
| name: "Calling promise resolve function with thenable should call thenable.then", | |
| body: function (index) { | |
| var p = new Promise(function(res) { | |
| res({ then: function(resolve, reject) { | |
| echo('Test #' + index + ' - thenable.then resolve = ' + (typeof resolve) + ' reject = ' + (typeof reject)); | |
| }}); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Promise.all doesn't call then for rejected promises", | |
| body: function(index) { | |
| Promise.all([Promise.reject('expected1')]).then( | |
| result => echo(`Test #${index} - Success handler #1 called with result = ${result}`) | |
| ).catch( | |
| err => echo(`Test #${index} - Catch handler #1 called with err = ${err}`) | |
| ); | |
| Promise.all([Promise.reject('expected2'), Promise.resolve('unexpected1')]).then( | |
| result => echo(`Test #${index} - Success handler #2 called with result = ${result}`) | |
| ).catch( | |
| err => echo(`Test #${index} - Catch handler #2 called with err = ${err}`) | |
| ); | |
| Promise.all([Promise.resolve('unexpected2'), Promise.reject('expected3')]).then( | |
| result => echo(`Test #${index} - Success handler #3 called with result = ${result}`) | |
| ).catch( | |
| err => echo(`Test #${index} - Catch handler #3 called with err = ${err}`) | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.all with iterator that returns no items", | |
| body: function(index) { | |
| var promises = []; | |
| var p = Promise.all(promises); | |
| p.then(v => { | |
| echo(`Test #${index} - Success handler #1 called with result = '${v}' (length = ${v.length}) (isArray = ${Array.isArray(v)})`); | |
| }).catch(err => { | |
| echo(`Test #${index} - Catch handler called with err = ${err}`); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Simple tampering of Promise.all promise changes resolved result value", | |
| body: function(index) { | |
| var promises = [tamper(Promise.resolve('success'), 'tampered', true)]; | |
| Promise.all(promises).then(result => { | |
| echo(`Test #${index} - Success handler called with result = '${result}' (length = ${result.length}) (isArray = ${Array.isArray(result)})`); | |
| }).catch(err => { | |
| echo(`Test #${index} - Catch handler called with err = ${err}`); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Promise.all - can't prevent remaining elements counter from reaching zero", | |
| body: function (index) { | |
| var promises = [tamper(Promise.resolve('success'))]; | |
| Promise.all(promises).then(result => { | |
| echo(`Test #${index} - Success handler called with result = '${result}' (length = ${result.length}) (isArray = ${Array.isArray(result)})`); | |
| }).catch(err => { | |
| echo(`Test #${index} - Catch handler called with err = ${err}`); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Promise from Promise.all never resolved before arguments", | |
| body: function (index) { | |
| var promises = [ | |
| Promise.resolve(0), | |
| tamper(Promise.resolve(1)), | |
| Promise.resolve(2).then(result => { | |
| echo(`Test #${index} - Success handler #1a called with result = '${result}' (isArray = ${Array.isArray(result)}) (fulfillCalled = ${fulfillCalled})`); | |
| return 3; | |
| }).then(result => { | |
| echo(`Test #${index} - Success handler #1b called with result = '${result}' (isArray = ${Array.isArray(result)}) (fulfillCalled = ${fulfillCalled})`); | |
| return 4; | |
| }).catch(err => { | |
| echo(`Test #${index} - Catch handler #1 called with err = ${err}`); | |
| }) | |
| ]; | |
| let fulfillCalled = false; | |
| Promise.all(promises).then(result => { | |
| fulfillCalled = true; | |
| echo(`Test #${index} - Success handler #2 called with result = '${result}' (length = ${result.length}) (isArray = ${Array.isArray(result)}) (fulfillCalled = ${fulfillCalled})`); | |
| }).catch((err) => { | |
| echo(`Test #${index} - Catch handler #2 called with err = ${err}`); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Promise from Promise.all never resolved if rejected promise in arguments", | |
| body: function (index) { | |
| var promises = [ | |
| Promise.resolve(0), | |
| tamper(Promise.resolve(1)), | |
| Promise.reject(2) | |
| ]; | |
| Promise.all(promises).then(result => { | |
| echo(`Test #${index} - Success handler #1 called with result = '${result}' (length = ${result.length}) (isArray = ${Array.isArray(result)})`); | |
| }, err => { | |
| echo(`Test #${index} - Error handler #1 called with err = ${err}`); | |
| }).catch(err => { | |
| echo(`Test #${index} - Catch handler #1 called with err = ${err}`); | |
| }); | |
| } | |
| }, | |
| { | |
| name: "Promise executor resolves with the first call resolve function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| resolve('success'); | |
| resolve('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise executor rejects with the first call reject function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| reject('success'); | |
| reject('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise executor resolves/rejects with the first call to either function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| resolve('success'); | |
| reject('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise executor rejects/resolves with the first call to either function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| reject('success'); | |
| resolve('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise executor rejects/resolves/rejects with the first call to either function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| reject('success'); | |
| resolve('failure'); | |
| reject('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise executor resolves/rejects/resolves with the first call to either function", | |
| body: function (index) { | |
| var p = new Promise(function(resolve,reject) { | |
| resolve('success'); | |
| reject('failure'); | |
| resolve('failure'); | |
| }); | |
| p.then( | |
| (res) => { echo(`Test #${index} - Success handler #1 called with res = '${res}'`); }, | |
| (err) => { echo(`Test #${index} - Error handler #1 called with err = '${err}'`); } | |
| ); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally - called for resolved promise", | |
| body: function (index) { | |
| var p = Promise.resolve("failure"); | |
| p.finally((arg) => { if(arg == "failure") { echo(`Test #${index} - Failed finally handler called with incorrect parameter = '${arg}'`); } | |
| else {echo(`Test #${index} - Success finally handler called for resolved promise without value`); } } ); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally - called for rejected promise", | |
| body: function (index) { | |
| var p = Promise.reject("failure"); | |
| p.finally((arg) => { if(arg == "failure") { echo(`Test #${index} - Failed finally handler called with incorrect parameter = '${arg}'`); } | |
| else { echo(`Test #${index} - Success finally handler called for rejected promise without value`); } } ); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally passes through result for Reject", | |
| body: function (index) { | |
| var p = Promise.reject("Result"); | |
| var final = p.finally((arg) => { return "failure" } ); | |
| final.then((e) => { echo(`Test #${index} - Failed - wrong status passed through finally`); }, | |
| (e) => { if(e == "Result") { echo(`Test #${index} - Success - Rejected status and value passed through finally`); } | |
| else { echo(`Test #${index} - Failed - wrong value passed through finally`); }}); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally passes through result for Resolve", | |
| body: function (index) { | |
| var p = Promise.resolve("Result"); | |
| var final = p.finally((arg) => { return "failure" } ); | |
| final.then((e) => { if(e == "Result") { echo(`Test #${index} - Success - Resolved status and value passed through finally`); } | |
| else { echo(`Test #${index} - Failed - wrong value passed through finally`); }}, | |
| (e) => { echo(`Test #${index} - Failed - wrong status passed through finally`); }); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally passes through result for Reject with not callable argument", | |
| body: function (index) { | |
| var p = Promise.reject("Result"); | |
| var final = p.finally("not callable"); | |
| final.then((e) => { echo(`Test #${index} - Failed - wrong status passed through finally`); }, | |
| (e) => { if(e == "Result") { echo(`Test #${index} - Success - Rejected status and value passed through finally with not callable argument`); } | |
| else { echo(`Test #${index} - Failed - wrong value passed through finally`); }}); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally passes through result for Resolve with not callable argument", | |
| body: function (index) { | |
| var p = Promise.resolve("Result"); | |
| var final = p.finally("not callable"); | |
| final.then((e) => { if(e == "Result") { echo(`Test #${index} - Success - Resolved status and value passed through finally with not callable argument`); } | |
| else { echo(`Test #${index} - Failed - wrong value passed through finally`); }}, | |
| (e) => { echo(`Test #${index} - Failed - wrong status passed through finally`); }); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally throws own rejection after a resolved promise", | |
| body: function (index) { | |
| var p = Promise.resolve("Result"); | |
| var final = p.finally(()=>{throw "own rejection";}); | |
| final.then((e) => { (e) => echo(`Test #${index} - Failed - wrong status passed through finally`); }, | |
| (e) => { if(e == "own rejection") { echo(`Test #${index} - Success - own rejection passed through finally`); } | |
| else {echo(`Test #${index} - Failed - wrong result ${e} passed through finally`); } } ); | |
| } | |
| }, | |
| { | |
| name: "Promise.prototype.finally throws own rejection after a rejected promise", | |
| body: function (index) { | |
| var p = Promise.reject("Result"); | |
| var final = p.finally(()=>{throw "own rejection";}); | |
| final.then((e) => { (e) => echo(`Test #${index} - Failed - wrong status passed through finally`); }, | |
| (e) => { if(e == "own rejection") { echo(`Test #${index} - Success - own rejection passed through finally`); } | |
| else {echo(`Test #${index} - Failed - wrong result ${e} passed through finally`); } } ); | |
| } | |
| }, | |
| { | |
| name: "Ensure Multiple then handlers on a single promise are executed in correct order", | |
| body: function (index) { | |
| let val = -7 | |
| let resolveFunc; | |
| const p = new Promise((resolve, reject) => { | |
| resolveFunc = resolve; | |
| }); | |
| p.then(() => { | |
| val = val * 3; | |
| }); | |
| p.then(() => { | |
| val = val + 21 | |
| }); | |
| p.then(() => { | |
| echo('Test #' + index + ' - val is ' + val + '(Expect 0)'); | |
| }); | |
| resolveFunc(); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled settles when all promises in iterable settle", | |
| body: function (index) { | |
| var iterable = [getAsyncResolvePromise('Test #' + index + ' - ', 'p1'), | |
| Promise.resolve('p2'), | |
| Promise.reject('p3'), | |
| getAsyncRejectPromise('Test #' + index + ' - ', 'p4'),]; | |
| Promise.allSettled(iterable).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled settles even if all promises reject", | |
| body: function (index) { | |
| var iterable = [getAsyncRejectPromise('Test #' + index + ' - ', 'p1'), | |
| Promise.reject('p2'), | |
| Promise.reject('p3')]; | |
| Promise.allSettled(iterable).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled settles immediately with an empty iterator", | |
| body: function (index) { | |
| var iterable = []; | |
| Promise.allSettled(iterable).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled doesn't settle if some promises don't settle", | |
| body: function (index) { | |
| var iterable = [Promise.resolve('p1'), new Promise(()=>{})]; | |
| Promise.allSettled(iterable).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled rejects immediately with an iterator that throws", | |
| body: function (index) { | |
| var objectWithIterator = { | |
| [Symbol.iterator]: function() { | |
| return { | |
| i: 0, | |
| next: function () { | |
| if (this.i > 2) | |
| { | |
| throw new TypeError('failure inside iterator'); | |
| } | |
| this.i++; | |
| return { | |
| done: this.i == 5, | |
| value: getAsyncResolvePromise('Test #' + index + ' - ', 'p' + this.i) | |
| }; | |
| } | |
| }; | |
| } | |
| }; | |
| Promise.allSettled(objectWithIterator).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled resolve and reject functions cannot be called multiple times", | |
| body: function(index) { | |
| let oldThen = Promise.prototype.then; | |
| try { | |
| var resolveFunc; | |
| var rejectFunc; | |
| Promise.prototype.then = function(resolve, reject) { | |
| console.log(`Test #${index} - Temp then handler called from Promise.allSettled`); | |
| // Stash resolve functions | |
| resolveFunc = resolve; | |
| rejectFunc = reject; | |
| // Reset Promise#then | |
| Promise.prototype.then = oldThen; | |
| // Call builtin Promise#then | |
| Promise.prototype.then.call(this, resolve, reject); | |
| } | |
| Promise.allSettled([getAsyncResolvePromise('Test #' + index + ' - ', 'p1')]).then( | |
| v => { | |
| console.log(`Test #${index} - Success: ${JSON.stringify(v)}`); | |
| // Call the stashed resolve function a second time. | |
| // It is a wrapper (defined in getAsyncResolvePromise) and will log something | |
| // but it does not trigger allSettled to fire. | |
| resolveFunc('p2'); | |
| rejectFunc('e2'); | |
| }, | |
| e => console.log(`Test #${index} - Error: ${e}`) | |
| ); | |
| } finally { | |
| // Reset Promise#then in case of failure | |
| Promise.prototype.then = oldThen; | |
| } | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled passes all elements of iterable to Promise.resolve", | |
| body: function (index) { | |
| var iterable = ['p1', 'p2']; | |
| Promise.allSettled(iterable).then( | |
| v => echo(`Test #${index} - Success - ${JSON.stringify(v)}`), | |
| e => echo(`Test #${index} - Failed - ${JSON.stringify(e)}`)); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled called with empty iterator, calls Promise.resolve synchronously and passes abrupt completion to reject handler", | |
| body: function (index) { | |
| function FakePromise(fn) { | |
| function resolve() { echo(`Test #${index} - resolve called`); throw new Error('oops'); } | |
| function reject(e) { echo(`Test #${index} - reject called: ${e.message}`) } | |
| fn(resolve, reject); | |
| this.then = function(onResolve, onReject) {}; | |
| } | |
| FakePromise.resolve = function() {}; | |
| Promise.allSettled.call(FakePromise, []); | |
| } | |
| }, | |
| { | |
| name: "Promise.allSettled gets the constructor's resolve function only once", | |
| body: function(index) { | |
| function FakePromise(fn) { | |
| fn(function() {}, function() {}); | |
| this.then = function(onResolve, onReject) {}; | |
| } | |
| Object.defineProperty(FakePromise, 'resolve', { | |
| get: function() { | |
| echo(`Test #${index} - get constructor resolve`); | |
| return function(x) { | |
| echo(`Test #${index} - constructor resolve called`); | |
| return Promise.resolve(x); | |
| }; | |
| } | |
| }); | |
| Promise.allSettled.call(FakePromise, [1, 2]); | |
| } | |
| }, | |
| { | |
| name: "Promise.all gets the constructor's resolve function only once", | |
| body: function(index) { | |
| function FakePromise(fn) { | |
| fn(function() {}, function() {}); | |
| this.then = function(onResolve, onReject) {}; | |
| } | |
| Object.defineProperty(FakePromise, 'resolve', { | |
| get: function() { | |
| echo(`Test #${index} - get constructor resolve`); | |
| return function(x) { | |
| echo(`Test #${index} - constructor resolve called`); | |
| return Promise.resolve(x); | |
| }; | |
| } | |
| }); | |
| Promise.all.call(FakePromise, [1, 2]); | |
| } | |
| }, | |
| { | |
| name: "Promise.race gets the constructor's resolve function only once", | |
| body: function(index) { | |
| function FakePromise(fn) { | |
| fn(function() {}, function() {}); | |
| this.then = function(onResolve, onReject) {}; | |
| } | |
| Object.defineProperty(FakePromise, 'resolve', { | |
| get: function() { | |
| echo(`Test #${index} - get constructor resolve`); | |
| return function(x) { | |
| echo(`Test #${index} - constructor resolve called`); | |
| return Promise.resolve(x); | |
| }; | |
| } | |
| }); | |
| Promise.race.call(FakePromise, [1, 2]); | |
| } | |
| } | |
| ]; | |
| var index = 1; | |
| function runTest(test) { | |
| echo('Executing test #' + index + ' - ' + test.name); | |
| try { | |
| test.body(index); | |
| } catch(e) { | |
| echo('Caught exception: ' + e); | |
| } | |
| index++; | |
| } | |
| tests.forEach(runTest); | |
| echo('\nCompletion Results:'); |