summaryrefslogtreecommitdiffstats
path: root/dom/promise/tests
diff options
context:
space:
mode:
Diffstat (limited to 'dom/promise/tests')
-rw-r--r--dom/promise/tests/chrome.ini7
-rw-r--r--dom/promise/tests/file_promise_and_timeout_ordering.js18
-rw-r--r--dom/promise/tests/file_promise_xrays.html32
-rw-r--r--dom/promise/tests/mochitest.ini18
-rw-r--r--dom/promise/tests/promise_uncatchable_exception.js9
-rw-r--r--dom/promise/tests/test_bug883683.html41
-rw-r--r--dom/promise/tests/test_on_new_promise.html45
-rw-r--r--dom/promise/tests/test_on_promise_settled.html54
-rw-r--r--dom/promise/tests/test_on_promise_settled_duplicates.html59
-rw-r--r--dom/promise/tests/test_promise.html831
-rw-r--r--dom/promise/tests/test_promise_and_timeout_ordering.html15
-rw-r--r--dom/promise/tests/test_promise_and_timeout_ordering_workers.html13
-rw-r--r--dom/promise/tests/test_promise_uncatchable_exception.html35
-rw-r--r--dom/promise/tests/test_promise_utils.html320
-rw-r--r--dom/promise/tests/test_promise_xrays.html365
-rw-r--r--dom/promise/tests/test_resolve.html67
-rw-r--r--dom/promise/tests/test_resolver_return_value.html40
-rw-r--r--dom/promise/tests/test_species_getter.html24
-rw-r--r--dom/promise/tests/test_thenable_vs_promise_ordering.html27
-rw-r--r--dom/promise/tests/test_webassembly_compile.html174
-rw-r--r--dom/promise/tests/unit/test_monitor_uncaught.js274
-rw-r--r--dom/promise/tests/unit/xpcshell.ini5
22 files changed, 2473 insertions, 0 deletions
diff --git a/dom/promise/tests/chrome.ini b/dom/promise/tests/chrome.ini
new file mode 100644
index 000000000..c6dc855c4
--- /dev/null
+++ b/dom/promise/tests/chrome.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+
+[test_on_new_promise.html]
+[test_on_promise_settled.html]
+[test_on_promise_settled_duplicates.html]
+[test_promise_xrays.html]
+support-files = file_promise_xrays.html
diff --git a/dom/promise/tests/file_promise_and_timeout_ordering.js b/dom/promise/tests/file_promise_and_timeout_ordering.js
new file mode 100644
index 000000000..c83b5a6ac
--- /dev/null
+++ b/dom/promise/tests/file_promise_and_timeout_ordering.js
@@ -0,0 +1,18 @@
+var log = [];
+var resolvedPromise = Promise.resolve(null);
+function schedulePromiseTask(f) {
+ resolvedPromise.then(f);
+}
+
+setTimeout(function() {
+ log.push('t1start');
+ schedulePromiseTask(function() {
+ log.push('promise');
+ });
+ log.push('t1end');
+}, 10);
+
+setTimeout(function() {
+ log.push('t2');
+ postMessage(log.join(', '));
+}, 10);
diff --git a/dom/promise/tests/file_promise_xrays.html b/dom/promise/tests/file_promise_xrays.html
new file mode 100644
index 000000000..73f9bf7d7
--- /dev/null
+++ b/dom/promise/tests/file_promise_xrays.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ function vendGetter(name) {
+ return function() { throw "Getting " + String(name) };
+ }
+ function vendSetter(name) {
+ return function() { throw "Setting " + String(name) };
+ }
+ var setupThrew = false;
+ try {
+ // Neuter everything we can think of on Promise.
+ for (var obj of [Promise, Promise.prototype]) {
+ propNames = Object.getOwnPropertyNames(obj);
+ propNames = propNames.concat(Object.getOwnPropertySymbols(obj));
+ for (var propName of propNames) {
+ if ((propName == "prototype" ||
+ propName == Symbol.hasInstance) &&
+ obj == Promise) {
+ // They're not configurable.
+ continue;
+ }
+ Object.defineProperty(obj, propName,
+ { get: vendGetter(propName), set: vendSetter(propName) });
+ }
+ }
+ } catch (e) {
+ // Something went wrong. Save that info so the test can check for it.
+ setupThrew = e;
+ }
+ </script>
+</html>
diff --git a/dom/promise/tests/mochitest.ini b/dom/promise/tests/mochitest.ini
new file mode 100644
index 000000000..0e6466d16
--- /dev/null
+++ b/dom/promise/tests/mochitest.ini
@@ -0,0 +1,18 @@
+[DEFAULT]
+support-files =
+ promise_uncatchable_exception.js
+
+[test_bug883683.html]
+[test_promise.html]
+[test_promise_uncatchable_exception.html]
+skip-if = debug == false
+[test_promise_utils.html]
+[test_resolve.html]
+[test_resolver_return_value.html]
+[test_thenable_vs_promise_ordering.html]
+[test_promise_and_timeout_ordering.html]
+support-files = file_promise_and_timeout_ordering.js
+[test_promise_and_timeout_ordering_workers.html]
+support-files = file_promise_and_timeout_ordering.js
+[test_species_getter.html]
+[test_webassembly_compile.html]
diff --git a/dom/promise/tests/promise_uncatchable_exception.js b/dom/promise/tests/promise_uncatchable_exception.js
new file mode 100644
index 000000000..d062e21af
--- /dev/null
+++ b/dom/promise/tests/promise_uncatchable_exception.js
@@ -0,0 +1,9 @@
+postMessage("Done", "*");
+
+var p = new Promise(function(resolve, reject) {
+ TestFunctions.throwUncatchableException();
+ ok(false, "Shouldn't get here!");
+}).catch(function(exception) {
+ ok(false, "Shouldn't get here!");
+});
+ok(false, "Shouldn't get here!");
diff --git a/dom/promise/tests/test_bug883683.html b/dom/promise/tests/test_bug883683.html
new file mode 100644
index 000000000..ab23ca10d
--- /dev/null
+++ b/dom/promise/tests/test_bug883683.html
@@ -0,0 +1,41 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Promise - bug 883683</title>
+ <script type="application/javascript" 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 runTest() {
+ [{}, {}, {}, {}, {}].reduce(Promise.reject.bind(Promise));
+ ok(true, "No leaks with reject?");
+
+ [{}, {}, {}, {}, {}].reduce(Promise.resolve.bind(Promise));
+ ok(true, "No leaks with resolve?");
+
+ [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { throw a; }); });
+ ok(true, "No leaks with exception?");
+
+ [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { }); });
+ ok(true, "No leaks with empty promise?");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+// -->
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/promise/tests/test_on_new_promise.html b/dom/promise/tests/test_on_new_promise.html
new file mode 100644
index 000000000..634dd7dda
--- /dev/null
+++ b/dom/promise/tests/test_on_new_promise.html
@@ -0,0 +1,45 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1083210 - Sanity test for interaction between DOM promises and
+Debugger.prototype.onNewPromise.
+-->
+
+<html>
+<head>
+ <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ is(Object.prototype.toString.call(new Promise(function () {})),
+ "[object Promise]",
+ "We should have the native DOM promise implementation.");
+
+ var Cu = Components.utils;
+ Cu.import("resource://gre/modules/jsdebugger.jsm");
+ var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+ addDebuggerToGlobal(dbgGlobal);
+ var dbg = new dbgGlobal.Debugger(this);
+
+ var wrappedPromise;
+ dbg.onNewPromise = function (wp) { wrappedPromise = wp; };
+
+ var promise = new Promise(function () {});
+ debugger;
+ ok(wrappedPromise);
+ is(wrappedPromise.unsafeDereference(), promise);
+ </script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/promise/tests/test_on_promise_settled.html b/dom/promise/tests/test_on_promise_settled.html
new file mode 100644
index 000000000..4061d3997
--- /dev/null
+++ b/dom/promise/tests/test_on_promise_settled.html
@@ -0,0 +1,54 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1084065 - Sanity test for interaction between DOM promises and
+Debugger.prototype.onPromiseResolved.
+-->
+
+<html>
+<head>
+ <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ is(Object.prototype.toString.call(new Promise(function () {})),
+ "[object Promise]",
+ "We should have the native DOM promise implementation.");
+
+ var Cu = Components.utils;
+ Cu.import("resource://gre/modules/jsdebugger.jsm");
+ var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+ addDebuggerToGlobal(dbgGlobal);
+ var dbg = new dbgGlobal.Debugger(this);
+
+ var wrappedPromise;
+ dbg.onPromiseSettled = function (wp) { wrappedPromise = wp; };
+
+ var promise = Promise.resolve();
+ promise
+ .then(function () {
+ ok(wrappedPromise);
+ is(wrappedPromise.unsafeDereference(), promise);
+ dbg.onPromiseSettled = undefined;
+ })
+ .then(null, function (e) {
+ ok(false, "Got an unexpected error: " + e);
+ })
+ .then(SimpleTest.finish);
+ </script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/promise/tests/test_on_promise_settled_duplicates.html b/dom/promise/tests/test_on_promise_settled_duplicates.html
new file mode 100644
index 000000000..5a1eddb1e
--- /dev/null
+++ b/dom/promise/tests/test_on_promise_settled_duplicates.html
@@ -0,0 +1,59 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!--
+Bug 1084065 - Test that Debugger.prototype.onPromiseResolved doesn't get dupes.
+-->
+
+<html>
+<head>
+ <title>Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ is(Object.prototype.toString.call(new Promise(function () {})),
+ "[object Promise]",
+ "We should have the native DOM promise implementation.");
+
+ var Cu = Components.utils;
+ Cu.import("resource://gre/modules/jsdebugger.jsm");
+ var dbgGlobal = new Cu.Sandbox(document.nodePrincipal);
+ addDebuggerToGlobal(dbgGlobal);
+ var dbg = new dbgGlobal.Debugger(this);
+
+ var seen = new Set();
+ dbg.onPromiseSettled = function (wp) {
+ is(seen.has(wp), false);
+ seen.add(wp);
+ };
+
+ var promise = new Promise(function (fulfill, reject) {
+ fulfill(1);
+ fulfill(2);
+ fulfill(3);
+ });
+
+ promise
+ .then(function () {
+ dbg.onPromiseSettled = undefined;
+ })
+ .then(null, function (e) {
+ ok(false, "Got an unexpected error: " + e);
+ })
+ .then(SimpleTest.finish);
+ </script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/promise/tests/test_promise.html b/dom/promise/tests/test_promise.html
new file mode 100644
index 000000000..af185efcd
--- /dev/null
+++ b/dom/promise/tests/test_promise.html
@@ -0,0 +1,831 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Basic Promise Test</title>
+ <script type="application/javascript" 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);
+ }).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();
+ }).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);
+ }).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.
+ var promise = new Promise(function(res, rej) {
+ noSuchMethod();
+ });
+ runTest();
+}
+
+function promiseRejectNoArg() {
+ var promise = new Promise(function(resolve, reject) {
+ reject();
+ }).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) {
+ throw 42;
+ }).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;
+ }).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);
+
+ todo(!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");
+ 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");
+ 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(resolve, reject) {
+ ok(true, "Nested promise is executed");
+ resolve(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(resolve, reject) {
+ ok(true, "Nested promise is executed");
+ resolve(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(r1, r2) {
+ ok(true, "Nested promise is executed");
+ r1(new Promise(function(r1, r2) {
+ ok(true, "Nested nested promise is executed");
+ r1(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).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).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");
+ })).then(function(what) {
+ is(what, 42, "Value == 42");
+ runTest();
+ }, function() {
+ ok(false, "This should not be called");
+ });
+}
+
+function promiseSimpleThenableResolve() {
+ var thenable = { then: function(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: function(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: function(resolve) {
+ throw new TypeError("Hi there");
+ 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: function(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: function(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() {
+ var promise = 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: function(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);
+ }).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>
+
diff --git a/dom/promise/tests/test_promise_and_timeout_ordering.html b/dom/promise/tests/test_promise_and_timeout_ordering.html
new file mode 100644
index 000000000..1dfa13bbb
--- /dev/null
+++ b/dom/promise/tests/test_promise_and_timeout_ordering.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for promise and timeout ordering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("Promise callbacks should run immediately after the setTimeout handler that enqueues them");
+var origPostMessage = window.postMessage;
+window.postMessage = function(msg) { origPostMessage.call(window, msg, "*"); }
+window.onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "t1start, t1end, promise, t2");
+});
+</script>
+<script src="file_promise_and_timeout_ordering.js"></script>
diff --git a/dom/promise/tests/test_promise_and_timeout_ordering_workers.html b/dom/promise/tests/test_promise_and_timeout_ordering_workers.html
new file mode 100644
index 000000000..122a794db
--- /dev/null
+++ b/dom/promise/tests/test_promise_and_timeout_ordering_workers.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for promise and timeout ordering in workers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("Promise callbacks in workers should run immediately after the setTimeout handler that enqueues them");
+var w = new Worker("file_promise_and_timeout_ordering.js");
+w.onmessage = t.step_func_done(function(e) {
+ assert_equals(e.data, "t1start, t1end, promise, t2");
+});
+</script>
diff --git a/dom/promise/tests/test_promise_uncatchable_exception.html b/dom/promise/tests/test_promise_uncatchable_exception.html
new file mode 100644
index 000000000..8f7167d56
--- /dev/null
+++ b/dom/promise/tests/test_promise_uncatchable_exception.html
@@ -0,0 +1,35 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Promise - uncatchable exceptions</title>
+ <script type="application/javascript" 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">
+
+onmessage = function(evt) {
+ ok(true, "finished");
+ SimpleTest.finish();
+}
+
+function go() {
+ var script = document.createElement("script");
+ script.src = "promise_uncatchable_exception.js";
+ document.body.appendChild(script);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/promise/tests/test_promise_utils.html b/dom/promise/tests/test_promise_utils.html
new file mode 100644
index 000000000..316ea02a5
--- /dev/null
+++ b/dom/promise/tests/test_promise_utils.html
@@ -0,0 +1,320 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Test for Promise.all, Promise.race</title>
+ <script type="application/javascript" 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 promiseUtilitiesDefined() {
+ ok(Promise.all, "Promise.all must be defined when Promise is enabled.");
+ ok(Promise.race, "Promise.race must be defined when Promise is enabled.");
+ runTest();
+}
+
+function promiseAllEmptyArray() {
+ var p = Promise.all([]);
+ ok(p instanceof Promise, "Return value of Promise.all should be a Promise.");
+ p.then(function(values) {
+ ok(Array.isArray(values), "Resolved value should be an array.");
+ is(values.length, 0, "Resolved array length should match iterable's length.");
+ runTest();
+ }, function() {
+ ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
+ runTest();
+ });
+}
+
+function promiseAllArray() {
+ var p = Promise.all([1, new Date(), Promise.resolve("firefox")]);
+ ok(p instanceof Promise, "Return value of Promise.all should be a Promise.");
+ p.then(function(values) {
+ ok(Array.isArray(values), "Resolved value should be an array.");
+ is(values.length, 3, "Resolved array length should match iterable's length.");
+ is(values[0], 1, "Array values should match.");
+ ok(values[1] instanceof Date, "Array values should match.");
+ is(values[2], "firefox", "Array values should match.");
+ runTest();
+ }, function() {
+ ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
+ runTest();
+ });
+}
+
+function promiseAllIterable() {
+ function* promiseGen() {
+ var i = 3;
+ while (--i) {
+ yield Promise.resolve(i);
+ }
+
+ yield new Promise(function(resolve) {
+ setTimeout(resolve, 10);
+ });
+ }
+
+ Promise.all(promiseGen()).then(function(values) {
+ is(values.length, 3, "Resolved array length should match iterable's length.");
+ is(values[0], 2, "Array values should match.");
+ is(values[1], 1, "Array values should match.");
+ is(values[2], undefined, "Array values should match.");
+ runTest();
+ }, function(e) {
+ ok(false, "Promise.all shouldn't fail when an iterable is passed.");
+ runTest();
+ });
+}
+
+function promiseAllWaitsForAllPromises() {
+ var arr = [
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 1), 50);
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 2), 10);
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, new Promise(function(resolve2) {
+ resolve2(3);
+ })), 10);
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 4), 20);
+ })
+ ];
+
+ var p = Promise.all(arr);
+ p.then(function(values) {
+ ok(Array.isArray(values), "Resolved value should be an array.");
+ is(values.length, 4, "Resolved array length should match iterable's length.");
+ is(values[0], 1, "Array values should match.");
+ is(values[1], 2, "Array values should match.");
+ is(values[2], 3, "Array values should match.");
+ is(values[3], 4, "Array values should match.");
+ runTest();
+ }, function() {
+ ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
+ runTest();
+ });
+}
+
+function promiseAllRejectFails() {
+ var arr = [
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 1), 50);
+ }),
+ new Promise(function(resolve, reject) {
+ setTimeout(reject.bind(undefined, 2), 10);
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 3), 10);
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve.bind(undefined, 4), 20);
+ })
+ ];
+
+ var p = Promise.all(arr);
+ p.then(function(values) {
+ ok(false, "Promise.all shouldn't resolve when iterable has rejected Promises.");
+ runTest();
+ }, function(e) {
+ ok(true, "Promise.all should reject when iterable has rejected Promises.");
+ is(e, 2, "Rejection value should match.");
+ runTest();
+ });
+}
+
+function promiseAllCastError() {
+ var p = Promise.all([Promise.resolve(2), { then: function() { foo(); } }]);
+ ok(p instanceof Promise, "Should cast to a Promise.");
+ p.then(function(v) {
+ ok(false, "promiseAllCastError: should've rejected.");
+ runTest();
+ }, function(e) {
+ ok(e instanceof ReferenceError, "promiseCastThenableError");
+ runTest();
+ });
+}
+
+// Check that the resolved array is enumerable.
+function promiseAllEnumerable() {
+ var p = Promise.all([1, new Date(), Promise.resolve("firefox")]);
+ p.then(function(v) {
+ var count = 0;
+ for (key in v) {
+ ++count;
+ ok(v[key] === 1 || v[key] instanceof Date || v[key] === "firefox",
+ "Enumerated properties don't match.");
+ }
+ is(count, 3, "Resolved array from Promise.all should be enumerable");
+ runTest();
+ }, function(e) {
+ ok(false, "promiseAllEnumerable: should've resolved.");
+ runTest();
+ });
+}
+
+function promiseRaceEmpty() {
+ var p = Promise.race([]);
+ ok(p instanceof Promise, "Should return a Promise.");
+ p.then(function() {
+ ok(false, "Should not resolve");
+ }, function() {
+ ok(false, "Should not reject");
+ });
+ // Per spec, An empty race never resolves or rejects.
+ setTimeout(function() {
+ ok(true);
+ runTest();
+ }, 50);
+}
+
+function promiseRaceValuesArray() {
+ var p = Promise.race([true, new Date(), 3]);
+ ok(p instanceof Promise, "Should return a Promise.");
+ p.then(function(winner) {
+ is(winner, true, "First value should win.");
+ runTest();
+ }, function(err) {
+ ok(false, "Should not fail " + err + ".");
+ runTest();
+ });
+}
+
+function promiseRacePromiseArray() {
+ function timeoutPromise(n) {
+ return new Promise(function(resolve) {
+ setTimeout(function() {
+ resolve(n);
+ }, n);
+ });
+ }
+
+ var arr = [
+ new Promise(function(resolve) {
+ resolve("first");
+ }),
+ Promise.resolve("second"),
+ new Promise(function() {}),
+ new Promise(function(resolve) {
+ setTimeout(function() {
+ setTimeout(function() {
+ resolve("fourth");
+ }, 0);
+ }, 0);
+ }),
+ ];
+
+ var p = Promise.race(arr);
+ p.then(function(winner) {
+ is(winner, "first", "First queued resolution should win the race.");
+ runTest();
+ });
+}
+
+function promiseRaceIterable() {
+ function* participants() {
+ yield new Promise(function(resolve) {
+ setTimeout(resolve, 10, 10);
+ });
+ yield new Promise(function(resolve) {
+ setTimeout(resolve, 20, 20);
+ });
+ }
+
+ Promise.race(participants()).then(function(winner) {
+ is(winner, 10, "Winner should be the one that finished earlier.");
+ runTest();
+ }, function(e) {
+ ok(false, "Promise.race shouldn't throw when an iterable is passed!");
+ runTest();
+ });
+}
+
+function promiseRaceReject() {
+ var p = Promise.race([
+ Promise.reject(new Error("Fail bad!")),
+ new Promise(function(resolve) {
+ setTimeout(resolve, 0);
+ })
+ ]);
+
+ p.then(function() {
+ ok(false, "Should not resolve when winning Promise rejected.");
+ runTest();
+ }, function(e) {
+ ok(true, "Should be rejected");
+ ok(e instanceof Error, "Should reject with Error.");
+ ok(e.message == "Fail bad!", "Message should match.");
+ runTest();
+ });
+}
+
+function promiseRaceThrow() {
+ var p = Promise.race([
+ new Promise(function(resolve) {
+ nonExistent();
+ }),
+ new Promise(function(resolve) {
+ setTimeout(resolve, 0);
+ })
+ ]);
+
+ p.then(function() {
+ ok(false, "Should not resolve when winning Promise had an error.");
+ runTest();
+ }, function(e) {
+ ok(true, "Should be rejected");
+ ok(e instanceof ReferenceError, "Should reject with ReferenceError for function nonExistent().");
+ runTest();
+ });
+}
+
+var tests = [
+ promiseUtilitiesDefined,
+ promiseAllEmptyArray,
+ promiseAllArray,
+ promiseAllIterable,
+ promiseAllWaitsForAllPromises,
+ promiseAllRejectFails,
+ promiseAllCastError,
+ promiseAllEnumerable,
+
+ promiseRaceEmpty,
+ promiseRaceValuesArray,
+ promiseRacePromiseArray,
+ promiseRaceIterable,
+ promiseRaceReject,
+ promiseRaceThrow,
+ ];
+
+function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+runTest();
+// -->
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/promise/tests/test_promise_xrays.html b/dom/promise/tests/test_promise_xrays.html
new file mode 100644
index 000000000..55398167e
--- /dev/null
+++ b/dom/promise/tests/test_promise_xrays.html
@@ -0,0 +1,365 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1170760
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1170760</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1170760">Mozilla Bug 1170760</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/chrome/dom/promise/tests/file_promise_xrays.html"></iframe>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+var win = $("t").contentWindow;
+
+/** Test for Bug 1170760 **/
+SimpleTest.waitForExplicitFinish();
+
+function testLoadComplete() {
+ is(win.location.href, $("t").src, "Should have loaded the right thing");
+ nextTest();
+}
+
+function testHaveXray() {
+ is(typeof win.Promise.race, "function", "Should see a race() function");
+ var exception;
+ try {
+ win.Promise.wrappedJSObject.race;
+ } catch (e) {
+ exception = e;
+ }
+ is(exception, "Getting race", "Should have thrown the right exception");
+ is(win.wrappedJSObject.setupThrew, false, "Setup should not have thrown");
+ nextTest();
+}
+
+function testConstructor1() {
+ var p = new win.Promise(function(resolve, reject) { resolve(win.Promise.resolve(5)); });
+ p.then(
+ function(arg) {
+ is(arg, 5, "Content Promise constructor resolved with content promise should work");
+ },
+ function(e) {
+ ok(false, "Content Promise constructor resolved with content promise should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testConstructor2() {
+ var p = new win.Promise(function(resolve, reject) { resolve(Promise.resolve(5)); });
+ p.then(
+ function(arg) {
+ is(arg, 5, "Content Promise constructor resolved with chrome promise should work");
+ },
+ function(e) {
+ ok(false, "Content Promise constructor resolved with chrome promise should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testRace1() {
+ var p = win.Promise.race(new win.Array(1, 2));
+ p.then(
+ function(arg) {
+ ok(arg == 1 || arg == 2,
+ "Should get the right value when racing content-side array");
+ },
+ function(e) {
+ ok(false, "testRace1 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testRace2() {
+ var p = win.Promise.race(
+ new Array(win.Promise.resolve(1), win.Promise.resolve(2)));
+ p.then(
+ function(arg) {
+ ok(arg == 1 || arg == 2,
+ "Should get the right value when racing content-side array of explicit Promises");
+ },
+ function(e) {
+ ok(false, "testRace2 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testRace3() {
+ // This works with a chrome-side array because we do the iteration
+ // while still in the Xray compartment.
+ var p = win.Promise.race([1, 2]);
+ p.then(
+ function(arg) {
+ ok(arg == 1 || arg == 2,
+ "Should get the right value when racing chrome-side array");
+ },
+ function(e) {
+ ok(false, "testRace3 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testRace4() {
+ // This works with both content-side and chrome-side Promises because we want
+ // it to and go to some lengths to make it work.
+ var p = win.Promise.race([Promise.resolve(1), win.Promise.resolve(2)]);
+ p.then(
+ function(arg) {
+ ok(arg == 1 || arg == 2,
+ "Should get the right value when racing chrome-side promises");
+ },
+ function(e) {
+ ok(false, "testRace4 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testAll1() {
+ var p = win.Promise.all(new win.Array(1, 2));
+ p.then(
+ function(arg) {
+ ok(arg instanceof win.Array, "Should get an Array from Promise.all (1)");
+ is(arg[0], 1, "First entry of Promise.all return value should be correct (1)");
+ is(arg[1], 2, "Second entry of Promise.all return value should be correct (1)");
+ },
+ function(e) {
+ ok(false, "testAll1 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testAll2() {
+ var p = win.Promise.all(
+ new Array(win.Promise.resolve(1), win.Promise.resolve(2)));
+ p.then(
+ function(arg) {
+ ok(arg instanceof win.Array, "Should get an Array from Promise.all (2)");
+ is(arg[0], 1, "First entry of Promise.all return value should be correct (2)");
+ is(arg[1], 2, "Second entry of Promise.all return value should be correct (2)");
+ },
+ function(e) {
+ ok(false, "testAll2 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testAll3() {
+ // This works with a chrome-side array because we do the iteration
+ // while still in the Xray compartment.
+ var p = win.Promise.all([1, 2]);
+ p.then(
+ function(arg) {
+ ok(arg instanceof win.Array, "Should get an Array from Promise.all (3)");
+ is(arg[0], 1, "First entry of Promise.all return value should be correct (3)");
+ is(arg[1], 2, "Second entry of Promise.all return value should be correct (3)");
+ },
+ function(e) {
+ ok(false, "testAll3 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testAll4() {
+ // This works with both content-side and chrome-side Promises because we want
+ // it to and go to some lengths to make it work.
+ var p = win.Promise.all([Promise.resolve(1), win.Promise.resolve(2)]);
+ p.then(
+ function(arg) {
+ ok(arg instanceof win.Array, "Should get an Array from Promise.all (4)");
+ is(arg[0], 1, "First entry of Promise.all return value should be correct (4)");
+ is(arg[1], 2, "Second entry of Promise.all return value should be correct (4)");
+ },
+ function(e) {
+ ok(false, "testAll4 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testAll5() {
+ var p = win.Promise.all(new win.Array());
+ p.then(
+ function(arg) {
+ ok(arg instanceof win.Array, "Should get an Array from Promise.all (5)");
+ },
+ function(e) {
+ ok(false, "testAll5 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testResolve1() {
+ var p = win.Promise.resolve(5);
+ ok(p instanceof win.Promise, "Promise.resolve should return a promise");
+ p.then(
+ function(arg) {
+ is(arg, 5, "Should get correct Promise.resolve value");
+ },
+ function(e) {
+ ok(false, "testAll5 threw exception: " + e);
+ }
+ ).then(nextTest);
+}
+
+function testResolve2() {
+ var p = win.Promise.resolve(5);
+ var q = win.Promise.resolve(p);
+ is(q, p, "Promise.resolve should just pass through Promise values");
+ nextTest();
+}
+
+function testResolve3() {
+ var p = win.Promise.resolve(Promise.resolve(5));
+ p.then(
+ function(arg) {
+ is(arg, 5, "Promise.resolve with chrome Promise should work");
+ },
+ function(e) {
+ ok(false, "Promise.resolve with chrome Promise should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testResolve4() {
+ var p = new win.Promise((res, rej) => {});
+ Components.utils.getJSTestingFunctions().resolvePromise(p, 42);
+ p.then(
+ function(arg) {
+ is(arg, 42, "Resolving an Xray to a promise with TestingFunctions resolvePromise should work");
+ },
+ function(e) {
+ ok(false, "Resolving an Xray to a promise with TestingFunctions resolvePromise should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testReject1() {
+ var p = win.Promise.reject(5);
+ ok(p instanceof win.Promise, "Promise.reject should return a promise");
+ p.then(
+ function(arg) {
+ ok(false, "Promise should be rejected");
+ },
+ function(e) {
+ is(e, 5, "Should get correct Promise.reject value");
+ }
+ ).then(nextTest);
+}
+
+function testReject2() {
+ var p = new win.Promise((res, rej) => {});
+ Components.utils.getJSTestingFunctions().rejectPromise(p, 42);
+ p.then(
+ function(arg) {
+ ok(false, "Rejecting an Xray to a promise with TestingFunctions rejectPromise should trigger catch handler");
+ },
+ function(e) {
+ is(e, 42, "Rejecting an Xray to a promise with TestingFunctions rejectPromise should work");
+ }
+ ).then(nextTest);
+}
+
+function testThen1() {
+ var p = win.Promise.resolve(5);
+ var q = p.then((x) => x*x);
+ ok(q instanceof win.Promise,
+ "Promise.then should return a promise from the right global");
+ q.then(
+ function(arg) {
+ is(arg, 25, "Promise.then should work");
+ },
+ function(e) {
+ ok(false, "Promise.then should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testThen2() {
+ var p = win.Promise.resolve(5);
+ var q = p.then((x) => Promise.resolve(x*x));
+ ok(q instanceof win.Promise,
+ "Promise.then should return a promise from the right global");
+ q.then(
+ function(arg) {
+ is(arg, 25, "Promise.then resolved with chrome promise should work");
+ },
+ function(e) {
+ ok(false, "Promise.then resolved with chrome promise should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testCatch1() {
+ var p = win.Promise.reject(5);
+ ok(p instanceof win.Promise, "Promise.reject should return a promise");
+ var q = p.catch((x) => x*x);
+ ok(q instanceof win.Promise,
+ "Promise.catch should return a promise from the right global");
+ q.then(
+ function(arg) {
+ is(arg, 25, "Promise.catch should work");
+ },
+ function(e) {
+ ok(false, "Promise.catch should not fail");
+ }
+ ).then(nextTest);
+}
+
+function testToStringTag1() {
+ is(win.Promise.prototype[Symbol.toStringTag], "Promise", "@@toStringTag was incorrect");
+ var p = win.Promise.resolve();
+ is(String(p), "[object Promise]", "String() result was incorrect");
+ is(p.toString(), "[object Promise]", "toString result was incorrect");
+ is(Object.prototype.toString.call(p), "[object Promise]", "second toString result was incorrect");
+ nextTest();
+}
+
+var tests = [
+ testLoadComplete,
+ testHaveXray,
+ testConstructor1,
+ testConstructor2,
+ testRace1,
+ testRace2,
+ testRace3,
+ testRace4,
+ testAll1,
+ testAll2,
+ testAll3,
+ testAll4,
+ testAll5,
+ testResolve1,
+ testResolve2,
+ testResolve3,
+ testResolve4,
+ testReject1,
+ testReject2,
+ testThen1,
+ testThen2,
+ testCatch1,
+ testToStringTag1,
+];
+
+function nextTest() {
+ if (tests.length == 0) {
+ SimpleTest.finish();
+ return;
+ }
+ tests.shift()();
+}
+
+addLoadEvent(nextTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/promise/tests/test_resolve.html b/dom/promise/tests/test_resolve.html
new file mode 100644
index 000000000..780eae6c6
--- /dev/null
+++ b/dom/promise/tests/test_resolve.html
@@ -0,0 +1,67 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Promise.resolve(anything) Test</title>
+ <script type="application/javascript" 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"><!--
+
+var tests = [
+ null,
+ 42,
+ "hello world",
+ true,
+ false,
+ {},
+ { a: 42 },
+ [ 1, 2, 3, 4, null, true, "hello world" ],
+ function() {},
+ window,
+ undefined,
+ document.createElement('input'),
+ new Date(),
+];
+
+function cbError() {
+ ok(false, "Nothing should arrive here!");
+}
+
+function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.pop();
+
+ new Promise(function(resolve, reject) {
+ resolve(test);
+ }).then(function(what) {
+ ok(test === what, "What is: " + what);
+ }, cbError).then(function() {
+ new Promise(function(resolve, reject) {
+ reject(test)
+ }).then(cbError, function(what) {
+ ok(test === what, "What is: " + what);
+ }).then(runTest, cbError);
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+// -->
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/promise/tests/test_resolver_return_value.html b/dom/promise/tests/test_resolver_return_value.html
new file mode 100644
index 000000000..1fb9652ac
--- /dev/null
+++ b/dom/promise/tests/test_resolver_return_value.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1120235
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1120235</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1120235 **/
+ var res, rej;
+ var p = new Promise(function(resolve, reject) { res = resolve; rej = reject; });
+ is(res(1), undefined, "Resolve function should return undefined");
+ is(rej(2), undefined, "Reject function should return undefined");
+
+ var thenable = {
+ then: function(resolve, reject) {
+ is(resolve(3), undefined, "Thenable resolve argument should return undefined");
+ is(reject(4), undefined, "Thenable reject argument should return undefined");
+ SimpleTest.finish();
+ }
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ p = Promise.resolve(thenable);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1120235">Mozilla Bug 1120235</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/promise/tests/test_species_getter.html b/dom/promise/tests/test_species_getter.html
new file mode 100644
index 000000000..04e590a13
--- /dev/null
+++ b/dom/promise/tests/test_species_getter.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for ...</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var desc = Object.getOwnPropertyDescriptor(Promise, Symbol.species);
+ assert_not_equals(desc, undefined, "Should have a property");
+
+ assert_equals(desc.configurable, true, "Property should be configurable");
+ assert_equals(desc.enumerable, false, "Property should not be enumerable");
+ assert_equals(desc.set, undefined, "Should not have a setter");
+ var getter = desc.get;
+
+ var things = [undefined, null, 5, "xyz", Promise, Object];
+ for (var thing of things) {
+ assert_equals(getter.call(thing), thing,
+ "Getter should return its this value");
+ }
+
+}, "Promise should have an @@species getter that works per spec");
+</script>
diff --git a/dom/promise/tests/test_thenable_vs_promise_ordering.html b/dom/promise/tests/test_thenable_vs_promise_ordering.html
new file mode 100644
index 000000000..161e95d75
--- /dev/null
+++ b/dom/promise/tests/test_thenable_vs_promise_ordering.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for promise resolution ordering with thenables and promises</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("A promise resolved first (with a thenable) should trigger its callbacks before a promise resolved second (with a promise).");
+t.step(function() {
+ var customThenCalled = false;
+ var p0 = Promise.resolve();
+ p0.then = function(resolved, rejected) {
+ customThenCalled = true;
+ Promise.prototype.then.call(this, resolved, rejected);
+ }
+ var p1 = new Promise(function(r) { r(p0); });
+ delete p0.then;
+ var p2 = new Promise(function(r) { r(p0); });
+ var resolutionOrder = "";
+ Promise.all([ p1.then(function() { resolutionOrder += "1"; }),
+ p2.then(function() { resolutionOrder += "2"; }) ])
+ .then(t.step_func_done(function() {
+ assert_true(customThenCalled, "Should have called custom then");
+ assert_equals(resolutionOrder, "12");
+ }));
+});
+</script>
diff --git a/dom/promise/tests/test_webassembly_compile.html b/dom/promise/tests/test_webassembly_compile.html
new file mode 100644
index 000000000..0243df49c
--- /dev/null
+++ b/dom/promise/tests/test_webassembly_compile.html
@@ -0,0 +1,174 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>WebAssembly.compile Test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+const wasmTextToBinary = SpecialPowers.unwrap(SpecialPowers.Cu.getJSTestingFunctions().wasmTextToBinary);
+const wasmIsSupported = SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported
+const fooModuleCode = wasmTextToBinary(`(module
+ (func $foo (result i32) (i32.const 42))
+ (export "foo" $foo)
+)`, 'new-format');
+
+function checkFooModule(m) {
+ ok(m instanceof WebAssembly.Module, "got a module");
+ var i = new WebAssembly.Instance(m);
+ ok(i instanceof WebAssembly.Instance, "got an instance");
+ ok(i.exports.foo() === 42, "got 42");
+}
+
+function checkFooInstance(i) {
+ ok(i instanceof WebAssembly.Instance, "got a module");
+ ok(i.exports.foo() === 42, "got 42");
+}
+
+function propertiesExist() {
+ if (!wasmIsSupported()) {
+ ok(!this["WebAssembly"], "If the device doesn't support, there will be no WebAssembly object");
+ SimpleTest.finish();
+ return;
+ }
+
+ ok(WebAssembly, "WebAssembly object should exist");
+ ok(WebAssembly.compile, "WebAssembly.compile function should exist");
+ runTest();
+}
+
+function compileFail() {
+ WebAssembly.compile().then(
+ () => { ok(false, "should have failed"); runTest() }
+ ).catch(
+ err => { ok(err instanceof TypeError, "empty compile failed"); runTest() }
+ );
+}
+
+function compileSuccess() {
+ WebAssembly.compile(fooModuleCode).then(
+ m => { checkFooModule(m); runTest() }
+ ).catch(
+ err => { ok(false, String(err)); runTest() }
+ );
+}
+
+function compileManySuccess() {
+ const N = 100;
+
+ var arr = [];
+ for (var i = 0; i < N; i++)
+ arr.push(WebAssembly.compile(fooModuleCode));
+
+ SpecialPowers.gc();
+
+ Promise.all(arr).then (ms => {
+ ok(ms.length === N, "got the right number");
+ for (var i = 0; i < N; i++)
+ checkFooModule(ms[i]);
+ runTest();
+ }).catch(
+ err => { ok(false, String(err)); runTest() }
+ );
+}
+
+function compileInWorker() {
+ var w = new Worker(`data:text/plain,
+ onmessage = e => {
+ WebAssembly.compile(e.data).then(m => {
+ var i = new WebAssembly.Instance(m);
+ if (i.exports.foo() !== 42)
+ throw "bad i.exports.foo() result";
+ postMessage("ok");
+ close();
+ }).catch(err => { throw err });
+ }
+ `);
+ w.postMessage(fooModuleCode);
+ w.onmessage = e => {
+ ok(e.data === "ok", "worker test");
+ runTest();
+ }
+}
+
+function terminateCompileInWorker() {
+ var w = new Worker(`data:text/plain,
+ var fooModuleCode;
+ function spawnWork() {
+ const N = 100;
+ var arr = [];
+ for (var i = 0; i < N; i++)
+ arr.push(WebAssembly.compile(fooModuleCode));
+ Promise.all(arr).then(spawnWork);
+ }
+ onmessage = e => {
+ fooModuleCode = e.data;
+ spawnWork();
+ postMessage("ok");
+ }
+ `);
+ w.postMessage(fooModuleCode);
+ w.onmessage = e => {
+ ok(e.data === "ok", "worker finished first step");
+ w.terminate();
+ runTest();
+ }
+}
+
+function instantiateFail() {
+ WebAssembly.instantiate().then(
+ () => { ok(false, "should have failed"); runTest() }
+ ).catch(
+ err => { ok(err instanceof TypeError, "empty compile failed"); runTest() }
+ );
+}
+
+function instantiateSuccess() {
+ WebAssembly.instantiate(fooModuleCode).then(
+ r => { checkFooModule(r.module); checkFooInstance(r.instance); runTest() }
+ ).catch(
+ err => { ok(false, String(err)); runTest() }
+ );
+}
+
+function chainSuccess() {
+ WebAssembly.compile(fooModuleCode).then(
+ m => WebAssembly.instantiate(m)
+ ).then(
+ i => { checkFooInstance(i); runTest() }
+ ).catch(
+ err => { ok(false, String(err)); runTest() }
+ );
+}
+
+var tests = [ propertiesExist,
+ compileFail,
+ compileSuccess,
+ compileManySuccess,
+ compileInWorker,
+ terminateCompileInWorker,
+ instantiateFail,
+ instantiateSuccess,
+ chainSuccess
+ ];
+
+function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["javascript.options.wasm", true]]}, runTest);
+</script>
+</body>
+</html>
+
diff --git a/dom/promise/tests/unit/test_monitor_uncaught.js b/dom/promise/tests/unit/test_monitor_uncaught.js
new file mode 100644
index 000000000..7dd80d212
--- /dev/null
+++ b/dom/promise/tests/unit/test_monitor_uncaught.js
@@ -0,0 +1,274 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var { utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Timer.jsm", this);
+Cu.import("resource://testing-common/PromiseTestUtils.jsm", this);
+
+// Prevent test failures due to the unhandled rejections in this test file.
+PromiseTestUtils.disableUncaughtRejectionObserverForSelfTest();
+
+add_task(function* test_globals() {
+ Assert.equal(Promise.defer || undefined, undefined, "We are testing DOM Promise.");
+ Assert.notEqual(PromiseDebugging, undefined, "PromiseDebugging is available.");
+});
+
+add_task(function* test_promiseID() {
+ let p1 = new Promise(resolve => {});
+ let p2 = new Promise(resolve => {});
+ let p3 = p2.then(null, null);
+ let promise = [p1, p2, p3];
+
+ let identifiers = promise.map(PromiseDebugging.getPromiseID);
+ do_print("Identifiers: " + JSON.stringify(identifiers));
+ let idSet = new Set(identifiers);
+ Assert.equal(idSet.size, identifiers.length,
+ "PromiseDebugging.getPromiseID returns a distinct id per promise");
+
+ let identifiers2 = promise.map(PromiseDebugging.getPromiseID);
+ Assert.equal(JSON.stringify(identifiers),
+ JSON.stringify(identifiers2),
+ "Successive calls to PromiseDebugging.getPromiseID return the same id for the same promise");
+});
+
+add_task(function* test_observe_uncaught() {
+ // The names of Promise instances
+ let names = new Map();
+
+ // The results for UncaughtPromiseObserver callbacks.
+ let CallbackResults = function(name) {
+ this.name = name;
+ this.expected = new Set();
+ this.observed = new Set();
+ this.blocker = new Promise(resolve => this.resolve = resolve);
+ };
+ CallbackResults.prototype = {
+ observe: function(promise) {
+ do_print(this.name + " observing Promise " + names.get(promise));
+ Assert.equal(PromiseDebugging.getState(promise).state, "rejected",
+ this.name + " observed a rejected Promise");
+ if (!this.expected.has(promise)) {
+ Assert.ok(false,
+ this.name + " observed a Promise that it expected to observe, " +
+ names.get(promise) +
+ " (" + PromiseDebugging.getPromiseID(promise) +
+ ", " + PromiseDebugging.getAllocationStack(promise) + ")");
+
+ }
+ Assert.ok(this.expected.delete(promise),
+ this.name + " observed a Promise that it expected to observe, " +
+ names.get(promise) + " (" + PromiseDebugging.getPromiseID(promise) + ")");
+ Assert.ok(!this.observed.has(promise),
+ this.name + " observed a Promise that it has not observed yet");
+ this.observed.add(promise);
+ if (this.expected.size == 0) {
+ this.resolve();
+ } else {
+ do_print(this.name + " is still waiting for " + this.expected.size + " observations:");
+ do_print(JSON.stringify(Array.from(this.expected.values(), (x) => names.get(x))));
+ }
+ },
+ };
+
+ let onLeftUncaught = new CallbackResults("onLeftUncaught");
+ let onConsumed = new CallbackResults("onConsumed");
+
+ let observer = {
+ onLeftUncaught: function(promise, data) {
+ onLeftUncaught.observe(promise);
+ },
+ onConsumed: function(promise) {
+ onConsumed.observe(promise);
+ },
+ };
+
+ let resolveLater = function(delay = 20) {
+ return new Promise((resolve, reject) => setTimeout(resolve, delay));
+ };
+ let rejectLater = function(delay = 20) {
+ return new Promise((resolve, reject) => setTimeout(reject, delay));
+ };
+ let makeSamples = function*() {
+ yield {
+ promise: Promise.resolve(0),
+ name: "Promise.resolve",
+ };
+ yield {
+ promise: Promise.resolve(resolve => resolve(0)),
+ name: "Resolution callback",
+ };
+ yield {
+ promise: Promise.resolve(0).then(null, null),
+ name: "`then(null, null)`"
+ };
+ yield {
+ promise: Promise.reject(0).then(null, () => {}),
+ name: "Reject and catch immediately",
+ };
+ yield {
+ promise: resolveLater(),
+ name: "Resolve later",
+ };
+ yield {
+ promise: Promise.reject("Simple rejection"),
+ leftUncaught: true,
+ consumed: false,
+ name: "Promise.reject",
+ };
+
+ // Reject a promise now, consume it later.
+ let p = Promise.reject("Reject now, consume later");
+ setTimeout(() => p.then(null, () => {
+ do_print("Consumed promise");
+ }), 200);
+ yield {
+ promise: p,
+ leftUncaught: true,
+ consumed: true,
+ name: "Reject now, consume later",
+ };
+
+ yield {
+ promise: Promise.all([
+ Promise.resolve("Promise.all"),
+ rejectLater()
+ ]),
+ leftUncaught: true,
+ name: "Rejecting through Promise.all"
+ };
+ yield {
+ promise: Promise.race([
+ resolveLater(500),
+ Promise.reject(),
+ ]),
+ leftUncaught: true, // The rejection wins the race.
+ name: "Rejecting through Promise.race",
+ };
+ yield {
+ promise: Promise.race([
+ Promise.resolve(),
+ rejectLater(500)
+ ]),
+ leftUncaught: false, // The resolution wins the race.
+ name: "Resolving through Promise.race",
+ };
+
+ let boom = new Error("`throw` in the constructor");
+ yield {
+ promise: new Promise(() => { throw boom; }),
+ leftUncaught: true,
+ name: "Throwing in the constructor",
+ };
+
+ let rejection = Promise.reject("`reject` during resolution");
+ yield {
+ promise: rejection,
+ leftUncaught: false,
+ consumed: false, // `rejection` is consumed immediately (see below)
+ name: "Promise.reject, again",
+ };
+
+ yield {
+ promise: new Promise(resolve => resolve(rejection)),
+ leftUncaught: true,
+ consumed: false,
+ name: "Resolving with a rejected promise",
+ };
+
+ yield {
+ promise: Promise.resolve(0).then(() => rejection),
+ leftUncaught: true,
+ consumed: false,
+ name: "Returning a rejected promise from success handler",
+ };
+
+ yield {
+ promise: Promise.resolve(0).then(() => { throw new Error(); }),
+ leftUncaught: true,
+ consumed: false,
+ name: "Throwing during the call to the success callback",
+ };
+ };
+ let samples = [];
+ for (let s of makeSamples()) {
+ samples.push(s);
+ do_print("Promise '" + s.name + "' has id " + PromiseDebugging.getPromiseID(s.promise));
+ }
+
+ PromiseDebugging.addUncaughtRejectionObserver(observer);
+
+ for (let s of samples) {
+ names.set(s.promise, s.name);
+ if (s.leftUncaught || false) {
+ onLeftUncaught.expected.add(s.promise);
+ }
+ if (s.consumed || false) {
+ onConsumed.expected.add(s.promise);
+ }
+ }
+
+ do_print("Test setup, waiting for callbacks.");
+ yield onLeftUncaught.blocker;
+
+ do_print("All calls to onLeftUncaught are complete.");
+ if (onConsumed.expected.size != 0) {
+ do_print("onConsumed is still waiting for the following Promise:");
+ do_print(JSON.stringify(Array.from(onConsumed.expected.values(), (x) => names.get(x))));
+ yield onConsumed.blocker;
+ }
+
+ do_print("All calls to onConsumed are complete.");
+ let removed = PromiseDebugging.removeUncaughtRejectionObserver(observer);
+ Assert.ok(removed, "removeUncaughtRejectionObserver succeeded");
+ removed = PromiseDebugging.removeUncaughtRejectionObserver(observer);
+ Assert.ok(!removed, "second call to removeUncaughtRejectionObserver didn't remove anything");
+});
+
+
+add_task(function* test_uninstall_observer() {
+ let Observer = function() {
+ this.blocker = new Promise(resolve => this.resolve = resolve);
+ this.active = true;
+ };
+ Observer.prototype = {
+ set active(x) {
+ this._active = x;
+ if (x) {
+ PromiseDebugging.addUncaughtRejectionObserver(this);
+ } else {
+ PromiseDebugging.removeUncaughtRejectionObserver(this);
+ }
+ },
+ onLeftUncaught: function() {
+ Assert.ok(this._active, "This observer is active.");
+ this.resolve();
+ },
+ onConsumed: function() {
+ Assert.ok(false, "We should not consume any Promise.");
+ },
+ };
+
+ do_print("Adding an observer.");
+ let deactivate = new Observer();
+ Promise.reject("I am an uncaught rejection.");
+ yield deactivate.blocker;
+ Assert.ok(true, "The observer has observed an uncaught Promise.");
+ deactivate.active = false;
+ do_print("Removing the observer, it should not observe any further uncaught Promise.");
+
+ do_print("Rejecting a Promise and waiting a little to give a chance to observers.");
+ let wait = new Observer();
+ Promise.reject("I am another uncaught rejection.");
+ yield wait.blocker;
+ yield new Promise(resolve => setTimeout(resolve, 100));
+ // Normally, `deactivate` should not be notified of the uncaught rejection.
+ wait.active = false;
+});
+
+function run_test() {
+ run_next_test();
+}
diff --git a/dom/promise/tests/unit/xpcshell.ini b/dom/promise/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..73df2380b
--- /dev/null
+++ b/dom/promise/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_monitor_uncaught.js]