970 lines
37 KiB
JavaScript
970 lines
37 KiB
JavaScript
"use strict";
|
|
|
|
var assert = require("assert");
|
|
var thenables = require("./helpers/thenables");
|
|
var reasons = require("./helpers/reasons");
|
|
|
|
var adapter = global.adapter;
|
|
var resolved = adapter.resolved;
|
|
var rejected = adapter.rejected;
|
|
var deferred = adapter.deferred;
|
|
|
|
var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
|
|
var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
|
|
var other = { other: "other" }; // a value we don't want to be strict equal to
|
|
var sentinelArray = [sentinel]; // a sentinel fulfillment value to test when we need an array
|
|
|
|
function testPromiseResolution(xFactory, test) {
|
|
specify("via return from a fulfilled promise", function (done) {
|
|
var promise = resolved(dummy).then(function onBasePromiseFulfilled() {
|
|
return xFactory();
|
|
});
|
|
|
|
test(promise, done);
|
|
});
|
|
|
|
specify("via return from a rejected promise", function (done) {
|
|
var promise = rejected(dummy).then(null, function onBasePromiseRejected() {
|
|
return xFactory();
|
|
});
|
|
|
|
test(promise, done);
|
|
});
|
|
}
|
|
|
|
function testCallingResolvePromise(yFactory, stringRepresentation, test) {
|
|
describe("`y` is " + stringRepresentation, function () {
|
|
describe("`then` calls `resolvePromise` synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(yFactory());
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, test);
|
|
});
|
|
|
|
describe("`then` calls `resolvePromise` asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
setTimeout(function () {
|
|
resolvePromise(yFactory());
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, test);
|
|
});
|
|
});
|
|
}
|
|
|
|
function testCallingRejectPromise(r, stringRepresentation, test) {
|
|
describe("`r` is " + stringRepresentation, function () {
|
|
describe("`then` calls `rejectPromise` synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(r);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, test);
|
|
});
|
|
|
|
describe("`then` calls `rejectPromise` asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
setTimeout(function () {
|
|
rejectPromise(r);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, test);
|
|
});
|
|
});
|
|
}
|
|
|
|
function testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, fulfillmentValue) {
|
|
testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) {
|
|
promise.then(function onPromiseFulfilled(value) {
|
|
assert.strictEqual(value, fulfillmentValue);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
function testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, rejectionReason) {
|
|
testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) {
|
|
promise.then(null, function onPromiseRejected(reason) {
|
|
assert.strictEqual(reason, rejectionReason);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
function testCallingRejectPromiseRejectsWith(reason, stringRepresentation) {
|
|
testCallingRejectPromise(reason, stringRepresentation, function (promise, done) {
|
|
promise.then(null, function onPromiseRejected(rejectionReason) {
|
|
assert.strictEqual(rejectionReason, reason);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
describe("2.3.3: Otherwise, if `x` is an object or function,", function () {
|
|
describe("2.3.3.1: Let `then` be `x.then`", function () {
|
|
describe("`x` is an object with null prototype", function () {
|
|
var numberOfTimesThenWasRetrieved = null;
|
|
|
|
beforeEach(function () {
|
|
numberOfTimesThenWasRetrieved = 0;
|
|
});
|
|
|
|
function xFactory() {
|
|
return Object.create(null, {
|
|
then: {
|
|
get: function () {
|
|
++numberOfTimesThenWasRetrieved;
|
|
return function thenMethodForX(onFulfilled) {
|
|
onFulfilled();
|
|
};
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function () {
|
|
assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`x` is an object with normal Object.prototype", function () {
|
|
var numberOfTimesThenWasRetrieved = null;
|
|
|
|
beforeEach(function () {
|
|
numberOfTimesThenWasRetrieved = 0;
|
|
});
|
|
|
|
function xFactory() {
|
|
return Object.create(Object.prototype, {
|
|
then: {
|
|
get: function () {
|
|
++numberOfTimesThenWasRetrieved;
|
|
return function thenMethodForX(onFulfilled) {
|
|
onFulfilled();
|
|
};
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function () {
|
|
assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`x` is a function", function () {
|
|
var numberOfTimesThenWasRetrieved = null;
|
|
|
|
beforeEach(function () {
|
|
numberOfTimesThenWasRetrieved = 0;
|
|
});
|
|
|
|
function xFactory() {
|
|
function x() { }
|
|
|
|
Object.defineProperty(x, "then", {
|
|
get: function () {
|
|
++numberOfTimesThenWasRetrieved;
|
|
return function thenMethodForX(onFulfilled) {
|
|
onFulfilled();
|
|
};
|
|
}
|
|
});
|
|
|
|
return x;
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function () {
|
|
assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with " +
|
|
"`e` as the reason.", function () {
|
|
function testRejectionViaThrowingGetter(e, stringRepresentation) {
|
|
function xFactory() {
|
|
return Object.create(Object.prototype, {
|
|
then: {
|
|
get: function () {
|
|
throw e;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
describe("`e` is " + stringRepresentation, function () {
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, e);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
Object.keys(reasons).forEach(function (stringRepresentation) {
|
|
testRejectionViaThrowingGetter(reasons[stringRepresentation], stringRepresentation);
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3: If `then` is a function, call it with `x` as `this`, first argument `resolvePromise`, and " +
|
|
"second argument `rejectPromise`", function () {
|
|
describe("Calls with `x` as `this` and two function arguments", function () {
|
|
function xFactory() {
|
|
var x = {
|
|
then: function (onFulfilled, onRejected) {
|
|
assert.strictEqual(this, x);
|
|
assert.strictEqual(typeof onFulfilled, "function");
|
|
assert.strictEqual(typeof onRejected, "function");
|
|
onFulfilled();
|
|
}
|
|
};
|
|
return x;
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function () {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Uses the original value of `then`", function () {
|
|
var numberOfTimesThenWasRetrieved = null;
|
|
|
|
beforeEach(function () {
|
|
numberOfTimesThenWasRetrieved = 0;
|
|
});
|
|
|
|
function xFactory() {
|
|
return Object.create(Object.prototype, {
|
|
then: {
|
|
get: function () {
|
|
if (numberOfTimesThenWasRetrieved === 0) {
|
|
return function (onFulfilled) {
|
|
onFulfilled();
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function () {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3.1: If/when `resolvePromise` is called with value `y`, run `[[Resolve]](promise, y)`",
|
|
function () {
|
|
describe("`y` is not a thenable", function () {
|
|
testCallingResolvePromiseFulfillsWith(function () { return undefined; }, "`undefined`", undefined);
|
|
testCallingResolvePromiseFulfillsWith(function () { return null; }, "`null`", null);
|
|
testCallingResolvePromiseFulfillsWith(function () { return false; }, "`false`", false);
|
|
testCallingResolvePromiseFulfillsWith(function () { return 5; }, "`5`", 5);
|
|
testCallingResolvePromiseFulfillsWith(function () { return sentinel; }, "an object", sentinel);
|
|
testCallingResolvePromiseFulfillsWith(function () { return sentinelArray; }, "an array", sentinelArray);
|
|
});
|
|
|
|
describe("`y` is a thenable", function () {
|
|
Object.keys(thenables.fulfilled).forEach(function (stringRepresentation) {
|
|
function yFactory() {
|
|
return thenables.fulfilled[stringRepresentation](sentinel);
|
|
}
|
|
|
|
testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel);
|
|
});
|
|
|
|
Object.keys(thenables.rejected).forEach(function (stringRepresentation) {
|
|
function yFactory() {
|
|
return thenables.rejected[stringRepresentation](sentinel);
|
|
}
|
|
|
|
testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel);
|
|
});
|
|
});
|
|
|
|
describe("`y` is a thenable for a thenable", function () {
|
|
Object.keys(thenables.fulfilled).forEach(function (outerStringRepresentation) {
|
|
var outerThenableFactory = thenables.fulfilled[outerStringRepresentation];
|
|
|
|
Object.keys(thenables.fulfilled).forEach(function (innerStringRepresentation) {
|
|
var innerThenableFactory = thenables.fulfilled[innerStringRepresentation];
|
|
|
|
var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation;
|
|
|
|
function yFactory() {
|
|
return outerThenableFactory(innerThenableFactory(sentinel));
|
|
}
|
|
|
|
testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel);
|
|
});
|
|
|
|
Object.keys(thenables.rejected).forEach(function (innerStringRepresentation) {
|
|
var innerThenableFactory = thenables.rejected[innerStringRepresentation];
|
|
|
|
var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation;
|
|
|
|
function yFactory() {
|
|
return outerThenableFactory(innerThenableFactory(sentinel));
|
|
}
|
|
|
|
testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3.2: If/when `rejectPromise` is called with reason `r`, reject `promise` with `r`",
|
|
function () {
|
|
Object.keys(reasons).forEach(function (stringRepresentation) {
|
|
testCallingRejectPromiseRejectsWith(reasons[stringRepresentation](), stringRepresentation);
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3.3: If both `resolvePromise` and `rejectPromise` are called, or multiple calls to the same " +
|
|
"argument are made, the first call takes precedence, and any further calls are ignored.",
|
|
function () {
|
|
describe("calling `resolvePromise` then `rejectPromise`, both synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
resolvePromise(sentinel);
|
|
rejectPromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` synchronously then `rejectPromise` asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
resolvePromise(sentinel);
|
|
|
|
setTimeout(function () {
|
|
rejectPromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` then `rejectPromise`, both asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
setTimeout(function () {
|
|
resolvePromise(sentinel);
|
|
}, 0);
|
|
|
|
setTimeout(function () {
|
|
rejectPromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling " +
|
|
"`rejectPromise`, both synchronously", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.resolve(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
resolvePromise(d.promise);
|
|
rejectPromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling " +
|
|
"`rejectPromise`, both synchronously", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.reject(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
resolvePromise(d.promise);
|
|
rejectPromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` then `resolvePromise`, both synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
resolvePromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` synchronously then `resolvePromise` asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
|
|
setTimeout(function () {
|
|
resolvePromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` then `resolvePromise`, both asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
setTimeout(function () {
|
|
rejectPromise(sentinel);
|
|
}, 0);
|
|
|
|
setTimeout(function () {
|
|
resolvePromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` twice synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(sentinel);
|
|
resolvePromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` twice, first synchronously then asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(sentinel);
|
|
|
|
setTimeout(function () {
|
|
resolvePromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` twice, both times asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
setTimeout(function () {
|
|
resolvePromise(sentinel);
|
|
}, 0);
|
|
|
|
setTimeout(function () {
|
|
resolvePromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling it again, both " +
|
|
"times synchronously", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.resolve(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(d.promise);
|
|
resolvePromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling it again, both " +
|
|
"times synchronously", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.reject(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(d.promise);
|
|
resolvePromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` twice synchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
rejectPromise(other);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` twice, first synchronously then asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
|
|
setTimeout(function () {
|
|
rejectPromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calling `rejectPromise` twice, both times asynchronously", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
setTimeout(function () {
|
|
rejectPromise(sentinel);
|
|
}, 0);
|
|
|
|
setTimeout(function () {
|
|
rejectPromise(other);
|
|
}, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("saving and abusing `resolvePromise` and `rejectPromise`", function () {
|
|
var savedResolvePromise, savedRejectPromise;
|
|
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
savedResolvePromise = resolvePromise;
|
|
savedRejectPromise = rejectPromise;
|
|
}
|
|
};
|
|
}
|
|
|
|
beforeEach(function () {
|
|
savedResolvePromise = null;
|
|
savedRejectPromise = null;
|
|
});
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
var timesFulfilled = 0;
|
|
var timesRejected = 0;
|
|
|
|
promise.then(
|
|
function () {
|
|
++timesFulfilled;
|
|
},
|
|
function () {
|
|
++timesRejected;
|
|
}
|
|
);
|
|
|
|
if (savedResolvePromise && savedRejectPromise) {
|
|
savedResolvePromise(dummy);
|
|
savedResolvePromise(dummy);
|
|
savedRejectPromise(dummy);
|
|
savedRejectPromise(dummy);
|
|
}
|
|
|
|
setTimeout(function () {
|
|
savedResolvePromise(dummy);
|
|
savedResolvePromise(dummy);
|
|
savedRejectPromise(dummy);
|
|
savedRejectPromise(dummy);
|
|
}, 50);
|
|
|
|
setTimeout(function () {
|
|
assert.strictEqual(timesFulfilled, 1);
|
|
assert.strictEqual(timesRejected, 0);
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3.4: If calling `then` throws an exception `e`,", function () {
|
|
describe("2.3.3.3.4.1: If `resolvePromise` or `rejectPromise` have been called, ignore it.", function () {
|
|
describe("`resolvePromise` was called with a non-thenable", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(sentinel);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`resolvePromise` was called with an asynchronously-fulfilled promise", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.resolve(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(d.promise);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`resolvePromise` was called with an asynchronously-rejected promise", function () {
|
|
function xFactory() {
|
|
var d = deferred();
|
|
setTimeout(function () {
|
|
d.reject(sentinel);
|
|
}, 50);
|
|
|
|
return {
|
|
then: function (resolvePromise) {
|
|
resolvePromise(d.promise);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`rejectPromise` was called", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`resolvePromise` then `rejectPromise` were called", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
resolvePromise(sentinel);
|
|
rejectPromise(other);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`rejectPromise` then `resolvePromise` were called", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
rejectPromise(sentinel);
|
|
resolvePromise(other);
|
|
throw other;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.3.4.2: Otherwise, reject `promise` with `e` as the reason.", function () {
|
|
describe("straightforward case", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function () {
|
|
throw sentinel;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`resolvePromise` is called asynchronously before the `throw`", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise) {
|
|
setTimeout(function () {
|
|
resolvePromise(other);
|
|
}, 0);
|
|
throw sentinel;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("`rejectPromise` is called asynchronously before the `throw`", function () {
|
|
function xFactory() {
|
|
return {
|
|
then: function (resolvePromise, rejectPromise) {
|
|
setTimeout(function () {
|
|
rejectPromise(other);
|
|
}, 0);
|
|
throw sentinel;
|
|
}
|
|
};
|
|
}
|
|
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(null, function (reason) {
|
|
assert.strictEqual(reason, sentinel);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("2.3.3.4: If `then` is not a function, fulfill promise with `x`", function () {
|
|
function testFulfillViaNonFunction(then, stringRepresentation) {
|
|
var x = null;
|
|
|
|
beforeEach(function () {
|
|
x = { then: then };
|
|
});
|
|
|
|
function xFactory() {
|
|
return x;
|
|
}
|
|
|
|
describe("`then` is " + stringRepresentation, function () {
|
|
testPromiseResolution(xFactory, function (promise, done) {
|
|
promise.then(function (value) {
|
|
assert.strictEqual(value, x);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
testFulfillViaNonFunction(5, "`5`");
|
|
testFulfillViaNonFunction({}, "an object");
|
|
testFulfillViaNonFunction([function () { }], "an array containing a function");
|
|
testFulfillViaNonFunction(/a-b/i, "a regular expression");
|
|
testFulfillViaNonFunction(Object.create(Function.prototype), "an object inheriting from `Function.prototype`");
|
|
});
|
|
});
|