gecko-dev/dom/promise/tests/test_promise.html

845 строки
22 KiB
HTML

<!--
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>