diff options
Diffstat (limited to 'dom/promise/tests/test_promise.html')
-rw-r--r-- | dom/promise/tests/test_promise.html | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/dom/promise/tests/test_promise.html b/dom/promise/tests/test_promise.html new file mode 100644 index 0000000000..7c724daf51 --- /dev/null +++ b/dom/promise/tests/test_promise.html @@ -0,0 +1,844 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Basic Promise Test</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"><!-- + +function promiseResolve() { + ok(Promise, "Promise object should exist"); + + var promise = new Promise(function(resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(42); + }); + promise.then(function(what) { + ok(true, "Then - resolveCb has been called"); + is(what, 42, "ResolveCb received 42"); + runTest(); + }, function() { + ok(false, "Then - rejectCb has been called"); + runTest(); + }); +} + +function promiseResolveNoArg() { + var promise = new Promise(function(resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(); + }); + promise.then(function(what) { + ok(true, "Then - resolveCb has been called"); + is(what, undefined, "ResolveCb received undefined"); + runTest(); + }, function() { + ok(false, "Then - rejectCb has been called"); + runTest(); + }); +} + +function promiseReject() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + promise.then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + }); +} + +function promiseRejectNoHandler() { + // This test only checks that the code that reports unhandled errors in the + // Promises implementation does not crash or leak. + + new Promise(function(res, rej) { + // eslint-disable-next-line no-undef + noSuchMethod(); + }); + runTest(); +} + +function promiseRejectNoArg() { + var promise = new Promise(function(resolve, reject) { + reject(); + }); + promise.then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, undefined, "RejectCb received undefined"); + runTest(); + }); +} + +function promiseException() { + var promise = new Promise(function(resolve, reject) { + // eslint-disable-next-line no-throw-literal + throw 42; + }); + promise.then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + }); +} + +function promiseGC() { + var resolve; + var promise = new Promise(function(r1, r2) { + resolve = r1; + }); + promise.then(function(what) { + ok(true, "Then - promise is still alive"); + runTest(); + }); + + promise = null; + + SpecialPowers.gc(); + SpecialPowers.forceGC(); + SpecialPowers.forceCC(); + + resolve(42); +} + +function promiseAsync_TimeoutResolveThen() { + var handlerExecuted = false; + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + Promise.resolve().then(function() { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveTimeoutThen() { + var handlerExecuted = false; + + var promise = Promise.resolve(); + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + promise.then(function() { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveThenTimeout() { + var handlerExecuted = false; + + Promise.resolve().then(function() { + handlerExecuted = true; + }); + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_SyncXHR() { + var handlerExecuted = false; + + Promise.resolve().then(function() { + handlerExecuted = true; + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }); + + ok(!handlerExecuted, "Handlers are not called until the next microtask."); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", "testXHR.txt", false); + xhr.send(null); + + ok(!handlerExecuted, "Sync XHR should not trigger microtask execution."); +} + +function promiseDoubleThen() { + var steps = 0; + var promise = new Promise(function(r1, r2) { + r1(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + steps++; + }, function(what) { + ok(false, "Then.reject has been called"); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(steps, 1, "Then.resolve - step == 1"); + is(what, 42, "Value == 42"); + runTest(); + }, function(what) { + ok(false, "Then.reject has been called"); + }); +} + +function promiseThenException() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + // eslint-disable-next-line no-throw-literal + throw "booh"; + }).catch(function(e) { + ok(true, "window.onerror has been called!"); + runTest(); + }); +} + +function promiseThenCatchThen() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + var promise2 = promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }, function(what) { + ok(false, "Then.reject has been called"); + }); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }, function(what) { + ok(false, "Then.reject has been called"); + }).catch(function() { + ok(false, "Catch has been called"); + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }, function(what) { + ok(false, "Then.reject has been called"); + }); +} + +function promiseThenNoArg() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + var clone = promise.then(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.then(function(v) { + clone.then(function(cv) { + is(v, cv, "Both resolve to the same value"); + runTest(); + }); + }); +} + +function promiseThenUndefinedResolveFunction() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + try { + promise.then(undefined, function(v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on undefined resolve function"); + } +} + +function promiseThenNullResolveFunction() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + try { + promise.then(null, function(v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on null resolve function"); + } +} + +function promiseRejectThenCatchThen() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + var promise2 = promise.then(function(what) { + ok(false, "Then.resolve has been called"); + }, function(what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }).catch(function(what) { + ok(false, "Catch has been called"); + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseRejectThenCatchThen2() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }).catch(function(what) { + is(what, 42, "Value == 42"); + ok(true, "Catch has been called"); + return what + 1; + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + runTest(); + }); +} + +function promiseRejectThenCatchExceptionThen() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + promise.then(function(what) { + ok(false, "Then.resolve has been called"); + }, function(what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + // eslint-disable-next-line no-throw-literal + throw (what + 1); + }).catch(function(what) { + ok(true, "Catch has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseThenCatchOrderingResolve() { + var global = 0; + var f = new Promise(function(r1, r2) { + r1(42); + }); + + f.then(function() { + f.then(function() { + global++; + }); + f.catch(function() { + global++; + }); + f.then(function() { + global++; + }); + setTimeout(function() { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + }); +} + +function promiseThenCatchOrderingReject() { + var global = 0; + var f = new Promise(function(r1, r2) { + r2(42); + }); + + f.then(function() {}, function() { + f.then(function() { + global++; + }); + f.catch(function() { + global++; + }); + f.then(function() {}, function() { + global++; + }); + setTimeout(function() { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + }); +} + +function promiseCatchNoArg() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + var clone = promise.catch(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.catch(function(v) { + clone.catch(function(cv) { + is(v, cv, "Both reject to the same value"); + runTest(); + }); + }); +} + +function promiseNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(res, rej) { + ok(true, "Nested promise is executed"); + res(42); + })); + }).then(function(value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }); +} + +function promiseNestedNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(res, rej) { + ok(true, "Nested promise is executed"); + res(42); + }).then(function(what) { return what + 1; })); + }).then(function(value) { + is(value, 43, "Nested promise is executed and then == 43"); + runTest(); + }); +} + +function promiseWrongNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(r, r2) { + ok(true, "Nested promise is executed"); + r(42); + })); + reject(42); + }).then(function(value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }, function(value) { + ok(false, "This is wrong"); + }); +} + +function promiseLoop() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(res, rej) { + ok(true, "Nested promise is executed"); + res(new Promise(function(resInner, rejInner) { + ok(true, "Nested nested promise is executed"); + resInner(42); + })); + })); + }).then(function(value) { + is(value, 42, "Nested nested promise is executed and then == 42"); + runTest(); + }, function(value) { + ok(false, "This is wrong"); + }); +} + +function promiseStaticReject() { + var promise = Promise.reject(42); + promise.then(function(what) { + ok(false, "This should not be called"); + }, function(what) { + is(what, 42, "Value == 42"); + runTest(); + }); +} + +function promiseStaticResolve() { + var promise = Promise.resolve(42); + promise.then(function(what) { + is(what, 42, "Value == 42"); + runTest(); + }, function() { + ok(false, "This should not be called"); + }); +} + +function promiseResolveNestedPromise() { + var promise = Promise.resolve(new Promise(function(r, r2) { + ok(true, "Nested promise is executed"); + r(42); + }, function() { + ok(false, "This should not be called"); + })); + promise.then(function(what) { + is(what, 42, "Value == 42"); + runTest(); + }, function() { + ok(false, "This should not be called"); + }); +} + +function promiseSimpleThenableResolve() { + var thenable = { then(resolve) { resolve(5); } }; + var promise = new Promise(function(resolve, reject) { + resolve(thenable); + }); + + promise.then(function(v) { + ok(v === 5, "promiseSimpleThenableResolve"); + runTest(); + }, function(e) { + ok(false, "promiseSimpleThenableResolve: Should not reject"); + }); +} + +function promiseSimpleThenableReject() { + var thenable = { then(resolve, reject) { reject(5); } }; + var promise = new Promise(function(resolve, reject) { + resolve(thenable); + }); + + promise.then(function() { + ok(false, "promiseSimpleThenableReject: Should not resolve"); + runTest(); + }, function(e) { + ok(e === 5, "promiseSimpleThenableReject"); + runTest(); + }); +} + +function promiseThenableThrowsBeforeCallback() { + var thenable = { then(resolve) { + throw new TypeError("Hi there"); + + // eslint-disable-next-line no-unreachable + resolve(5); + }}; + + var promise = Promise.resolve(thenable); + promise.then(function(v) { + ok(false, "promiseThenableThrowsBeforeCallback: Should've rejected"); + runTest(); + }, function(e) { + ok(e instanceof TypeError, "promiseThenableThrowsBeforeCallback"); + runTest(); + }); +} + +function promiseThenableThrowsAfterCallback() { + var thenable = { then(resolve) { + resolve(5); + throw new TypeError("Hi there"); + }}; + + var promise = Promise.resolve(thenable); + promise.then(function(v) { + ok(v === 5, "promiseThenableThrowsAfterCallback"); + runTest(); + }, function(e) { + ok(false, "promiseThenableThrowsAfterCallback: Should've resolved"); + runTest(); + }); +} + +function promiseThenableRejectThenResolve() { + var thenable = { then(resolve, reject) { + reject(new TypeError("Hi there")); + resolve(5); + }}; + + var promise = Promise.resolve(thenable); + promise.then(function(v) { + ok(false, "promiseThenableRejectThenResolve should have rejected"); + runTest(); + }, function(e) { + ok(e instanceof TypeError, "promiseThenableRejectThenResolve"); + runTest(); + }); +} + +function promiseWithThenReplaced() { + // Ensure that we call the 'then' on the promise and not the internal then. + var promise = new Promise(function(resolve, reject) { + resolve(5); + }); + + // Rogue `then` always rejects. + promise.then = function(onFulfill, onReject) { + onReject(new TypeError("Foo")); + }; + + var promise2 = Promise.resolve(promise); + promise2.then(function(v) { + ok(false, "promiseWithThenReplaced: Should've rejected"); + runTest(); + }, function(e) { + ok(e instanceof TypeError, "promiseWithThenReplaced"); + runTest(); + }); +} + +function promiseStrictHandlers() { + var promise = Promise.resolve(5); + promise.then(function() { + "use strict"; + ok(this === undefined, "Strict mode callback should have this === undefined."); + runTest(); + }); +} + +function promiseStrictExecutorThisArg() { + new Promise(function(resolve, reject) { + "use strict"; + ok(this === undefined, "thisArg should be undefined."); + runTest(); + }); +} + +function promiseResolveArray() { + var p = Promise.resolve([1, 2, 3]); + ok(p instanceof Promise, "Should return a Promise."); + p.then(function(v) { + ok(Array.isArray(v), "Resolved value should be an Array"); + is(v.length, 3, "Length should match"); + is(v[0], 1, "Resolved value should match original"); + is(v[1], 2, "Resolved value should match original"); + is(v[2], 3, "Resolved value should match original"); + runTest(); + }); +} + +function promiseResolveThenable() { + var p = Promise.resolve({ then(onFulfill, onReject) { onFulfill(2); } }); + ok(p instanceof Promise, "Should cast to a Promise."); + p.then(function(v) { + is(v, 2, "Should resolve to 2."); + runTest(); + }, function(e) { + ok(false, "promiseResolveThenable should've resolved"); + runTest(); + }); +} + +function promiseResolvePromise() { + var original = Promise.resolve(true); + var cast = Promise.resolve(original); + + ok(cast instanceof Promise, "Should cast to a Promise."); + is(cast, original, "Should return original Promise."); + cast.then(function(v) { + is(v, true, "Should resolve to true."); + runTest(); + }); +} + +// Bug 1009569. +// Ensure that thenables are run on a clean stack asynchronously. +// Test case adopted from +// https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js. +function promiseResolveThenableCleanStack() { + function immed(s) { x++; s(); } + function incX() { x++; } + + var x = 0; + var thenable = { then: immed }; + var results = []; + + var p = Promise.resolve(thenable).then(incX); + results.push(x); + + // check what happens after all "next cycle" steps + // have had a chance to complete + setTimeout(function() { + // Result should be [0, 2] since `thenable` will be called async. + is(results[0], 0, "Expected thenable to be called asynchronously"); + // See Bug 1023547 comment 13 for why this check has to be gated on p. + p.then(function() { + results.push(x); + is(results[1], 2, "Expected thenable to be called asynchronously"); + runTest(); + }); + }, 1000); +} + +// Bug 1008467 - Promise fails with "too much recursion". +// The bug was that the callbacks passed to a thenable would resolve the +// promise synchronously when the fulfill handler returned a non-thenable. +// +// For example: +// var p = new Promise(function(resolve) { +// resolve(5); +// }); +// var m = Promise.resolve(p); +// +// At this point `m` is a Promise that is resolved with a thenable `p`, so it +// calls `p.then()` with two callbacks, both of which would synchronously resolve +// `m` when `p` invoked them (on account of itself being resolved, possibly +// synchronously. A chain of these 'Promise resolved by a Promise' would lead to +// stack overflow. +function promiseTestAsyncThenableResolution() { + var k = 3000; + Promise.resolve().then(function next() { + k--; + if (k > 0) return Promise.resolve().then(next); + return undefined; + }).then(function() { + ok(true, "Resolution of a chain of thenables should not be synchronous."); + runTest(); + }); +} + +// Bug 1062323 +function promiseWrapperAsyncResolution() { + var p = new Promise(function(resolve, reject) { + resolve(); + }); + + var results = []; + var q = p.then(function() { + results.push("1-1"); + }).then(function() { + results.push("1-2"); + }).then(function() { + results.push("1-3"); + }); + + var r = p.then(function() { + results.push("2-1"); + }).then(function() { + results.push("2-2"); + }).then(function() { + results.push("2-3"); + }); + + Promise.all([q, r]).then(function() { + var match = results[0] == "1-1" && + results[1] == "2-1" && + results[2] == "1-2" && + results[3] == "2-2" && + results[4] == "1-3" && + results[5] == "2-3"; + info(results); + ok(match, "Chained promises should resolve asynchronously."); + runTest(); + }, function() { + ok(false, "promiseWrapperAsyncResolution: One of the promises failed."); + runTest(); + }); +} + +var tests = [ promiseResolve, promiseReject, + promiseException, promiseGC, + promiseAsync_TimeoutResolveThen, + promiseAsync_ResolveTimeoutThen, + promiseAsync_ResolveThenTimeout, + promiseAsync_SyncXHR, + promiseDoubleThen, promiseThenException, + promiseThenCatchThen, promiseRejectThenCatchThen, + promiseRejectThenCatchThen2, + promiseRejectThenCatchExceptionThen, + promiseThenCatchOrderingResolve, + promiseThenCatchOrderingReject, + promiseNestedPromise, promiseNestedNestedPromise, + promiseWrongNestedPromise, promiseLoop, + promiseStaticReject, promiseStaticResolve, + promiseResolveNestedPromise, + promiseResolveNoArg, + promiseRejectNoArg, + promiseThenNoArg, + promiseThenUndefinedResolveFunction, + promiseThenNullResolveFunction, + promiseCatchNoArg, + promiseRejectNoHandler, + promiseSimpleThenableResolve, + promiseSimpleThenableReject, + promiseThenableThrowsBeforeCallback, + promiseThenableThrowsAfterCallback, + promiseThenableRejectThenResolve, + promiseWithThenReplaced, + promiseStrictHandlers, + promiseStrictExecutorThisArg, + promiseResolveArray, + promiseResolveThenable, + promiseResolvePromise, + promiseResolveThenableCleanStack, + promiseTestAsyncThenableResolution, + promiseWrapperAsyncResolution, + ]; + +function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +runTest(); +// --> +</script> +</pre> +</body> +</html> |