// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. // module CorsicaTests { "use strict"; var timeoutMax = 33 * 2; // expected duration for use with timeout() or timeout(0), 33ms was not always reliable var AsyncWorkQueue = function () { this._queue = []; this._token = 0; }; function errorHandler(msg) { try { LiveUnit.Assert.fail('There was an unhandled error in your test: ' + msg); } catch (ex) { } } AsyncWorkQueue.prototype = { cancel: function (token) { for (var i = 0, len = this._queue.length; i < len; i++) { if (this._queue[i].token === token) { this._queue[i].canceled = true; return true; } } return false; }, clear: function () { this._queue = [], this._token = 0; }, drain: function () { while (this._queue.length > 0) { this.process(); } }, getItem: function (pos) { return this._queue[pos]; }, process: function () { var len = this._queue.length; for (var i = 0; i < len; i++) { var item = this._queue[i]; if (item.canceled) { continue; } item.code(); } this._queue.splice(0, len); }, processN: function (cnt) { var len = Math.min(cnt || 0, this._queue.length); for (var i = 0; cnt > 0 && i < len; i++) { var item = this._queue[i]; if (item.canceled) { continue; } item.code(); cnt--; } this._queue.splice(0, len); }, schedule: function (item) { var token = ++this._token; this._queue.push({ code: item, token: token }); return token; } }; var q = new AsyncWorkQueue(); function asyncAdd(x, y): WinJS.Promise { return new WinJS.Promise(function (complete) { q.schedule(function () { complete(x + y); }, 0); }); } function asyncAddWithCancellation(x, y, onCancel?) { var token; return new WinJS.Promise( function (complete) { token = q.schedule(function () { complete(x + y); }, 0); }, function () { q.cancel(token); if (onCancel) { onCancel(); } } ); } function asyncAddWithProgress(x, y) { return new WinJS.Promise(function (complete, error, progress) { q.schedule( function () { progress("almost!"); q.schedule( function () { progress("really!"); q.schedule( function () { complete(x + y); }, 0 ); }, 0 ); }, 0 ); }); } function asyncAddWithError(x, y) { return new WinJS.Promise(function (complete, error) { q.schedule(function () { error("I refuse to do math!"); }, 0); }); } // This is how one would be able to protected a Promise from a cancelation request. // var NoncancelablePromise = function (promise) { return new WinJS.Promise(function (c, e, p) { promise.then(c, e, p); }); } function addAsyncNoQueue(l, r, options?): WinJS.Promise { // if options not passed in, define it as an empty object so we can just check if members are present options = options || {}; return new WinJS.Promise( function (complete, error) { try { if (options.throwException) { throw "addAsyncNoQueue throwing requested exception"; } var sum = l + r; complete(sum); } catch (e) { error(e); } } ); } export class Promise { testInitializationError = function () { q.clear(); var p = new WinJS.Promise(function () { throw "Error initializing"; }); var hitCompleteCount = 0, hitErrorCount = 0; p.then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Error initializing", e); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); } testCallingCompleteMultipleTimes = function () { q.clear(); var p = new WinJS.Promise(function (c) { q.schedule(function () { c(1); c(2); }); }); var hitCompleteCount = 0, hitErrorCount = 0; p.then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }, function () { hitErrorCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); } testCallingErrorMultipleTimes = function () { q.clear(); var p = new WinJS.Promise(function (c, e) { q.schedule(function () { e(1); e(2); }); }); var hitCompleteCount = 0, hitErrorCount = 0; p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(e, 1); } ); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); } testCallingCompleteAndThenError = function () { q.clear(); var p = new WinJS.Promise(function (c, e) { q.schedule(function () { c(1); e(2); }); }); var hitCompleteCount = 0, hitErrorCount = 0; p.then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }).then(null, function (e) { hitErrorCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); } testCallingErrorAndThenComplete = function () { q.clear(); var p = new WinJS.Promise(function (c, e) { q.schedule(function () { e(2); c(1); }); }); var hitCompleteCount = 0, hitErrorCount = 0; p. then(function (v) { hitCompleteCount++; }). then(null, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(2, e); }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); } testCallingCompleteAfterProgress = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(0); p(1); c(2); }); }); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; p. then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(2, v); }, function () { hitErrorCount++; }, function () { hitProgressCount++; } ); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(2, hitProgressCount); } testCallingProgressAfterComplete = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(0); c(2); p(1); }); }); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; p. then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(2, v); }, function () { hitErrorCount++; }, function (v) { hitProgressCount++; LiveUnit.Assert.areEqual(0, v); } ); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, hitProgressCount); } testCallingErrorAfterProgress = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(0); p(1); e(2); }); }); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; p. then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(2, e); }, function () { hitProgressCount++; } ); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(2, hitProgressCount); } testCallingProgressAfterError = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(0); e(2); p(1); }); }); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; p. then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(2, e); }, function (v) { hitProgressCount++; LiveUnit.Assert.areEqual(0, v); } ); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(1, hitProgressCount); } testThenPartialRegistration1 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; asyncAddWithProgress(1, 2). then(function () { hitCompleteCount++; }). then(null, function (e) { hitErrorCount++; throw e; }). then(null, null, function () { hitProgressCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); // Because the progress handler is registered after a complete or // error handler it doesn't see the progress. // LiveUnit.Assert.areEqual(0, hitProgressCount); } testThenPartialRegistration2 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; asyncAddWithProgress(1, 2). then(null, null, function () { hitProgressCount++; }). then(null, function (e) { hitErrorCount++; throw e; }). then(function () { hitCompleteCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(2, hitProgressCount); } testThenPartialRegistration3 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; asyncAddWithError(1, 2). then(null, function (e) { hitErrorCount++; throw e; }). then(null, null, function () { hitProgressCount++; }). then(function () { hitCompleteCount++; }). then(null, function () { }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, hitProgressCount); } testThenPartialRegistration4 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; asyncAddWithError(1, 2). then(null, null, function () { hitProgressCount++; }). then(function () { hitCompleteCount++; }). then(null, function (e) { hitErrorCount++; throw e; }). then(null, function () { }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, hitProgressCount); } testThenPartialRegistration5 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; asyncAddWithProgress(1, 2). then(null, function (e) { hitErrorCount++; throw e; }). then(null, null, function () { hitProgressCount++; }). then(function () { hitCompleteCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); // Because the progress handler is registered after a complete or // error handler it doesn't see the progress. // LiveUnit.Assert.areEqual(0, hitProgressCount); } testProgressIsNotBuffered = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(1); }); q.schedule(function () { p(2); }); q.schedule(function () { c(3); }); }); var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; q.processN(1); p.then( function () { hitCompleteCount++; }, function () { hitErrorCount++; }, function (v) { hitProgressCount++; LiveUnit.Assert.areEqual(2, v); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(0, hitProgressCount); q.processN(1); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, hitProgressCount); q.processN(1); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, hitProgressCount); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, hitProgressCount); } testProgressWaterfall = function () { q.clear(); var p = new WinJS.Promise(function (c, e, p) { q.schedule(function () { p(1); }); q.schedule(function () { c(2); }); }); var hitProgress1Count = 0, hitProgress2Count = 0, hitProgress3Count = 0, hitProgress4Count = 0, hitCompleteCount = 0; p.then(null, null, function (v) { hitProgress1Count++; LiveUnit.Assert.areEqual(1, v); }).then(null, null, function (v) { hitProgress2Count++; LiveUnit.Assert.areEqual(1, v); }).then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(2, v); }, null, function (v) { hitProgress3Count++; LiveUnit.Assert.areEqual(1, v); }).then(null, null, function (v) { hitProgress4Count++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitProgress1Count); LiveUnit.Assert.areEqual(1, hitProgress2Count); LiveUnit.Assert.areEqual(1, hitProgress3Count); LiveUnit.Assert.areEqual(0, hitProgress4Count); LiveUnit.Assert.areEqual(1, hitCompleteCount); } testCancellationOfConstantPromise = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAddWithCancellation(value, 1); }). then(function (value) { hitCompleteCount++; }, function (error) { hitErrorCount++; }). cancel(); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testCancellationOfInFlightPromise = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; asyncAddWithCancellation(1, 1). then(function (value) { return asyncAdd(value, 1); }). then(function (value) { hitCompleteCount++; }, function (error) { hitErrorCount++; }). cancel(); LiveUnit.Assert.isTrue(q.getItem(0).canceled); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testCancellationOfTree = function () { q.clear(); var cancelCount = 0; var onCancel = function () { cancelCount++; }; var p1 = asyncAddWithCancellation(1, 1, onCancel); q.drain(); LiveUnit.Assert.areEqual(0, cancelCount); p1 = p1.then(function (value) { return asyncAddWithCancellation(value, 3, onCancel); }); var p2 = asyncAddWithCancellation(1, 2, onCancel); var p3 = WinJS.Promise.join({ p1: p1, p2: p2 }). then(function (values) { return asyncAddWithCancellation(values, 4, onCancel); }); p3.cancel(); q.drain(); LiveUnit.Assert.areEqual(2, cancelCount); }; testPromiseDeadlock = function () { q.clear(); var hitCompleteCount = 0; var a, b; var c = asyncAdd(1, 2); a = c.then(function (v) { hitCompleteCount++; return b; }). then(function (v) { hitCompleteCount++; }); b = c.then(function (v) { hitCompleteCount++; return a; }). then(function (v) { hitCompleteCount++; }); q.drain(); // These promises deadlock on each other and never make forward progress. LiveUnit.Assert.areEqual(2, hitCompleteCount); }; testPromiseDeadlockCancellation = function () { q.clear(); var hitCompleteCount = 0, hitFirstErrorCount = 0, hitSecondErrorCount = 0; var c: any = asyncAdd(1, 2); var a0: any = c.then( function (v) { hitCompleteCount++; return b; }, function () { hitFirstErrorCount++; } ); var a: any = a0.then( function () { hitCompleteCount++; }, function (e) { hitSecondErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.message); throw e; } ); var b0: any = c.then( function (v) { hitCompleteCount++; return a; }, function () { hitFirstErrorCount++; } ) var b: any = b0.then( function () { hitCompleteCount++; }, function (e) { hitSecondErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.message); throw e; } ); c.name = "c"; a0.name = "a0"; a.name = "a"; b0.name = "b0"; b.name = "b"; q.drain(); // These promises deadlock on each other and never make forward progress. LiveUnit.Assert.areEqual(2, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitFirstErrorCount); LiveUnit.Assert.areEqual(0, hitSecondErrorCount); // Should cancel both. b.cancel(); LiveUnit.Assert.areEqual(2, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitFirstErrorCount); LiveUnit.Assert.areEqual(2, hitSecondErrorCount); }; testBlockingOfCancelation = function () { q.clear(); var hitCancelCount = 0, hitProtectedCompleteCount = 0, hitCompleteCount = 0, hitErrorCount = 0; var a = NoncancelablePromise( asyncAddWithCancellation(1, 2, function () { hitCancelCount++; }). then(function () { hitProtectedCompleteCount++; }) ).then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.message); } ); a.cancel(); q.drain(); LiveUnit.Assert.areEqual(0, hitCancelCount); LiveUnit.Assert.areEqual(1, hitProtectedCompleteCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); } testCancellationOnJoinOfSamePromise = function () { q.clear(); var hitCancelCount = 0, hitCompleteCount = 0, hitErrorCount = 0; var p = asyncAddWithCancellation(1, 2, function () { hitCancelCount++; }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }); WinJS.Promise.join([p, p, p]).cancel(); q.drain(); LiveUnit.Assert.areEqual(1, hitCancelCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testCycle = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; var p = asyncAdd(1, 2); p. then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(3, v); return p; }, function () { hitErrorCount++; } ). then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(3, v); return p; }, function () { hitErrorCount++; } ). then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(3, v); }, function () { hitErrorCount++; } ); q.drain(); LiveUnit.Assert.areEqual(3, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); }; testChainedAdds = function () { q.clear(); var hitCompleteCount = 0; WinJS.Promise.as(1). then(function (value) { hitCompleteCount++; return asyncAdd(value, 1); }). then(function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(2, value); return asyncAdd(value, 2); }). then(function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(4, value); return asyncAdd(value, 3); }). then(function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(7, value); }); q.drain(); LiveUnit.Assert.areEqual(4, hitCompleteCount); }; testError = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAddWithError(value, 1); }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; throw 1; }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(2, hitErrorCount); }; testErrorRecovery = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAddWithError(value, 1); }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; return 10; }). then(function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(10, value); }, function () { hitErrorCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testErrorWithFork = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; var p = WinJS.Promise.as(1). then(function (value) { return asyncAddWithError(value, 1); }); p.then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }); p.then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(2, hitErrorCount); }; testExceptionFromCompleteHandler = function () { q.clear(); var hit1stCompleteCount = 0, hit1stErrorCount = 0, hit2ndCompleteCount = 0, hit2ndErrorCount = 0; asyncAdd(1, 2). then( function () { hit1stCompleteCount++; throw 1; }, function () { hit1stErrorCount++; } ). then( function () { hit2ndCompleteCount++; }, function () { hit2ndErrorCount++; } ); q.drain(); LiveUnit.Assert.areEqual(1, hit1stCompleteCount); LiveUnit.Assert.areEqual(0, hit1stErrorCount); LiveUnit.Assert.areEqual(0, hit2ndCompleteCount); LiveUnit.Assert.areEqual(1, hit2ndErrorCount); }; testFallthroughComplete = function () { q.clear(); var hitCompleteCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAdd(value, 1); }). then(function (value) { LiveUnit.Assert.areEqual(2, value); hitCompleteCount++; return value; }). then(function (value) { LiveUnit.Assert.areEqual(2, value); hitCompleteCount++; return value; }). then(function (value) { LiveUnit.Assert.areEqual(2, value); hitCompleteCount++; return value; }); q.drain(); LiveUnit.Assert.areEqual(3, hitCompleteCount); }; testProgess = function () { q.clear(); var hitProgressCount = 0, hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAddWithProgress(value, 1); }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }, function () { hitProgressCount++; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(2, hitProgressCount); }; testProgressExceptionIsIgnored = function () { q.clear(); var hitProgressCount = 0, hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.as(1). then(function (value) { return asyncAddWithProgress(value, 1); }). then(function () { hitCompleteCount++; }, function () { hitErrorCount++; }, function () { hitProgressCount++; throw 1; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(2, hitProgressCount); } testPromise_As = function () { q.clear(); var p = asyncAdd(1, 2); LiveUnit.Assert.isTrue(p instanceof WinJS.Promise); LiveUnit.Assert.areEqual(p, WinJS.Promise.as(p)); var o = { a: 1 }; var p1 = WinJS.Promise.as(o); LiveUnit.Assert.areNotEqual(o, p1); var hitCompleteCount = 0; var p1Value; // This will run synchronously because it is already complete. p1.then(function (value) { hitCompleteCount++; p1Value = value; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.isTrue(o === p1Value); // The contract we look for in 'as' is the same as 'is', you must have // a function which is named 'then'. var o2 = { then: function () { } }; LiveUnit.Assert.isTrue(o2 === WinJS.Promise.as(o2)); }; testPromise_Any = function () { q.clear(); var hitCompleteCount = 0; var input = [ asyncAdd(1, 2), asyncAdd(3, 4) ]; WinJS.Promise.any(input). then(function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual(input[0], item.value); LiveUnit.Assert.areNotEqual(input[1], item.value); }); q.processN(1); LiveUnit.Assert.areEqual(1, hitCompleteCount); q.drain(); }; testPromise_AnyWithNonPromiseInput = function () { q.clear(); var hitCompleteCount = 0; var input: any[] = [ asyncAdd(1, 2), asyncAdd(3, 4), 5 ]; WinJS.Promise.any(input). then(function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual(5, item.value); LiveUnit.Assert.areEqual(input[2], item.value); LiveUnit.Assert.areNotEqual(input[0], item.value); LiveUnit.Assert.areNotEqual(input[1], item.value); }); LiveUnit.Assert.areEqual(1, hitCompleteCount); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); }; testPromise_AnyWithAllNonPromiseInput = function () { q.clear(); var hitCompleteCount = 0; var input: any[] = [ 3, 7, 5 ]; WinJS.Promise.any(input). then(function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual(3, item.value); LiveUnit.Assert.areEqual(input[0], item.value); LiveUnit.Assert.areNotEqual(input[1], item.value); LiveUnit.Assert.areNotEqual(input[2], item.value); }); LiveUnit.Assert.areEqual(1, hitCompleteCount); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); }; testPromise_AnyWithError = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; var input = [ asyncAddWithError(1, 2), asyncAdd(3, 4) ]; WinJS.Promise.any(input). then( function () { hitCompleteCount++; }, function (item) { hitErrorCount++; LiveUnit.Assert.areEqual(input[0], item.value); // One promise is fulfilled, remove it from the array, and block until // the other one is fulfilled input.splice(input.indexOf(item.value), 1); return WinJS.Promise.any(input); } ). then( function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual(input[0], item.value); }, function () { hitErrorCount++; } ); q.processN(1); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testPromise_AnyWithError2 = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; var p0 = asyncAddWithError(1, 2); var p1 = asyncAdd(3, 4); // Make sure the later element in the array completes earlier. // var input = [p1, p0]; WinJS.Promise.any(input). then( function () { hitCompleteCount++; }, function (item) { hitErrorCount++; LiveUnit.Assert.areEqual(input[1], item.value); // One promise is fulfilled, remove it from the array, and block until // the other one is fulfilled input.splice(input.indexOf(item.value), 1); return WinJS.Promise.any(input); } ). then( function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual(input[0], item.value); }, function () { hitErrorCount++; } ); q.processN(1); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testPromise_AnyCancellation = function () { q.clear(); var hitCancelCount = 0, hitCompleteCount = 0, hitErrorCount = 0; var p0 = asyncAddWithCancellation(1, 2, function () { hitCancelCount++; }); var p1 = asyncAddWithCancellation(1, 2, function () { hitCancelCount++; }); var a = WinJS.Promise.any([p0, p1]). then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.message); } ); a.cancel(); LiveUnit.Assert.areEqual(2, hitCancelCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); q.drain(); WinJS.Promise.any([p0, p1]). then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.message); } ); LiveUnit.Assert.areEqual(2, hitCancelCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(2, hitErrorCount); q.drain(); LiveUnit.Assert.areEqual(2, hitCancelCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(2, hitErrorCount); }; testPromise_AnyWithRecord = function () { q.clear(); var hitCompleteCount = 0; var input = { first: asyncAdd(1, 2), second: asyncAdd(3, 4) }; WinJS.Promise.any(input). then(function (item) { hitCompleteCount++; LiveUnit.Assert.areEqual("first", item.key); LiveUnit.Assert.areEqual(input.first, item.value); LiveUnit.Assert.areNotEqual(input.second, item.value); }); q.processN(1); LiveUnit.Assert.areEqual(1, hitCompleteCount); q.drain(); } testPromise_Is = function () { LiveUnit.Assert.isTrue(WinJS.Promise.is(WinJS.Promise.wrap(12))); LiveUnit.Assert.isFalse(WinJS.Promise.is(12)); LiveUnit.Assert.isTrue(WinJS.Promise.is({ then: function () { return 1; } })); LiveUnit.Assert.isFalse(WinJS.Promise.is({ then: 1 })); }; testPromise_wrapCancellation = function () { var hitCompleteCount = 0; var a = WinJS.Promise.wrap(1); a.cancel(); a.then(function () { hitCompleteCount++; }); LiveUnit.Assert.areEqual(1, hitCompleteCount); }; testPromise_wrapErrorCancellation = function () { var hitCompleteCount = 0, hitErrorCount = 0; var a = WinJS.Promise.wrapError(1); a.cancel(); a.then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(1, e); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testPromise_Join = function () { q.clear(); var hitCompleteCount = 0; var result; WinJS.Promise.join({ first: asyncAdd(1, 2), second: asyncAdd(3, 4) }). then(function (value) { hitCompleteCount++; result = value.first + value.second; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(10, result); }; testPromise_JoinProgress = function () { q.clear(); var hitCompleteCount = 0, hitProgressCount = 0; var result; var input = { first: asyncAddWithProgress(1, 2), second: asyncAddWithProgress(3, 4), third: asyncAddWithProgress(6, 7) }; WinJS.Promise.join(input). then( function (value) { hitCompleteCount++; result = value.first + value.second + value.third; }, null, function (value) { LiveUnit.Assert.areEqual(Object.keys(input)[hitProgressCount], value.Key); hitProgressCount++; } ); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); // We don't get a progress callback for the last item to complete. // LiveUnit.Assert.areEqual(2, hitProgressCount); LiveUnit.Assert.areEqual(23, result); }; testPromise_JoinWithArray = function () { q.clear(); var hitCompleteCount = 0; var result; WinJS.Promise.join([ asyncAdd(1, 2), asyncAdd(3, 4) ]). then(function (value) { hitCompleteCount++; result = value[0] + value[1]; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(10, result); }; testPromise_JoinWithError = function () { q.clear(); var hitCompleteCount = 0, hitErrorCount = 0; WinJS.Promise.join({ first: asyncAdd(1, 2), second: asyncAddWithError(3, 4) }). then(function (value) { hitCompleteCount++; return 12; }). then(null, function (error) { hitErrorCount++; LiveUnit.Assert.isTrue(error.second !== undefined); LiveUnit.Assert.isTrue(error.first === undefined); }); q.drain(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testPromise_JoinWithNonPromiseInput = function () { q.clear(); var hitCompleteCount = 0; var result; WinJS.Promise.join({ first: asyncAdd(1, 2), second: asyncAdd(3, 4), third: 24 }). then(function (value) { hitCompleteCount++; result = value.first + value.second + value.third; }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(34, result); }; testPromise_JoinWithAllNonPromiseInput = function () { q.clear(); var hitCompleteCount = 0; var result; WinJS.Promise.join({ first: 3, second: 7, third: 24 }). then(function (value) { hitCompleteCount++; result = value.first + value.second + value.third; }); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(34, result); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(34, result); }; testPromise_Then = function () { q.clear(); var hitCompleteCount = 0; var p = WinJS.Promise.as(1); p = WinJS.Promise.then(p, function (value) { hitCompleteCount++; return asyncAdd(value, 1); }); p = WinJS.Promise.then(p, function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(2, value); return asyncAdd(value, 2); }); p = WinJS.Promise.then(p, function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(4, value); return asyncAdd(value, 3); }); p = WinJS.Promise.then(p, function (value) { hitCompleteCount++; LiveUnit.Assert.areEqual(7, value); }); q.drain(); LiveUnit.Assert.areEqual(4, hitCompleteCount); }; // @TODO, how do we do this? Include jQuery or dojo in the test tree? testInterop = function () { }; testTimeoutNoWait = function (complete) { var i = 0; var hit1 = false, hit2 = false; WinJS.Utilities._setImmediate(function () { hit1 = true; }); // calling timeout() without a parameter results in calling WinJS.Utilities._setImmediate() which will // execute immediately after the browser has processed outstanding work. WinJS.Promise.timeout().then(function () { i++; LiveUnit.Assert.areEqual(1, i); LiveUnit.Assert.isTrue(hit1, "expected to run after the above explicit WinJS.Utilities._setImmediate"); LiveUnit.Assert.isFalse(hit2, "expected to run before the below explicit WinJS.Utilities._setImmediate"); }). then(null, errorHandler). then(complete); LiveUnit.Assert.areEqual(0, i); WinJS.Utilities._setImmediate(function () { hit2 = true; }); }; testTimeoutOfPromises = function (complete) { var p = new WinJS.Promise(function () { }); var hitCompleteCount = 0, hitErrorCount = 0; p.then( function () { hitCompleteCount++; LiveUnit.Assert.fail("Should not complete"); }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.name); }); WinJS.Promise.timeout(0, p). then( function () { hitCompleteCount++; LiveUnit.Assert.fail("Should not complete"); }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("Canceled", e.name); } ). then(function () { LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(2, hitErrorCount); }). then(null, errorHandler). then(complete); }; testTimeoutOfPromisesWhichComplete = function (complete) { var completeP; var p = new WinJS.Promise(function (c) { completeP = c; }); var hitCompleteCount = 0, hitErrorCount = 0; p.then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }, function () { hitErrorCount++; LiveUnit.Assert.fail("should not error"); }); WinJS.Promise.timeout(0, p). then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }, function () { hitErrorCount++; LiveUnit.Assert.fail("should not errror"); } ). then(function () { LiveUnit.Assert.areEqual(2, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); }). then(null, errorHandler). then(complete); completeP(1); }; testTimeoutZeroWait = function (complete) { var i = 0; var start = new Date(); // We were seeing some occaisional failures when expecting delay < 33ms so using larger value now // to increase test reliability. WinJS.Promise.timeout(0).then(function () { i++; LiveUnit.Assert.areEqual(1, i); var delay = new Date().valueOf() - start.valueOf(); LiveUnit.Assert.isTrue(delay < timeoutMax, "expected timeout < " + timeoutMax + ", got=" + delay); }). then(null, errorHandler). then(complete); LiveUnit.Assert.areEqual(0, i); }; testTimeoutWait = function (complete) { var timeoutDuration = 100; var i = 0; var start = new Date(); // expecting this timeout to fire within 50% of requested time out at the earliest WinJS.Promise.timeout(timeoutDuration).then(function () { i++; LiveUnit.Assert.areEqual(1, i); var delay = new Date().valueOf() - start.valueOf(); LiveUnit.Assert.isTrue(delay >= timeoutDuration / 2, "expected delay to be at least " + timeoutDuration / 2 + "; got actual delay=" + delay); }). then(null, errorHandler). then(complete); LiveUnit.Assert.areEqual(0, i); }; testTimeoutNoWaitCancel = function (complete) { var i = 0; var a = WinJS.Promise.timeout().then(function () { i++; LiveUnit.Assert.fail("this should never get called!"); }); WinJS.Promise.timeout(35).then(function () { i++; LiveUnit.Assert.areEqual(1, i); }). then(null, errorHandler). then(complete); a.cancel(); LiveUnit.Assert.areEqual(0, i); }; testTimeoutWaitCancel = function (complete) { var i = 0; var a = WinJS.Promise.timeout(25).then(function () { i++; LiveUnit.Assert.fail("this should never get called!"); }); WinJS.Promise.timeout(35).then(function () { i++; LiveUnit.Assert.areEqual(1, i); }). then(null, errorHandler). then(complete); a.cancel(); LiveUnit.Assert.areEqual(0, i); }; testThenEach = function (complete) { q.clear(); WinJS.Promise.thenEach([asyncAdd(1, 2), asyncAdd(3, 4), asyncAdd(5, 6)], function (value) { LiveUnit.Assert.areEqual("number", typeof value); return value; }). then(function (values) { LiveUnit.Assert.areEqual(3, values[0]); LiveUnit.Assert.areEqual(7, values[1]); LiveUnit.Assert.areEqual(11, values[2]); }). then(null, errorHandler). then(complete); q.drain(); } testCompletePromise = function () { var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; var c = new (WinJS.Promise).wrap(1); var r1 = c.then( function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }, function () { hitErrorCount++; }, function () { hitProgressCount++; } ); var r2 = r1.then( function () { hitCompleteCount++; } ); LiveUnit.Assert.isTrue(r1 === r2, "Because of our optimization in the sync completion path we should not allocate a new promise unless there is a new return value"); LiveUnit.Assert.areEqual(2, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(0, hitProgressCount); } testErrorPromise = function () { var hitCompleteCount = 0, hitErrorCount = 0, hitProgressCount = 0; var c = new (WinJS.Promise).wrapError(1); c.then( function () { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual(1, e); }, function () { hitProgressCount++; } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, hitProgressCount); } testOnErrorHandler = function () { q.clear(); var hitCount = 0; WinJS.Promise.onerror = function (evt) { LiveUnit.Assert.isTrue(!!evt.detail.error); hitCount++; }; var errorPromise = WinJS.Promise.wrapError(1); LiveUnit.Assert.areEqual(1, hitCount); var p = new WinJS.Promise(function (c, e) { q.schedule(function () { e(1); }); }); LiveUnit.Assert.areEqual(1, hitCount); q.drain(); LiveUnit.Assert.areEqual(2, hitCount); var hitComplete = false; p.then(null, function (value) { return 7; }).then(function (value) { hitComplete = true; LiveUnit.Assert.areEqual(7, value); }); LiveUnit.Assert.isTrue(hitComplete); LiveUnit.Assert.areEqual(3, hitCount); } testOnErrorDoesNotSeeCancelation = function () { q.clear(); var hitCount = 0; WinJS.Promise.onerror = function (evt) { LiveUnit.Assert.isTrue(!!evt.detail.error); hitCount++; }; try { LiveUnit.Assert.areEqual(0, hitCount); var p = new WinJS.Promise(function (c, e) { q.schedule(function () { e(1); }); }); LiveUnit.Assert.areEqual(0, hitCount); var completedHitCount = 0; var canceledHitCount = 0; p.then( function () { completedHitCount++; }, function (e) { if (e.name === "Canceled") { canceledHitCount++; } } ); p.cancel(); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(1, canceledHitCount); LiveUnit.Assert.areEqual(0, completedHitCount); q.drain(); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(1, canceledHitCount); LiveUnit.Assert.areEqual(0, completedHitCount); } finally { WinJS.Promise.onerror = undefined; } } testOnErrorHandlerPolicyWithHandledError = function (complete) { var onerrorCallbackCount = 0; var unhandledAfterPostCount = 0; var unhandledErrors = []; WinJS.Promise.onerror = function (evt) { onerrorCallbackCount++; var details = evt.detail; var id = details.id; if (!details.parent) { unhandledErrors[id] = details; WinJS.Promise.timeout().then(function () { var error = unhandledErrors[id]; if (error) { // // in real code here we would either log or rethrow the exception // unhandledAfterPostCount++; delete unhandledErrors[id] } }); } else if (details.handler) { delete unhandledErrors[id]; } }; var p: any = new WinJS.Promise(function (c, e) { e("This promise is broken"); }); LiveUnit.Assert.areEqual(1, onerrorCallbackCount); LiveUnit.Assert.areEqual(1, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(0, unhandledAfterPostCount); var hitCompleteCount = 0; var hitErrorCount = 0; var p = p. then(function (v) { /* never get here */ hitCompleteCount++; }). then(function (v) { /* never get here either */ hitCompleteCount++; }); // since we optimize the case where a promise has already been completed in error and // no error handler was specified we will only see a single onerrorCallbackCount LiveUnit.Assert.areEqual(1, onerrorCallbackCount); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(0, unhandledAfterPostCount); p. then(null, function (v) { hitErrorCount++; return 1; }). then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }); LiveUnit.Assert.areEqual(2, onerrorCallbackCount); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(0, unhandledAfterPostCount); WinJS.Promise.timeout().then(function () { LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(0, unhandledAfterPostCount); WinJS.Promise.onerror = undefined; complete(); }); } testOnErrorHandlerPolicyWithUnhandledError = function (complete) { var unhandledAfterPostCount = 0; var unhandledErrors = []; WinJS.Promise.onerror = function (evt) { var details = evt.detail; var id = details.id; if (!details.parent) { unhandledErrors[id] = details; WinJS.Promise.timeout().then(function () { var error = unhandledErrors[id]; if (error) { // // in real code here we would either log or rethrow the exception // unhandledAfterPostCount++; delete unhandledErrors[id] } }); } else if (details.handler) { delete unhandledErrors[id]; } }; var p = new WinJS.Promise(function (c, e) { e("This promise is broken"); }); var hitCompleteCount = 0; var hitErrorCount = 0; var p = p. then(function (v) { /* never get here */ hitCompleteCount++; }). then(function (v) { /* never get here either */ hitCompleteCount++; }); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(1, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(0, unhandledAfterPostCount); var errorHandler = function () { return true; } WinJS.Application.addEventListener("error", errorHandler); WinJS.Promise.timeout().then(function () { LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); // After hitting the unhandled handler it cleared out the unhandled error LiveUnit.Assert.areEqual(0, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(1, unhandledAfterPostCount); p. then(null, function (v) { hitErrorCount++; return 1; }). then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(1, v); }); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(1, unhandledAfterPostCount); WinJS.Promise.timeout().then(function () { LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, Object.keys(unhandledErrors).length); LiveUnit.Assert.areEqual(1, unhandledAfterPostCount); WinJS.Promise.onerror = undefined; WinJS.Application.removeEventListener("error", errorHandler); complete(); }); }); } // Testing basic error functionality and throwing an error inside complete testBasicOnErrorFunctionality = function () { var errorHitCount = 0, completeHitCount = 0; var onErrorHitCount = 0; try { WinJS.Promise.onerror = function (evt) { onErrorHitCount++; var detail = evt.detail; if (!!detail.parent) LiveUnit.Assert.isTrue(!!detail.handler); LiveUnit.Assert.isTrue(!!detail.id); LiveUnit.Assert.isTrue(!!detail.exception); LiveUnit.Assert.isTrue(!detail.error); } var p = new WinJS.Promise(function () { throw "exception inside function"; }). then(function () { completeHitCount++; }, function () { errorHitCount++; }). then( function () { LiveUnit.Assert.areEqual(1, errorHitCount); LiveUnit.Assert.areEqual(0, completeHitCount); LiveUnit.Assert.areEqual(2, onErrorHitCount); } ); } finally { WinJS.Promise.onerror = undefined; } } // Test removing the onerror handler before an error takes place and make sure that onerror will not be called testUnRegisterHandlerBeforeCallingPromise = function () { var handlerHitCount = 0; var errroHandler = function () { return true; } WinJS.Application.addEventListener("error", errorHandler); WinJS.Promise.onerror = function (evt) { var detail = evt.detail; handlerHitCount++; } try { WinJS.Promise.onerror = undefined; var p = new WinJS.Promise(function () { throw "exception inside function"; }, function () { }). then(function () { LiveUnit.Assert.areEqual(0, handlerHitCount); }); } finally { WinJS.Application.removeEventListener("error", errorHandler); WinJS.Promise.onerror = undefined; } } // Making sure that the errorID is the same for all the promise tree testVerifySameErrorId = function () { var handlerHitCount = 0; var id = undefined; WinJS.Promise.onerror = function (evt) { var detail = evt.detail; id = id || detail.id; LiveUnit.Assert.areEqual(id, detail.id); handlerHitCount++; } try { var p = new WinJS.Promise(function () { throw "error"; }). then(function () { }, function () { }). then(function () { LiveUnit.Assert.areEqual(2, handlerHitCount); }); } finally { WinJS.Promise.onerror = undefined; } } testThrowingUndefined = function () { WinJS.Promise.onerror = function (evt) { var detail = evt.detail; LiveUnit.Assert.isTrue(!detail.exception); LiveUnit.Assert.areEqual("undefined", typeof detail.exception); } try { var p = new WinJS.Promise(function () { throw undefined; }); } finally { WinJS.Promise.onerror = undefined; } } testThrowingNull = function () { WinJS.Promise.onerror = function (evt) { var detail = evt.detail; LiveUnit.Assert.areEqual(null, detail.exception); LiveUnit.Assert.areEqual("object", typeof detail.exception); } try { var p = new WinJS.Promise(function () { throw null; }); } finally { WinJS.Promise.onerror = undefined; } } testThrowingNumber = function () { WinJS.Promise.onerror = function (evt) { var detail = evt.detail; LiveUnit.Assert.areEqual(1, detail.exception); LiveUnit.Assert.areEqual("number", typeof detail.exception); } try { var p = new WinJS.Promise(function () { throw 1; }); } finally { WinJS.Promise.onerror = undefined; } } testThrowingObject = function () { WinJS.Promise.onerror = function (evt) { var detail = evt.detail; var temp = detail.exception; var type = typeof temp; LiveUnit.Assert.areEqual("object", type); LiveUnit.Assert.areEqual(10, temp.x); } try { var p = new WinJS.Promise(function () { throw { x: 10 }; }); } finally { WinJS.Promise.onerror = undefined; } } // Making sure that different IDs are generated for errors produced by different promise trees testDifferentIdsForDifferentPromises = function () { var handlerHitCount = 0; var id = undefined; WinJS.Promise.onerror = function (evt) { var detail = evt.detail; handlerHitCount++; if (!detail.parent) { if (id) LiveUnit.Assert.areNotEqual(id, detail.id); else id = detail.id; } } try { var p1 = new WinJS.Promise(function () { throw "exception inside function"; }). then(function () { }, function () { }). then(function () { }); var p2 = new WinJS.Promise(function () { }). then(function () { LiveUnit.Assert.areEqual(4, handlerHitCount) }); //To make sure that the onerror is called 4 times; } finally { WinJS.Promise.onerror = undefined; } } // Making sure that same object is passed to onerror for an error on the same promise tree testVerifySameObjectInMultipleErrorHandlers = function () { function Equal(evt1, evt2) { for (var i in evt1) { if (typeof evt1[i] === "object") return Equal(evt1[i], evt2[i]); else if (typeof evt1[i] !== "object" && evt1[i] !== evt2[i]) return false; } return true; } function eventHandler1(evt) { detail1 = evt.detail; handlerHitCount++; } function eventHandler2(evt) { detail2 = evt.detail; LiveUnit.Assert.isTrue(Equal(detail1, detail2)); handlerHitCount++; } function eventHandler3(evt) { detail3 = evt.detail; LiveUnit.Assert.isTrue(Equal(detail3, detail2)); handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler1); WinJS.Promise.addEventListener("error", eventHandler2); WinJS.Promise.addEventListener("error", eventHandler3); try { var detail1, detail2, detail3; var handlerHitCount = 0, hitCount = 0; var p = new WinJS.Promise(function () { throw "Error initializing"; }); p.then(function () { hitCount++; }, function (e) { }); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(6, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler1); WinJS.Promise.removeEventListener("error", eventHandler2); WinJS.Promise.removeEventListener("error", eventHandler3); } } //Making sure that error property exists in the parameter and exception does not exist testCorrectnessOfErrors = function () { var errorString = "This is an error"; WinJS.Promise.onerror = function (evt) { var detail = evt.detail; LiveUnit.Assert.isTrue(!detail.exception); LiveUnit.Assert.isTrue(!!detail.error); LiveUnit.Assert.isTrue(!!detail.id); LiveUnit.Assert.areEqual(errorString, detail.error); } try { var p = new WinJS.Promise(function (c, e) { e(errorString); }); } finally { WinJS.Promise.onerror = undefined; } } // Testing parent list and making sure that the parent is falsy only in case of the root // This will still work work the first time because the parent is undefined for the initial error testOnErrorParentsList = function () { var parent = undefined; WinJS.Promise.onerror = function (evt) { var detail = evt.detail; LiveUnit.Assert.areEqual(parent, detail.parent); parent = detail.promise; } try { var p = new WinJS.Promise(function () { throw "exception inside function"; }). then(function () { }). then(function () { }). then(null, function () { }); } finally { WinJS.Promise.onerror = undefined; } } testOnErrorParameterProperties = function () { var exceptionStr = "exception inside function"; WinJS.Promise.onerror = function (evt) { var detail = evt.detail; if (!detail.parent) { LiveUnit.Assert.isTrue(!!detail.exception); LiveUnit.Assert.isTrue(!detail.handler); LiveUnit.Assert.isTrue(!detail.error); LiveUnit.Assert.areEqual(exceptionStr, detail.exception); } } try { var p = new WinJS.Promise(function () { throw exceptionStr; }). then(function () { }, function () { }); } finally { WinJS.Promise.onerror = undefined; } } // Throwing an error in error handler // This will result in throwing a new error with a new id which will result in a new a tree of onerror function call testThrowingAnErrorInErrorHandler = function () { var handlerHitCount = 0, hitCount = 0; var str; function eventHandler(evt) { handlerHitCount++; var detail = evt.detail; str = str || detail.exception; LiveUnit.Assert.areEqual(str, detail.exception); } WinJS.Promise.addEventListener("error", eventHandler); try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then(function () { hitCount++; }, function (e) { throw e; }). then(function () { hitCount++; }, function (e) { throw e; }). then(function () { hitCount++; }, function (e) { throw e; }); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(4, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler); } } // Testing an error chain testHugeErrorWithChain = function () { var handlerHitCount = 0, hitCount = 0; function eventHandler(evt) { handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler); try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then(function () { hitCount++; }). then(function () { hitCount++; }). then(null, function (e) { throw e; }); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(2, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler); } } // Testing unregistering the onerror inside an error chain by using removeEventListener testUnregisterInHugeChain = function () { var handlerHitCount = 0, hitCount = 0; function eventHandler(evt) { handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler); try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then( function () { hitCount++; }, function (e) { throw e; } ). then( function () { hitCount++; }, function (e) { WinJS.Promise.removeEventListener("error", eventHandler); throw e; } ). then( function () { hitCount++; }, function (e) { throw e; } ); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(3, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler); } } // Testing unregistering the onerror inside an error chain by setting onerror to undefined testSettingOnErrorToUndefinedInHugeChain = function () { var handlerHitCount = 0, hitCount = 0; WinJS.Promise.onerror = function (evt) { handlerHitCount++; } try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then( function () { hitCount++; }, function (e) { throw e; } ). then( function () { hitCount++; }, function (e) { WinJS.Promise.onerror = undefined; throw e; } ). then( function () { hitCount++; }, function (e) { } ); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(3, handlerHitCount); } finally { WinJS.Promise.onerror = undefined; } } // Testing chaining when the then does not have an error handler so the error or the exception // gets chained to the next then so that it can be handled by an errorHandler testSettingOnErrorToUndefinedInHugeChain2 = function () { var handlerHitCount = 0, hitCount = 0; WinJS.Promise.onerror = function (evt) { handlerHitCount++; } try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then(function () { hitCount++; }). then( function () { hitCount++; }, function (e) { WinJS.Promise.onerror = undefined; throw e; } ). then(function () { hitCount++; }, function (e) { }); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(2, handlerHitCount); } finally { WinJS.Promise.onerror = undefined; } } testRemovingOnErrorInHugeChain2 = function () { var handlerHitCount = 0, hitCount = 0; function eventHandler(evt) { handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler); try { var p = new WinJS.Promise(function () { throw "Error initializing"; }); p. then(function () { hitCount++; }). then( function () { hitCount++; }, function (e) { WinJS.Promise.removeEventListener("error", eventHandler); throw e; } ). then(function () { hitCount++; }, function (e) { }); LiveUnit.Assert.areEqual(0, hitCount); LiveUnit.Assert.areEqual(2, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler); } } // adding multiple listeners to onerror and make sure that they all get called testAddingMultipleListeners = function () { function eventHandler1(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } function eventHandler2(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } function eventHandler3(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler1); WinJS.Promise.addEventListener("error", eventHandler1); WinJS.Promise.addEventListener("error", eventHandler2); WinJS.Promise.addEventListener("error", eventHandler3); try { var handlerHitCount = 0; var p = new WinJS.Promise(function () { throw "Error initializing"; }); var hitCompleteCount = 0; p. then(function () { hitCompleteCount++; }, function (e) { }); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(3, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler1); WinJS.Promise.removeEventListener("error", eventHandler2); WinJS.Promise.removeEventListener("error", eventHandler3); } } // deleting the last listener of the error and make sure that it won't get called in future errors testDeletingMultipleErrorHandlers = function () { function eventHandler1(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } function eventHandler2(evt) { var detail = evt.detail; WinJS.Promise.removeEventListener("error", eventHandler3); if (!detail.parent) handlerHitCount++; } function eventHandler3(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler1); WinJS.Promise.addEventListener("error", eventHandler2); WinJS.Promise.addEventListener("error", eventHandler3); try { var handlerHitCount = 0; var p = new WinJS.Promise(function () { throw "Error initializing"; }); var hitCompleteCount = 0; p. then(function () { hitCompleteCount++; }, function (e) { }); var p2 = new WinJS.Promise(function () { throw "Error initializing 2"; }); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(5, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler1); WinJS.Promise.removeEventListener("error", eventHandler2); WinJS.Promise.removeEventListener("error", eventHandler3); } } // deleting the listener in the middle of the onerror and make sure that it won't get called in future errors testDeletingMultipleErrorHandlers2 = function () { function eventHandler1(evt) { var detail = evt.detail; WinJS.Promise.removeEventListener("error", eventHandler2); if (!detail.parent) handlerHitCount++; } function eventHandler2(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } function eventHandler3(evt) { var detail = evt.detail; if (!detail.parent) handlerHitCount++; } WinJS.Promise.addEventListener("error", eventHandler1); WinJS.Promise.addEventListener("error", eventHandler2); WinJS.Promise.addEventListener("error", eventHandler3); try { var handlerHitCount = 0; var p = new WinJS.Promise(function () { throw "Error initializing"; }); var hitCompleteCount = 0; var p2 = new WinJS.Promise(function () { throw "Error initializing2"; }); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(5, handlerHitCount); } finally { WinJS.Promise.removeEventListener("error", eventHandler1); WinJS.Promise.removeEventListener("error", eventHandler2); WinJS.Promise.removeEventListener("error", eventHandler3); } } testAddingFinally = function () { Object.getPrototypeOf(WinJS.Promise.prototype).cleanup = function (func) { return this.then( function (v) { func(); return v; }, function (e) { func(); throw e; } ); }; q.clear(); var hitCompleteCount = 0; var hitFirstFinallyCount = 0; var hitSecondFinallyCount = 0; var hitErrorCount = 0; (asyncAdd(1, 2)). cleanup(function () { hitFirstFinallyCount++; // the return value from cleanup is irrelevant return 5; }). then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(3, v); throw "MyError"; }). cleanup(function () { hitSecondFinallyCount++; }). then(null, function (e) { hitErrorCount++; LiveUnit.Assert.areEqual("MyError", e); }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitFirstFinallyCount); LiveUnit.Assert.areEqual(1, hitSecondFinallyCount); LiveUnit.Assert.areEqual(1, hitErrorCount); } testAddingAlways = function () { Object.getPrototypeOf(WinJS.Promise.prototype).always = function (func) { return this.then(func, func); }; q.clear(); var hitCompleteCount = 0; (asyncAdd(1, 2)). always(function () { return 5; }). then(function (v) { hitCompleteCount++; LiveUnit.Assert.areEqual(5, v); }); q.drain(); LiveUnit.Assert.areEqual(1, hitCompleteCount); } testDoneSimple = function () { var old = WinJS.Promise['_doneHandler']; try { WinJS.Promise['_doneHandler'] = function (v) { throw v; }; var p: any = new WinJS.Promise(function (c, e) { e(1); }); var hitErrorCount = 0; p = p.then(null, function (e) { LiveUnit.Assert.areEqual(1, e); hitErrorCount++; throw e; }); LiveUnit.Assert.areEqual(1, hitErrorCount); var hitCatchCount = 0; try { p.done(); } catch (ex) { hitCatchCount++; LiveUnit.Assert.areEqual(1, ex); } LiveUnit.Assert.areEqual(1, hitCatchCount); } finally { WinJS.Promise['_doneHandler'] = old; } }; testDoneAsynchronous = function () { var old = WinJS.Promise['_doneHandler']; try { WinJS.Promise['_doneHandler'] = function (v) { throw v; }; q.clear(); var p: any = new WinJS.Promise(function (c, e) { q.schedule(function () { e(1) }); }); var hitErrorCount = 0; p = p.then(null, function (e) { LiveUnit.Assert.areEqual(1, e); hitErrorCount++; throw e; }); LiveUnit.Assert.areEqual(0, hitErrorCount); var hitCatchCount = 0; try { p.done(); } catch (ex) { hitCatchCount++; } LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(0, hitCatchCount); // The exception will be thrown while the queue is draining try { q.drain(); } catch (ex) { hitCatchCount++; LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(1, ex); } LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(1, hitCatchCount); } finally { WinJS.Promise['_doneHandler'] = old; } }; testDoneDoesNotLetCancelEscape = function () { var old = WinJS.Promise['_doneHandler']; try { WinJS.Promise['_doneHandler'] = function (v) { throw v; }; q.clear(); var p: any = new WinJS.Promise(function (c, e) { q.schedule(function () { e(1) }); }); var hitErrorCount = 0; p = p.then(null, function (e) { LiveUnit.Assert.isTrue(e instanceof Error); LiveUnit.Assert.areEqual("Canceled", e.name); hitErrorCount++; throw e; }); LiveUnit.Assert.areEqual(0, hitErrorCount); var hitCatchCount = 0; try { p.done(); } catch (ex) { hitCatchCount++; } LiveUnit.Assert.areEqual(0, hitErrorCount); LiveUnit.Assert.areEqual(0, hitCatchCount); p.cancel(); // The exception will be thrown while the queue is draining try { q.drain(); } catch (ex) { hitCatchCount++; } LiveUnit.Assert.areEqual(1, hitErrorCount); LiveUnit.Assert.areEqual(0, hitCatchCount); } finally { WinJS.Promise['_doneHandler'] = old; } }; testDoneChainedSuccess = function (complete) { // test success case where done() chained to successful promise addAsyncNoQueue(1, 2). then(function (result) { LiveUnit.Assert.areEqual(3, result); return result; }). done(function (result) { LiveUnit.Assert.areEqual(3, result); complete(); }); } testErrorHandledDoneSuccess = function (complete) { // generate error in promise, handle error in then(), verify success in done() addAsyncNoQueue(1, 2, { throwException: 1 }). then(function () { LiveUnit.Assert.fail("expected error from prev promise"); }, function (e) { // handle the error LiveUnit.Assert.areEqual(e, "addAsyncNoQueue throwing requested exception"); }). done(function (result) { // should get here LiveUnit.Assert.areEqual(undefined, result, "expecting result to be undefined after handled error"); complete(); }); } testDoneChainedError = function (complete) { // chain done() to a promise chain that throws an exception not handled by intermediate then() addAsyncNoQueue(1, 2, { throwException: 1 }). then(function (result) { LiveUnit.Assert.fail("1 expecting error, not complete from done()"); return result; }). done(function () { LiveUnit.Assert.fail("2 expecting error, not complete from done()"); complete(); }, function (e) { LiveUnit.Assert.areEqual(e, "addAsyncNoQueue throwing requested exception"); complete(); } ); } testDoneAsErrorHandler = function (complete) { // use done() as an error handler addAsyncNoQueue(1, 2, { throwException: 1 }). done(function () { LiveUnit.Assert.fail("expected error, not complete from done()"); complete(); }, function (e) { LiveUnit.Assert.areEqual(e, "addAsyncNoQueue throwing requested exception"); complete(); } ); } testDoneUnhandledError = function (complete) { var old = WinJS.Promise['_doneHandler']; try { WinJS.Promise['_doneHandler'] = function (v) { throw v; }; // done() without error handler, expect throw() and onerror() var hitCount = 0; var catchHit = 0; WinJS.Promise.onerror = function (evt) { LiveUnit.Assert.isTrue(!!evt.detail.error); // verify error msg hitCount++; }; try { addAsyncNoQueue(1, 2, { throwException: 1 }). done(function () { LiveUnit.Assert.fail("expected error, not complete from done()"); complete(); }); } catch (ex) { LiveUnit.Assert.areEqual(ex, "addAsyncNoQueue throwing requested exception"); catchHit++; } finally { LiveUnit.Assert.areEqual(1, hitCount); // verify onerror was called LiveUnit.Assert.areEqual(1, catchHit); // verify catch was called // reset the onerror handler WinJS.Promise.onerror = undefined; complete(); } } finally { WinJS.Promise['_doneHandler'] = old; } } testDoneUnhandledError2 = function (complete) { var old = WinJS.Promise['_doneHandler']; try { WinJS.Promise['_doneHandler'] = function (v) { throw v; }; // empty done() without error handler, expect throw() and onerror() var hitCount = 0; var catchHit = 0; WinJS.Promise.onerror = function (evt) { LiveUnit.Assert.isTrue(!!evt.detail.error); // verify error msg hitCount++; }; try { addAsyncNoQueue(1, 2, { throwException: 1 }). done(); } catch (ex) { LiveUnit.Assert.areEqual(ex, "addAsyncNoQueue throwing requested exception"); catchHit++; } finally { LiveUnit.Assert.areEqual(1, hitCount); // verify onerror was called LiveUnit.Assert.areEqual(1, catchHit); // verify catch was called // reset the onerror handler WinJS.Promise.onerror = undefined; complete(); } } finally { WinJS.Promise['_doneHandler'] = old; } } testDoneCanceled = function (complete) { // validate canceled promise goes through done() error function var cancelCount = 0; var token; // create promise with a cancel handler. // This promise waits 500ms before completing to give time to cancel var x = new WinJS.Promise( function (c) { token = setTimeout(function () { cancelCount = -1; c(); }, 5); }, function () { clearTimeout(token); ++cancelCount; }); // call the promise x. then(function () { LiveUnit.Assert.fail("1 expected error from canceled promise"); }). done( function () { LiveUnit.Assert.fail("2 expected error from canceled promise"); complete(); }, function (e) { if (e.description) { LiveUnit.Assert.areEqual(e.description, "Canceled", "expected e.description == 'Canceled'"); } LiveUnit.Assert.areEqual(e.message, "Canceled", "expected e.message == 'Canceled'"); LiveUnit.Assert.areEqual(e.name, "Canceled", "expected e.name == 'Canceled'"); LiveUnit.Assert.areEqual(1, cancelCount, "expected cancel count == 1 in done()"); complete(); }); // since the promise is async and waiting 500ms in this case, we have time to cancel it x.cancel(); } testDoneProgress = function (complete) { // validate progress calls get to done() var expectedCount = 50; var progressCount = 0; // Create a promise which counts to 50, notifying done() of progress // We need to delay via setTimeout so we can hook up the done() statement to // monitor the progress calls. var x = new WinJS.Promise(function (c, e, p) { setTimeout(function () { for (var count = 0; count < expectedCount; count++) { p(count); }; c(1); }, 5); }); x.done( function (c) { LiveUnit.Assert.areEqual(1, c, "expected return value from complete == 1 from done()"); LiveUnit.Assert.areEqual(50, progressCount, "expected progressCount == 50 from done()"); complete(); }, function (e) { LiveUnit.Assert.fail("not expecting error in done(), got=" + e); complete(); }, function (p) { LiveUnit.Assert.areEqual(p, progressCount, "expected p == progressCount"); progressCount++; }); } testEmptyJoin = function (complete) { WinJS.Promise.join([]). then(null, errorHandler). then(complete); }; testEmptyAny = function (complete) { WinJS.Promise.any([]). then(null, errorHandler). then(complete); }; testStaticCanceledPromise = function (complete) { var hitCount = 0; WinJS.Promise.as() .then(function () { return WinJS.Promise.cancel; }) .then(null, function (error) { hitCount++; LiveUnit.Assert.areEqual("Canceled", error.name); LiveUnit.Assert.areEqual("Canceled", error.message); }) .then(null, errorHandler) .then(function () { LiveUnit.Assert.areEqual(1, hitCount); }) .then(null, errorHandler) .then(complete); }; testSignal = function (complete) { var hitComplete = 0; var hitError = 0; var hitProgress = 0; var s = new WinJS._Signal(); s.promise .then( function (value) { hitComplete++; LiveUnit.Assert.areEqual("complete value", value); }, function (error) { hitError++; }, function (progress) { hitProgress++; LiveUnit.Assert.areEqual(hitProgress, progress); } ) .then(null, errorHandler) .then(function () { LiveUnit.Assert.areEqual(1, hitComplete); LiveUnit.Assert.areEqual(0, hitError); LiveUnit.Assert.areEqual(2, hitProgress); }) .then(null, errorHandler) .then(complete); s.progress(1); s.progress(2); s.complete("complete value"); // shouldn't hit handlers for any of these s.progress(27); s.complete("other complete value"); s.error("an error?"); }; testSignalError = function (complete) { var hitComplete = 0; var hitError = 0; var hitProgress = 0; var s = new WinJS._Signal(); s.promise .then( function (value) { hitComplete++; }, function (error) { hitError++; LiveUnit.Assert.areEqual("error value", error); }, function (progress) { hitProgress++; LiveUnit.Assert.areEqual(hitProgress, progress); } ) .then(null, errorHandler) .then(function () { LiveUnit.Assert.areEqual(0, hitComplete); LiveUnit.Assert.areEqual(1, hitError); LiveUnit.Assert.areEqual(2, hitProgress); }) .then(null, errorHandler) .then(complete); s.progress(1); s.progress(2); s.error("error value"); // shouldn't hit handlers for any of these s.progress(27); s.complete("complete value"); s.error("another error?"); }; testNestedCancelationRecovery = function (complete) { var hitUnexpected = 0; var hitP1Complete = 0; var hitP2Complete = 0; var hitP3Complete = 0; function neverCompletes() { return new WinJS.Promise(function () { }); } var p1 = neverCompletes().then( function () { hitUnexpected++; }, function (e) { if (e instanceof Error && e.name === "Canceled") { return p2; } hitUnexpected++; } ); p1.then( function (v) { hitP1Complete++; }, function () { hitUnexpected++; } ); var p2 = neverCompletes().then( function () { hitUnexpected++; }, function (e) { if (e instanceof Error && e.name === "Canceled") { return p3; } hitUnexpected++; } ); p2.then( function (v) { hitP2Complete++; }, function () { hitUnexpected++; } ); p1.cancel(); var p3 = WinJS.Promise.timeout().then(function () { LiveUnit.Assert.areEqual(0, hitUnexpected); LiveUnit.Assert.areEqual(0, hitP1Complete); LiveUnit.Assert.areEqual(0, hitP2Complete); hitP3Complete++; return 1; }).then(function (v) { hitP3Complete++; return v; }); p2.cancel(); WinJS.Promise.timeout().then(function () { LiveUnit.Assert.areEqual(0, hitUnexpected); LiveUnit.Assert.areEqual(1, hitP1Complete); LiveUnit.Assert.areEqual(1, hitP2Complete); LiveUnit.Assert.areEqual(2, hitP3Complete); }) .then(null, errorHandler) .then(complete); }; testJoiningCanceledPromisesCancels = function () { var hitCompleteCount = 0, hitErrorCount = 0; var p1 = new WinJS.Promise(function () { }); var p2 = new WinJS.Promise(function () { }); var p3 = new WinJS.Promise(function () { }); var p = WinJS.Promise.join([p1, p2, p3]); p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.isTrue(e instanceof Error); LiveUnit.Assert.areEqual("Canceled", e.name); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p1.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p2.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p3.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testJoinSomeCanceledAndOtherFailedPromisesFails = function () { var hitCompleteCount = 0, hitErrorCount = 0; var p1 = new WinJS.Promise(function () { }); var p2 = new WinJS.Promise(function () { }); var p3e; var p3 = new WinJS.Promise(function (c, e) { p3e = e; }); var p = WinJS.Promise.join([p1, p2, p3]); p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.isFalse(e instanceof Error); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p1.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p2.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p3e(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testJoinSomeCanceledAndOtherCompletedPromisesCancels = function () { var hitCompleteCount = 0, hitErrorCount = 0; var p1 = new WinJS.Promise(function () { }); var p2 = new WinJS.Promise(function () { }); var p3c; var p3 = new WinJS.Promise(function (c) { p3c = c; }); var p = WinJS.Promise.join([p1, p2, p3]); p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.isTrue(e instanceof Error); LiveUnit.Assert.areEqual("Canceled", e.name); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p1.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p2.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p3c(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testAnyCanceledPromisesCancels = function () { var hitCompleteCount = 0, hitErrorCount = 0; var p1 = new WinJS.Promise(function () { }); var p2 = new WinJS.Promise(function () { }); var p3 = new WinJS.Promise(function () { }); var p = WinJS.Promise.any([p1, p2, p3]); p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; LiveUnit.Assert.isTrue(e instanceof Error); LiveUnit.Assert.areEqual("Canceled", e.name); } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p1.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p2.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p3.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(1, hitErrorCount); }; testAnySomeCanceledPromisesStillSucceeds = function () { var hitCompleteCount = 0, hitErrorCount = 0; var p1 = new WinJS.Promise(function () { }); var p2 = new WinJS.Promise(function () { }); var p3c; var p3 = new WinJS.Promise(function (c) { p3c = c; }); var p = WinJS.Promise.any([p1, p2, p3]); p.then( function (v) { hitCompleteCount++; }, function (e) { hitErrorCount++; } ); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p1.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p2.cancel(); LiveUnit.Assert.areEqual(0, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); p3c(); LiveUnit.Assert.areEqual(1, hitCompleteCount); LiveUnit.Assert.areEqual(0, hitErrorCount); }; testCancelingPromiseWhichErrorCorrectsTheCancel = function () { var hitCount = 0; var complete; var p = new WinJS.Promise( function (c) { complete = c; }, function () { complete(1); } ); p.cancel(); p.then(function (v) { hitCount++; LiveUnit.Assert.areEqual(1, v); }); LiveUnit.Assert.areEqual(1, hitCount); }; testFIFODelivery = function () { var c; var p = new WinJS.Promise(function (complete) { c = complete; }); var count = 0; var p1 = p.then(function () { LiveUnit.Assert.areEqual(0, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(3, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(4, count); count++; }); var p2 = p.then(function () { LiveUnit.Assert.areEqual(1, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(5, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(6, count); count++; }); var p3 = p.then(function () { LiveUnit.Assert.areEqual(2, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(7, count); count++; }); p1.then(function () { LiveUnit.Assert.areEqual(8, count); count++; }); c(); }; testRecursivelyChainingPromises = function (complete) { q.clear(); var count = 3000; var i = 0; function run() { if (i < count) { console.log("Execution #" + i, i); i++; return new WinJS.Promise(function (c) { q.schedule(function () { c(); }); }).then(run); } } run() .then(function () { LiveUnit.Assert.areEqual(count, i); }) .then(null, function () { LiveUnit.Assert.fail("should not get here"); }) .then(null, function () { ; }) .then(complete); q.drain(); }; }; } LiveUnit.registerTestClass("CorsicaTests.Promise");