/*
|
* linting omitted on spec mainly because jslint doesn"t seem to allow the
|
* mocha expression-like syntax
|
*/
|
"use strict";
|
const expect = require("chai").expect,
|
sut = require("./index"),
|
SynchronousPromise = sut.SynchronousPromise;
|
|
describe("synchronous-promise", function () {
|
it("should be constructable", function () {
|
expect(SynchronousPromise).to.exist;
|
expect(new SynchronousPromise(function () {
|
})).to.exist;
|
});
|
it("should have a then function", function () {
|
expect(SynchronousPromise.prototype.then).to.be.a("function");
|
});
|
it("should have a catch function", function () {
|
expect(SynchronousPromise.prototype.catch).to.be.a("function");
|
});
|
|
function create(ctor) {
|
return new SynchronousPromise(ctor);
|
}
|
|
function createResolved(data) {
|
return SynchronousPromise.resolve(data);
|
}
|
|
function createRejected(data) {
|
return SynchronousPromise.reject(data);
|
}
|
|
describe("then", function () {
|
it("when resolved, should return a new resolved promise", function () {
|
const sut = createResolved();
|
expect(sut.then(null, function () {
|
})).to.be.instanceOf(SynchronousPromise);
|
});
|
|
it("should return the new resolved promise v2", function () {
|
const result = createResolved().then(function () {
|
/* purposely don't return anything */
|
});
|
expect(result).to.be.instanceOf(SynchronousPromise);
|
});
|
|
it("should return a new rejected promise", function () {
|
const sut = createRejected();
|
expect(sut.then(function () {
|
})).to.be.instanceOf(SynchronousPromise);
|
});
|
|
it("should return a new rejected promise v2", function () {
|
const result = createRejected().then(function () {
|
/* purposely don't return anything */
|
});
|
expect(result).to.be.instanceOf(SynchronousPromise)
|
});
|
|
it("should bring the first resolve value into the first then", function () {
|
const initial = "123";
|
let captured = null;
|
createResolved(initial).then(function (data) {
|
captured = data;
|
});
|
expect(captured).to.equal(initial);
|
});
|
|
it("should call into the immediate catch function when the function given to then throws", function () {
|
const sut = createResolved(),
|
expected = "the error";
|
let received = null;
|
sut.then(function () {
|
throw new Error(expected);
|
}).catch(function (err) {
|
received = err;
|
});
|
expect(received.message).to.equal(expected);
|
});
|
|
it(`should allow re-throwing in a .catch and re-catching later`, () => {
|
// Arrange
|
const sut = createResolved(),
|
error1 = "moo";
|
let received = null;
|
const expected = "moo-cow";
|
// Act
|
sut.then(function () {
|
throw new Error(error1);
|
}).catch(function (err) {
|
debugger;
|
throw new Error(err.message + "-cow");
|
}).catch(function (err) {
|
received = err.message;
|
});
|
// Assert
|
expect(received).to.equal(expected);
|
});
|
|
it("should call the catch from a rejection invoke", () => {
|
// Arrange
|
const expected = "moo",
|
sut = new SynchronousPromise(function (resolve, reject) {
|
reject(expected);
|
});
|
let captured = null;
|
// Act
|
sut.catch(function (e) {
|
captured = e;
|
});
|
// Assert
|
expect(captured).to.equal(expected);
|
});
|
|
it("should call into the later catch function when the function given to then throws", function () {
|
const sut = createResolved(),
|
expected = "the error";
|
let received = null;
|
sut.then(function () {
|
throw new Error(expected);
|
}).then(function () {
|
return 42; // not a thrower
|
}).catch(function (err) {
|
received = err;
|
});
|
|
expect(received.message).to.equal(expected);
|
});
|
|
it("should prefer to call into onRejected over the .catch handler on failure", function () {
|
const sut = createResolved(),
|
expected = "the error";
|
let captured = null,
|
catchCaptured = null;
|
sut.then(function () {
|
throw expected;
|
}, function (e) {
|
captured = e;
|
}).catch(function (e) {
|
console.log(".catch handler");
|
catchCaptured = e;
|
});
|
|
expect(captured).to.equal(expected);
|
expect(catchCaptured).to.be.null;
|
});
|
|
it("should bring the first rejected value into the first onRejected then handler", function () {
|
const initial = new Error("123");
|
let captured = null;
|
createRejected(initial).then(function () {
|
}, function (e) {
|
captured = e
|
});
|
expect(captured).to.equal(initial);
|
});
|
|
it("should resolve when the first resolution is a resolved promise", function () {
|
const initial = createResolved("123");
|
let captured = null;
|
createResolved(initial).then(function (data) {
|
captured = data;
|
});
|
expect(captured).to.equal("123");
|
});
|
|
it("should catch when the first resolution is a rejected promise", function () {
|
const initial = createRejected("123");
|
let captured = null;
|
createResolved(initial).catch(function (data) {
|
captured = data;
|
});
|
expect(captured).to.equal("123");
|
});
|
|
it("should catch when a subsequent resolution returns a rejected promise", function () {
|
const initial = createResolved("123");
|
let captured = null;
|
const expected = "le error";
|
initial.then(function () {
|
return createRejected(expected);
|
}).catch(function (e) {
|
captured = e;
|
});
|
|
expect(captured).to.equal(expected);
|
});
|
|
it("should run a simple chain", function () {
|
const initial = "123",
|
second = "abc";
|
let captured = null;
|
createResolved(initial).then(function () {
|
return createResolved(second);
|
}).then(function (data) {
|
captured = data;
|
});
|
expect(captured).to.equal(second);
|
});
|
|
it("should run a longer chain", function () {
|
const initial = "123",
|
second = "abc",
|
third = "---",
|
expected = second + third;
|
let captured = null;
|
createResolved(initial).then(function () {
|
return createResolved(second);
|
}).then(function () {
|
return second;
|
}).then(function (data) {
|
captured = data + third;
|
});
|
expect(captured).to.equal(expected);
|
});
|
|
it("should run a longer chain v2", function () {
|
const initial = "123",
|
second = "abc",
|
third = "---",
|
expected = second + third;
|
let captured = null;
|
createResolved(initial).then(function () {
|
return createResolved(second);
|
}).then(function () {
|
return createResolved(second);
|
}).then(function (data) {
|
captured = data + third;
|
});
|
expect(captured).to.equal(expected);
|
});
|
|
it("should run a longer chain v3", function () {
|
const initial = "123",
|
second = "abc",
|
third = "---",
|
expected = second + third;
|
let captured = null;
|
createResolved(initial).then(function () {
|
return second;
|
}).then(function () {
|
return createResolved(second);
|
}).then(function (data) {
|
captured = data + third;
|
});
|
expect(captured).to.equal(expected);
|
});
|
|
it("should resolve when the ctor function resolves", function () {
|
let providedResolve = null,
|
captured = null;
|
const expected = "xyz",
|
promise = create(function (resolve) {
|
providedResolve = resolve;
|
}).then(function (data) {
|
captured = data;
|
});
|
expect(promise).to.exist;
|
expect(captured).to.be.null;
|
expect(providedResolve).to.be.a("function");
|
providedResolve(expected);
|
expect(captured).to.equal(expected);
|
});
|
|
it("should resolve the same value from the same promise multiple times", () => {
|
// Arrange
|
const expected = "multi-pass",
|
sut = SynchronousPromise.resolve(expected);
|
let captured1 = null,
|
captured2 = null;
|
// Act
|
sut.then(result => captured1 = result);
|
sut.then(result => captured2 = result);
|
// Assert
|
expect(captured1).to.equal(expected);
|
expect(captured2).to.equal(expected);
|
});
|
|
describe(`es6 native Promise compatibility`, () => {
|
it(`should pass on the prior result when no function provided`, async () => {
|
// Arrange
|
// Act
|
const result = await SynchronousPromise.resolve("expected")
|
.then();
|
// Assert
|
expect(result)
|
.to.equal("expected");
|
});
|
});
|
});
|
|
describe("catch", function () {
|
it("should be called if the initial reject is called", function () {
|
const expected = "123";
|
let captured = null;
|
createRejected(expected).catch(function (e) {
|
captured = e;
|
});
|
expect(captured).to.equal(expected);
|
});
|
|
it("should call handler if the promise resolves", function () {
|
// Arrange
|
const sut = SynchronousPromise.unresolved();
|
let resolved = false,
|
caught = false;
|
// Act
|
sut.then(() => resolved = true).catch(() => caught = true);
|
sut.resolve();
|
// Assert
|
expect(resolved).to.be.true;
|
expect(caught).to.be.false;
|
});
|
|
it("should be called on a delayed rejection", function () {
|
let providedReject = null,
|
captured = null;
|
const expected = "123",
|
promise = create(function (resolve, reject) {
|
providedReject = reject;
|
}).catch(function (e) {
|
captured = e;
|
});
|
expect(promise).to.exist;
|
expect(captured).to.be.null;
|
expect(providedReject).to.be.a("function");
|
providedReject(expected);
|
expect(captured).to.equal(expected);
|
});
|
|
it("should return a resolved promise if doesn't throw an error", function () {
|
const promise = createRejected("123"),
|
result = promise.catch(function (data) {
|
expect(data).to.equal("123");
|
});
|
|
expect(result).to.exist;
|
expect(result).to.be.instanceOf(SynchronousPromise);
|
expect(result.status).to.be.equal("resolved");
|
});
|
|
it("should not interfere with a later then if there is no error", function () {
|
let captured = null;
|
const expected = "123";
|
let capturedError = null;
|
createResolved(expected).catch(function (e) {
|
capturedError = e;
|
}).then(function (data) {
|
captured = data;
|
});
|
|
expect(capturedError).to.be.null;
|
expect(captured).to.equal(expected);
|
});
|
|
it("should not be called if the promise is handled successful by a previous onRejected handler", function () {
|
const expected = new Error("123"),
|
notExpected = new Error("Not expected");
|
let capturedError = null;
|
createRejected(expected).then(
|
function () {
|
},
|
function (e) {
|
capturedError = e
|
})
|
.catch(function () {
|
/* purposely don't return anything */
|
capturedError = notExpected;
|
});
|
|
expect(capturedError).to.equal(expected);
|
});
|
|
it("should prevent the handlers after the error from being called", function () {
|
let captured = null;
|
createResolved("123").catch(function (e) {
|
}).then(function () {
|
throw "foo";
|
}).then(function () {
|
captured = "abc";
|
});
|
|
expect(captured).to.be.null;
|
});
|
|
it("should re-catch if a catch handler returns a rejected promise", function (done) {
|
// Arrange
|
const expected = "123",
|
pausedRejectedPromise = SynchronousPromise.reject(expected).pause();
|
let capturedA = null,
|
capturedB = null;
|
|
pausedRejectedPromise.catch(function (e) {
|
capturedA = e;
|
// prove that this works even from an async promise
|
return Promise.reject(e);
|
}).catch(function (e) {
|
capturedB = e;
|
});
|
|
// Act
|
pausedRejectedPromise.resume();
|
|
// Assert
|
setTimeout(function () {
|
try {
|
expect(capturedA).to.equal(expected);
|
expect(capturedB).to.equal(expected);
|
done();
|
} catch (e) {
|
done(e);
|
}
|
}, 100);
|
});
|
|
it("should re-catch if a then onRejected handler returns a rejected promise", function (done) {
|
// Arrange
|
const expected = "123",
|
pausedRejectedPromise = SynchronousPromise.reject(expected).pause();
|
let capturedA = null,
|
capturedB = null;
|
|
pausedRejectedPromise.then(function () {
|
/* purposely don't return anything */
|
}, function (e) {
|
capturedA = e;
|
// prove that this works even from an async promise
|
return Promise.reject(e);
|
}).catch(function (e) {
|
capturedB = e;
|
});
|
|
// Act
|
pausedRejectedPromise.resume();
|
|
// Assert
|
setTimeout(function () {
|
expect(capturedA).to.equal(expected);
|
expect(capturedB).to.equal(expected);
|
done();
|
}, 100);
|
});
|
|
it("should continue if a catch handler returns a resolved promise", function (done) {
|
// Arrange
|
const expected = "123",
|
pausedRejectedPromise = SynchronousPromise.reject(expected).pause();
|
let capturedA = null,
|
capturedB = null,
|
secondResolve;
|
|
pausedRejectedPromise.catch(function (e) {
|
capturedA = e;
|
// prove that this works even from an async promise
|
return Promise.resolve("456");
|
}).catch(function (e) {
|
capturedB = e;
|
}).then(function (data) {
|
secondResolve = data;
|
});
|
|
// Act
|
pausedRejectedPromise.resume();
|
|
// Assert
|
setTimeout(function () {
|
try {
|
expect(capturedA).to.equal(expected);
|
expect(capturedB).to.be.null;
|
expect(secondResolve).to.equal("456");
|
done();
|
} catch (e) {
|
done(e);
|
}
|
}, 100);
|
});
|
});
|
|
describe("prototype pause", function () {
|
it("should exist as a function on the prototype", function () {
|
expect(SynchronousPromise.prototype.pause).to.be.a("function");
|
});
|
|
it("should return the promise", function () {
|
const
|
promise = createResolved("123"),
|
result = promise.pause();
|
expect(result).to.equal(promise);
|
});
|
|
it("should prevent resolution from continuing at that point", function () {
|
let calls = 0;
|
createResolved("123").then(function () {
|
return calls++;
|
}).pause().then(function () {
|
return calls++;
|
});
|
expect(calls).to.equal(1);
|
});
|
|
it("should prevent rejection from being caught at that point", function () {
|
let calls = 0;
|
createRejected("123").pause().catch(function () {
|
calls++;
|
});
|
expect(calls).to.equal(0);
|
});
|
|
it("should prevent rejection from continuing past at that point", function () {
|
let calls = 0,
|
captured = null;
|
|
createRejected("123").then(function () {
|
// should not be called
|
calls++;
|
}).catch(function (e) {
|
captured = e;
|
}).pause().then(function () {
|
calls++;
|
});
|
|
expect(captured).to.equal("123");
|
expect(calls).to.equal(0);
|
});
|
|
describe("starting paused", function () {
|
it("should return a promise in paused state with no initial data and being resolved on resume", function () {
|
let captured = undefined;
|
const promise = SynchronousPromise.resolve().pause().then(function () {
|
return "moo";
|
}).then(function (data) {
|
captured = data;
|
});
|
expect(captured).to.be.undefined;
|
promise.resume();
|
expect(captured).to.equal("moo");
|
});
|
|
it("should return a promise in paused state with no initial data and being rejected on resume", function () {
|
let captured = undefined;
|
const expected = new Error("moon"),
|
promise = SynchronousPromise.resolve().pause().then(function () {
|
throw expected
|
}).catch(function (e) {
|
captured = e;
|
});
|
expect(captured).to.be.undefined;
|
promise.resume();
|
expect(captured).to.equal(expected);
|
});
|
|
it("should return a promise in paused state with no initial data and being resolved after a catch on resume", function () {
|
let captured = undefined;
|
const error = new Error("moon"),
|
promise = SynchronousPromise.resolve().pause().then(function () {
|
throw error
|
}).catch(function (e) {
|
return e.message;
|
}).then(function (m) {
|
captured = m;
|
});
|
expect(captured).to.be.undefined;
|
promise.resume();
|
expect(captured).to.equal("moon");
|
});
|
});
|
});
|
describe("resume", function () {
|
it("should exist as a function on the prototype", function () {
|
expect(SynchronousPromise.prototype.resume).to.be.a("function");
|
});
|
|
it("should return the promise", function () {
|
const promise = createResolved("123").pause(),
|
result = promise.resume();
|
expect(result).to.equal(promise);
|
});
|
|
it("should not barf if the promise is not already paused", function () {
|
const promise = createResolved("123");
|
expect(function () {
|
promise.resume();
|
}).not.to.throw;
|
});
|
|
it("should resume resolution operations after the last pause", function () {
|
let calls = 0;
|
const promise = createResolved("123").then(function () {
|
return calls++;
|
}).pause().then(function () {
|
return calls++;
|
});
|
expect(calls).to.equal(1);
|
promise.resume();
|
expect(calls).to.equal(2);
|
});
|
|
it("should resume rejection operations after the last pause", function () {
|
let calls = 0,
|
captured = null;
|
const expected = "die, scum!",
|
promise = createResolved("123").then(function () {
|
throw expected;
|
}).pause().then(function () {
|
return calls++;
|
}).catch(function (e) {
|
captured = e;
|
});
|
expect(calls).to.equal(0);
|
expect(captured).to.be.null;
|
promise.resume();
|
expect(calls).to.equal(0);
|
expect(captured).to.equal(expected);
|
});
|
|
it("should resume a promise which was started rejected as rejected", function () {
|
let calls = 0,
|
captured = null;
|
const expected = "it\"s the end of the world!",
|
promise = SynchronousPromise.reject(expected).pause().then(function () {
|
calls++;
|
}).catch(function (e) {
|
captured = e;
|
});
|
expect(calls).to.equal(0);
|
expect(captured).to.be.null;
|
promise.resume();
|
expect(calls).to.equal(0);
|
expect(captured).to.equal(expected);
|
});
|
});
|
describe("static resolve", function () {
|
it("should be a function", function () {
|
expect(SynchronousPromise.resolve).to.be.a("function");
|
});
|
|
it("should return a resolved promise", function () {
|
const expected = "foo",
|
result = SynchronousPromise.resolve(expected);
|
expect(result.status).to.equal("resolved");
|
let captured = null;
|
result.then(function (data) {
|
captured = data;
|
});
|
expect(captured).to.equal(expected);
|
});
|
});
|
describe("static reject", function () {
|
it("should be a function", function () {
|
expect(SynchronousPromise.reject).to.be.a("function");
|
});
|
|
it("should return a rejected promise", function () {
|
const expected = "moo",
|
result = SynchronousPromise.reject(expected);
|
expect(result.status).to.equal("rejected");
|
let captured = null;
|
result.catch(function (err) {
|
captured = err;
|
});
|
expect(captured).to.equal(expected);
|
});
|
});
|
|
describe("static all", function () {
|
it("should be a function", function () {
|
expect(SynchronousPromise.all).to.be.a("function")
|
});
|
|
it("should resolve with all values from given resolved promises as variable args", function () {
|
const p1 = createResolved("abc"),
|
p2 = createResolved("123"),
|
all = SynchronousPromise.all(p1, p2);
|
let captured = null;
|
|
all.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.contain("abc");
|
expect(captured).to.contain("123");
|
});
|
|
it("should resolve with all values from given promise or none promise variable args", function () {
|
const all = SynchronousPromise.all(["123", createResolved("abc")]);
|
let captured = null;
|
|
all.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.contain("abc");
|
expect(captured).to.contain("123");
|
});
|
|
it("should resolve with all values from given resolved promises as an array", function () {
|
const p1 = createResolved("abc"),
|
p2 = createResolved("123"),
|
all = SynchronousPromise.all([p1, p2]);
|
let captured = null;
|
|
all.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.contain("abc");
|
expect(captured).to.contain("123");
|
});
|
|
it("should resolve empty promise array", function () {
|
const all = SynchronousPromise.all([]);
|
let captured = null;
|
|
all.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(0);
|
});
|
|
it("should resolve with values in the correct order", function () {
|
let resolve1 = undefined,
|
resolve2 = undefined,
|
captured = undefined;
|
|
const p1 = create(function (resolve) {
|
resolve1 = resolve;
|
});
|
|
const p2 = create(function (resolve) {
|
resolve2 = resolve;
|
});
|
|
SynchronousPromise.all([p1, p2]).then(function (data) {
|
captured = data;
|
});
|
|
resolve2("a");
|
resolve1("b");
|
|
expect(captured).to.deep.equal(["b", "a"]);
|
});
|
|
it("should reject if any promise rejects", function () {
|
const p1 = createResolved("abc"),
|
p2 = createRejected("123"),
|
all = SynchronousPromise.all(p1, p2);
|
let capturedData = null,
|
capturedError = null;
|
all.then(function (data) {
|
capturedData = data;
|
}).catch(function (err) {
|
capturedError = err;
|
});
|
expect(capturedData).to.be.null;
|
expect(capturedError).to.equal("123");
|
});
|
});
|
|
describe("static any", function () {
|
it("should be a function", function () {
|
expect(SynchronousPromise.any).to.be.a("function")
|
});
|
|
it("should resolve with first value from given resolved promises as variable args", function () {
|
const p1 = createResolved("abc"),
|
p2 = createResolved("123"),
|
any = SynchronousPromise.any(p1, p2);
|
let captured = null;
|
|
any.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.equal("abc");
|
});
|
|
it("should resolve with first value from given promise or none promise variable args", function () {
|
const any = SynchronousPromise.any(["123", createResolved("abc")]);
|
let captured = null;
|
|
any.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.equal("123");
|
});
|
|
it("should reject empty promise array", function () {
|
const any = SynchronousPromise.any([]);
|
let capturedData = null,
|
capturedError = null;
|
|
any.then(function (data) {
|
capturedData = data;
|
}, function (err) {
|
capturedError = err;
|
});
|
|
expect(capturedData).to.be.null;
|
expect(capturedError).to.have.property("errors");
|
expect(capturedError).property("errors").to.have.length(0);
|
});
|
|
it("should resolve if any promise resolves", function () {
|
const p1 = createResolved("abc"),
|
p2 = createRejected("123"),
|
any = SynchronousPromise.any(p1, p2);
|
let capturedData = null,
|
capturedError = null;
|
any.then(function (data) {
|
capturedData = data;
|
}).catch(function (err) {
|
capturedError = err;
|
});
|
|
expect(capturedData).to.equal("abc");
|
expect(capturedError).to.be.null;
|
});
|
|
it("should reject if all promises reject", function () {
|
const p1 = createRejected("abc"),
|
p2 = createRejected("123"),
|
any = SynchronousPromise.any(p1, p2);
|
let capturedData = null,
|
capturedError = null;
|
any.then(function (data) {
|
capturedData = data;
|
}).catch(function (err) {
|
capturedError = err;
|
});
|
|
expect(capturedData).to.be.null;
|
expect(capturedError).to.have.property("errors");
|
expect(capturedError).property("errors").to.have.length(2);
|
expect(capturedError).property("errors").to.contain("abc");
|
expect(capturedError).property("errors").to.contain("123");
|
});
|
|
it("should reject with values in the correct order", function () {
|
let reject1 = undefined,
|
reject2 = undefined,
|
capturedError = undefined;
|
|
const p1 = create(function (resolve, reject) {
|
reject1 = reject;
|
});
|
|
const p2 = create(function (resolve, reject) {
|
reject2 = reject;
|
});
|
|
SynchronousPromise.any([p1, p2]).catch(function (data) {
|
capturedError = data;
|
});
|
|
reject2("a");
|
reject1("b");
|
|
expect(capturedError).to.have.property("errors");
|
expect(capturedError).property("errors").to.deep.equal(["b", "a"]);
|
});
|
|
describe("in browsers supporting AggregateError", function() {
|
|
// Used to restore previous global.window value
|
let windowRef = null;
|
|
/** Mock of AggregateError */
|
class AggregateError extends Error {
|
constructor(errors, message) {
|
super(message);
|
this.name = "AggregateError";
|
this.errors = errors;
|
}
|
}
|
|
beforeEach(function() {
|
// Mock window object with AggregateError
|
windowRef = global.window;
|
global.window = { AggregateError };
|
});
|
|
afterEach(function () {
|
// Restore window object
|
global.window = windowRef;
|
windowRef = null;
|
});
|
|
it("should reject with AggregateError for empty promise array", function () {
|
const anyEmpty = SynchronousPromise.any([]);
|
let capturedError = null;
|
anyEmpty.catch(function (err) {
|
capturedError = err;
|
});
|
|
expect(capturedError).to.be.instanceOf(AggregateError);
|
});
|
|
it("should reject with AggregateError for rejected promises as variable args", function () {
|
const p1 = createRejected("abc"),
|
any = SynchronousPromise.any(p1);
|
let capturedError = null;
|
any.catch(function (err) {
|
capturedError = err;
|
});
|
|
expect(capturedError).to.be.instanceOf(AggregateError);
|
});
|
});
|
});
|
|
describe("static allSettled", function () {
|
it("should be a function", function () {
|
expect(SynchronousPromise.allSettled).to.be.a("function")
|
});
|
|
it("should resolve with all values from given resolved promises as variable args", function () {
|
const p1 = createResolved("abc"),
|
p2 = createResolved("123"),
|
allSettled = SynchronousPromise.allSettled(p1, p2);
|
let captured = null;
|
|
allSettled.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "abc" });
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "123" });
|
});
|
|
it("should resolve with all values from given rejected promises as variable args", function () {
|
const error1 = new Error("error1");
|
const error2 = new Error("error2");
|
const p1 = createRejected(error1),
|
p2 = createRejected(error2),
|
allSettled = SynchronousPromise.allSettled(p1, p2);
|
let captured = null;
|
|
allSettled.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.deep.contain({ status: "rejected", reason: error1 });
|
expect(captured).to.deep.contain({ status: "rejected", reason: error2 });
|
});
|
|
it("should resolve with all values from given promise or none promise variable args", function () {
|
const allSettled = SynchronousPromise.allSettled(["123", createResolved("abc")]);
|
let captured = null;
|
|
allSettled.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "abc" });
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "123" });
|
});
|
|
it("should resolve with all values from given resolved promises as an array", function () {
|
const p1 = createResolved("abc"),
|
p2 = createResolved("123"),
|
allSettled = SynchronousPromise.allSettled([p1, p2]);
|
let captured = null;
|
|
allSettled.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(2);
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "abc" });
|
expect(captured).to.deep.contain({ status: "fulfilled", value: "123" });
|
});
|
|
it("should resolve empty promise array", function () {
|
const allSettled = SynchronousPromise.allSettled([]);
|
let captured = null;
|
|
allSettled.then(function (data) {
|
captured = data;
|
});
|
|
expect(captured).to.have.length(0);
|
});
|
|
it("should resolve with values in the correct order", function () {
|
let resolve1 = undefined,
|
resolve2 = undefined,
|
captured = undefined;
|
|
const p1 = create(function (resolve) {
|
resolve1 = resolve;
|
});
|
|
const p2 = create(function (resolve) {
|
resolve2 = resolve;
|
});
|
|
SynchronousPromise.allSettled([p1, p2]).then(function (data) {
|
captured = data;
|
});
|
|
resolve2("a");
|
resolve1("b");
|
|
expect(captured).to.deep.equal([
|
{ status: "fulfilled", value: "b" },
|
{ status: "fulfilled", value: "a" }
|
]);
|
});
|
|
it("should only resolve after all promises are settled", function () {
|
const p1 = createResolved("abc"),
|
p2 = createRejected("123"),
|
allSettled = SynchronousPromise.allSettled(p1, p2);
|
let capturedData = null;
|
allSettled.then(function (data) {
|
capturedData = data;
|
});
|
|
expect(capturedData).to.have.length(2);
|
});
|
});
|
|
describe("static unresolved", function () {
|
it("should exist as a function", function () {
|
// Arrange
|
// Act
|
// Assert
|
expect(SynchronousPromise.unresolved).to.exist;
|
expect(SynchronousPromise.unresolved).to.be.a("function");
|
});
|
|
it("should return a new SynchronousPromise", function () {
|
// Arrange
|
// Act
|
const result1 = SynchronousPromise.unresolved(),
|
result2 = SynchronousPromise.unresolved();
|
// Assert
|
expect(result1).to.exist;
|
expect(result2).to.exist;
|
expect(Object.getPrototypeOf(result1)).to.equal(SynchronousPromise.prototype);
|
expect(Object.getPrototypeOf(result2)).to.equal(SynchronousPromise.prototype);
|
expect(result1).not.to.equal(result2);
|
});
|
|
describe("result", function () {
|
it("should not be resolved or rejected", function () {
|
// Arrange
|
let resolved = false,
|
rejected = false;
|
// Act
|
SynchronousPromise.unresolved().then(function () {
|
resolved = true;
|
}).catch(function () {
|
rejected = true;
|
});
|
// Assert
|
expect(resolved).to.be.false;
|
expect(rejected).to.be.false;
|
});
|
|
describe("resolve", function () {
|
it("should be a function", function () {
|
// Arrange
|
// Act
|
const sut = SynchronousPromise.unresolved();
|
// Assert
|
expect(sut.resolve).to.exist;
|
expect(sut.resolve).to.be.a("function");
|
});
|
|
it("should resolve the promise when invoked", function () {
|
// Arrange
|
let resolved = undefined,
|
error = undefined;
|
const sut = SynchronousPromise.unresolved().then(function (result) {
|
resolved = result;
|
}).catch(function (err) {
|
error = err;
|
}),
|
expected = {key: "value"};
|
// Act
|
debugger;
|
sut.resolve(expected);
|
// Assert
|
expect(resolved).to.equal(expected);
|
expect(error).not.to.exist;
|
});
|
|
it("should resolve all thens when invoked", () => {
|
// Arrange
|
const sut = SynchronousPromise.unresolved();
|
let captured1 = undefined,
|
captured2 = undefined;
|
const next1 = sut.then(result => captured1 = result),
|
next2 = sut.then(result => captured2 = result),
|
expected = "cake-moo";
|
expect(next1).to.exist;
|
expect(next2).to.exist;
|
// Act
|
sut.resolve(expected);
|
// Assert
|
expect(captured1).to.equal(expected);
|
expect(captured2).to.equal(expected);
|
});
|
});
|
describe("reject property", function () {
|
it("should be a function", function () {
|
// Arrange
|
// Act
|
const sut = SynchronousPromise.unresolved();
|
// Assert
|
expect(sut.reject).to.exist;
|
expect(sut.reject).to.be.a("function");
|
});
|
|
it("should reject the promise when invoked", function () {
|
// Arrange
|
let resolved = undefined,
|
error = undefined;
|
const sut = SynchronousPromise.unresolved().then(function (result) {
|
resolved = result;
|
}).catch(function (err) {
|
error = err;
|
}),
|
expected = {key: "value"};
|
// Act
|
sut.reject(expected);
|
// Assert
|
expect(error).to.equal(expected);
|
expect(resolved).not.to.exist;
|
});
|
});
|
|
describe("with timeout in ctor", () => {
|
it("should complete when the timeout does", (done) => {
|
// Arrange
|
let captured;
|
// Act
|
new SynchronousPromise(function (resolve) {
|
setTimeout(function () {
|
resolve("moo");
|
}, 0);
|
}).then(function (result) {
|
captured = result;
|
});
|
// Assert
|
setTimeout(function () {
|
expect(captured).to.equal("moo");
|
done();
|
}, 500);
|
});
|
});
|
});
|
});
|
describe(`finally`, () => {
|
it(`should call the provided function when the promise resolves`, async () => {
|
// Arrange
|
let called = false;
|
// Act
|
SynchronousPromise.resolve("foo").finally(function () {
|
called = true;
|
});
|
// Assert
|
expect(called).to.be.true;
|
});
|
|
it(`should call the provided function when the promise rejects`, async () => {
|
// Arrange
|
let called = false;
|
// Act
|
SynchronousPromise.reject("foo").finally(function () {
|
called = true;
|
});
|
// Assert
|
});
|
|
it(`should call the provided function when the promise is rejected then caught`, async () => {
|
// Arrange
|
let catchCalled = false,
|
finallyCalled = false;
|
// Act
|
SynchronousPromise.reject("error")
|
.catch(function () {
|
catchCalled = true;
|
}).finally(function () {
|
finallyCalled = true;
|
});
|
// Assert
|
expect(catchCalled).to.be.true;
|
expect(finallyCalled).to.be.true;
|
});
|
|
it(`should start a new promise chain after resolution, with non-throwing finally`, async () => {
|
// Arrange
|
let captured = null;
|
// Act
|
SynchronousPromise.resolve("first value")
|
.finally(function () {
|
return "second value";
|
}).then(function (data) {
|
captured = data;
|
});
|
// Assert
|
expect(captured).to.equal("first value");
|
});
|
|
it(`should start a new promise chain after resolution, with resolving finally`, async () => {
|
// Arrange
|
let captured = null;
|
// Act
|
SynchronousPromise.resolve("first value")
|
.finally(function () {
|
return SynchronousPromise.resolve("second value");
|
}).then(function (data) {
|
captured = data;
|
});
|
// Assert
|
expect(captured).to.equal("first value");
|
});
|
|
it(`should start a new promise chain after resolution, with throwing finally`, async () => {
|
// Arrange
|
let captured = null;
|
// Act
|
SynchronousPromise.reject("first error")
|
.finally(function () {
|
throw "finally data";
|
}).catch(function (data) {
|
captured = data;
|
});
|
// Assert
|
expect(captured).to.equal("finally data");
|
});
|
|
it(`should start a new promise chain after resolution, with rejecting finally`, async () => {
|
// Arrange
|
let captured = null;
|
// Act
|
SynchronousPromise.reject("first error")
|
.finally(function () {
|
return SynchronousPromise.reject("finally data");
|
}).catch(function (data) {
|
captured = data;
|
});
|
// Assert
|
expect(captured).to.equal("finally data");
|
});
|
|
it(`should start a new promise chain after rejection, with non-throwing finally`, async () => {
|
// Arrange
|
let called = false;
|
// Act
|
SynchronousPromise.reject("le error")
|
.finally(function () {
|
}).then(function () {
|
called = true;
|
});
|
// Assert
|
expect(called).to.be.true;
|
});
|
|
it(`should start a new promise chain after rejection, with resolving finally`, async () => {
|
// Arrange
|
let captured = null;
|
let capturedErr = null;
|
// Act
|
SynchronousPromise.reject("le error")
|
.finally(function () {
|
return SynchronousPromise.resolve("le data");
|
}).then(function (data) {
|
captured = data;
|
}).catch(function(err) {
|
capturedErr = err;
|
});
|
// Assert
|
expect(captured).to.be.null;
|
expect(capturedErr).to.equal("le error");
|
});
|
|
it(`should start a new promise chain after rejection, with throwing finally`, async () => {
|
// Arrange
|
let finallyError = null;
|
// Act
|
SynchronousPromise.reject("another error")
|
.finally(function () {
|
throw "moo cakes";
|
}).catch(function (err) {
|
finallyError = err;
|
});
|
// Assert
|
expect(finallyError).to.equal("moo cakes");
|
});
|
|
it(`should start a new promise chain after rejection, with rejecting finally`, async () => {
|
// Arrange
|
let finallyError = null;
|
// Act
|
SynchronousPromise.reject("another error")
|
.finally(function () {
|
return SynchronousPromise.reject("moo cakes");
|
}).catch(function (err) {
|
finallyError = err;
|
});
|
// Assert
|
expect(finallyError).to.equal("moo cakes");
|
});
|
|
describe(`issues`, () => {
|
it(`should be called after one then from resolved()`, async () => {
|
// Arrange
|
const events = [];
|
// Act
|
SynchronousPromise.resolve("initial")
|
.then(result => {
|
events.push(result);
|
events.push("then");
|
}).finally(() => {
|
events.push("finally");
|
});
|
// Assert
|
expect(events).to.eql(
|
["initial", "then", "finally"]
|
);
|
});
|
|
it(`should be called after two thens from resolved()`, async () => {
|
// Arrange
|
const events = [];
|
// Act
|
SynchronousPromise.resolve("initial")
|
.then(result => {
|
events.push(result);
|
events.push("then1");
|
return "then1";
|
}).then(result => {
|
events.push(`then2 received: ${result}`);
|
events.push("then2");
|
console.log(events);
|
}).finally(() => {
|
events.push("finally");
|
});
|
// Assert
|
expect(events).to.eql(
|
["initial", "then1", "then2 received: then1", "then2", "finally"]
|
);
|
});
|
|
it(`should not be called from an unresolved promise`, async () => {
|
// Arrange
|
const events = [];
|
// Act
|
SynchronousPromise.unresolved()
|
.then(result => {
|
debugger;
|
events.push(`result: ${result}`)
|
})
|
.catch(() => events.push("catch"))
|
.finally(() => events.push("finally"));
|
// Assert
|
expect(events).to.be.empty;
|
});
|
|
it(`should not be run if chain is paused`, async () => {
|
// Arrange
|
const events = [];
|
|
const promise = SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.pause()
|
.then(() => {
|
events.push("resumed")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
expect(events).to.eql(["result: init"]);
|
// Act
|
promise.resume();
|
// Assert
|
expect(events).to.eql(["result: init", "resumed", "finally"]);
|
});
|
|
it(`should pass the result onto the next .then`, async () => {
|
// Arrange
|
// Act
|
const result = await SynchronousPromise.resolve("expected")
|
.finally(r => r);
|
// Assert
|
expect(result)
|
.to.equal("expected");
|
});
|
|
it(`should pass result onto next .then when no finally handler`, async () => {
|
// Arrange
|
// Act
|
const result = await SynchronousPromise.resolve("expected")
|
.finally();
|
// Assert
|
expect(result).to.equal("expected");
|
});
|
|
it(`should pass last result onto next .then when finally has an empty handler fn`, async () => {
|
// Arrange
|
// Act
|
const result = await SynchronousPromise.resolve("expected")
|
.finally(() => {});
|
// Assert
|
expect(result)
|
.to.equal("expected");
|
});
|
|
describe(`imported specs from blalasaadri`, () => {
|
// these relate to https://github.com/fluffynuts/synchronous-promise/issues/15
|
// reported by https://github.com/blalasaadri
|
describe("SynchronousPromise", () => {
|
describe("new SynchronousPromise", () => {
|
it("calls .then() after being resolved", () => {
|
const events = [];
|
|
new SynchronousPromise((resolve) => {
|
events.push("init");
|
resolve("resolve")
|
}).then(result => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
});
|
|
expect(events)
|
.to.eql(["init", "result: resolve", "then"])
|
});
|
|
it("calls .catch() but not previous .then()s after being rejected", () => {
|
const events = [];
|
|
new SynchronousPromise((resolve, reject) => {
|
events.push("init");
|
reject("reject")
|
}).then(result => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.catch(error => {
|
events.push(`error: ${error}`)
|
});
|
|
expect(events)
|
.to.eql(["init", "error: reject"])
|
});
|
|
it("calls .finally() after .then()", () => {
|
const events = [];
|
|
new SynchronousPromise((resolve) => {
|
resolve("init")
|
}).then(result => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.to.eql(["result: init", "then", "finally"])
|
});
|
|
it("calls .finally() after .catch()", () => {
|
const events = [];
|
|
new SynchronousPromise((resolve, reject) => {
|
reject("init")
|
}).then(result => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.catch(error => {
|
events.push(`error: ${error}`)
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.to.eql(["error: init", "finally"])
|
})
|
});
|
|
describe("SynchronousPromise.unresolved", () => {
|
describe("calls .then() only after being resolved", () => {
|
it("calls nothing before promise.resolve is called", () => {
|
const events = [];
|
|
SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
});
|
|
expect(events).to.eql([])
|
});
|
|
it("calls .then() once promise.resolve is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
});
|
promise.resolve("resolve");
|
|
expect(events).to.eql(["result: resolve", "then"])
|
})
|
});
|
|
it("calls .catch() but not previous .then()s after being rejected", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.catch(error => {
|
events.push(`error: ${error}`)
|
});
|
promise.reject("reject");
|
|
expect(events)
|
.to.eql(["error: reject"])
|
});
|
|
describe("calls .finally() after .then()", () => {
|
it("calls nothing before promise.resolve is called", () => {
|
const events = [];
|
|
SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.not.to.contain("finally");
|
expect(events)
|
.to.eql([])
|
});
|
|
it("calls .then() and .finally() once promise.resolve is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.then(() => {
|
events.push("then")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
promise.resolve("resolve");
|
|
expect(events)
|
.not.to.eql(["finally", "result: undefined", "then"]);
|
expect(events)
|
.to.eql(["result: resolve", "then", "finally"])
|
})
|
});
|
|
describe("calls .finally() after .catch()", () => {
|
it("calls nothing before promise.reject is called", () => {
|
const events = [];
|
|
SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.catch(() => {
|
events.push("catch")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.not.to.contain("finally");
|
expect(events)
|
.to.eql([])
|
});
|
|
it("calls .catch() and .finally() once promise.reject is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.unresolved()
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.catch((error) => {
|
events.push(`error: ${error}`)
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
promise.reject("reject");
|
|
expect(events)
|
.not.to.eql(["finally", "result: undefined"]);
|
expect(events)
|
.to.eql(["error: reject", "finally"])
|
})
|
})
|
});
|
|
describe("SynchronousPromise.resolve(...).pause", () => {
|
describe("calls .then() only after being resolved", () => {
|
it("calls nothing after the initial initialization before promise.resume is called", () => {
|
const events = [];
|
|
SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.pause()
|
.then(() => {
|
events.push("resumed")
|
});
|
|
expect(events)
|
.to.eql(["result: init"])
|
});
|
|
it("calls .then() after the inital initialization after promise.resume is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.pause()
|
.then(() => {
|
events.push("resumed")
|
});
|
promise.resume();
|
|
expect(events)
|
.to.eql(["result: init", "resumed"])
|
})
|
});
|
|
describe("calls .catch() only after being resolved", () => {
|
it("calls nothing after the inital initialization before promise.resume is called", () => {
|
const events = [];
|
|
SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`);
|
throw Error("resumed")
|
})
|
.pause()
|
.catch(({message}) => {
|
events.push(`catch: ${message}`)
|
});
|
|
expect(events)
|
.to.eql(["result: init"])
|
});
|
|
it("calls .catch() after the inital initialization after promise.resume is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`);
|
throw Error("resumed")
|
})
|
.pause()
|
.catch(({message}) => {
|
events.push(`catch: ${message}`)
|
});
|
promise.resume();
|
|
expect(events)
|
.to.eql(["result: init", "catch: resumed"])
|
})
|
});
|
|
describe("calls .finally() after .then()", () => {
|
it("calls nothing before promise.resume is called", () => {
|
const events = [];
|
|
SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.pause()
|
.then(() => {
|
events.push("resumed")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.not.to.contain("finally");
|
expect(events)
|
.to.eql(["result: init"])
|
});
|
|
it("calls .then() and .finally() once promise.resume is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`)
|
})
|
.pause()
|
.then(() => {
|
events.push("resumed")
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
promise.resume();
|
|
expect(events)
|
.not.to.eql(["result: init", "finally", "resumed"]);
|
expect(events)
|
.to.eql(["result: init", "resumed", "finally"])
|
})
|
});
|
|
describe("calls .finally() after .catch()", () => {
|
it("calls nothing before promise.resume is called", () => {
|
const events = [];
|
|
SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`);
|
throw Error("resumed")
|
})
|
.pause()
|
.catch(({message}) => {
|
events.push(`catch: ${message}`)
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
|
expect(events)
|
.not.to.contain("finally");
|
expect(events)
|
.to.eql(["result: init"])
|
});
|
|
it("calls .catch() and .finally() once promise.resume is called", () => {
|
const events = [];
|
|
const promise = SynchronousPromise.resolve("init")
|
.then((result) => {
|
events.push(`result: ${result}`);
|
throw Error("resumed")
|
})
|
.pause()
|
.catch(({message}) => {
|
events.push(`catch: ${message}`)
|
})
|
.finally(() => {
|
events.push("finally")
|
});
|
promise.resume();
|
|
expect(events)
|
.not.to.eql(["result: init", "finally", "catch: resumed"]);
|
expect(events)
|
.to.eql(["result: init", "catch: resumed", "finally"])
|
})
|
})
|
})
|
})
|
});
|
|
});
|
});
|
});
|