diff --git a/src/DeferredPromise.ts b/src/DeferredPromise.ts index 554c24a..213c888 100644 --- a/src/DeferredPromise.ts +++ b/src/DeferredPromise.ts @@ -1,8 +1,10 @@ export type DeferredPromiseState = "pending" | "resolved" | "rejected"; export type ResolveFunction = ( data: Data -) => Result; -export type RejectFunction = (reason?: unknown) => Result; +) => Result | PromiseLike; +export type RejectFunction = ( + reason?: unknown +) => Result | PromiseLike; /** * Represents the completion of an asynchronous operation. @@ -16,7 +18,7 @@ export type RejectFunction = (reason?: unknown) => Result; * const portReady = new DeferredPromise() * portReady.reject(new Error('Port is already in use')) */ -export class DeferredPromise { +export class DeferredPromise { public resolve: ResolveFunction; public reject: RejectFunction; public state: DeferredPromiseState; @@ -60,12 +62,15 @@ export class DeferredPromise { /** * Attaches callbacks for the resolution and/or rejection of the Promise. */ - public then( - onresolved?: ResolveFunction, - onrejected?: RejectFunction - ) { - this.promise = this.promise.then(onresolved, onrejected); - return this; + public then( + onresolved?: ResolveFunction, + onrejected?: RejectFunction + ): DeferredPromise { + this.promise = this.promise.then( + onresolved, + onrejected + ); + return this as DeferredPromise; } /** @@ -73,7 +78,7 @@ export class DeferredPromise { */ public catch( onrejected?: RejectFunction - ): this { + ): DeferredPromise { this.promise = this.promise.catch(onrejected); return this; } @@ -83,7 +88,7 @@ export class DeferredPromise { * the Promise is settled (fulfilled or rejected). The resolved * value cannot be modified from the callback. */ - public finally(onfinally?: () => void): this { + public finally(onfinally?: () => void): DeferredPromise { this.promise = this.promise.finally(onfinally); return this; } diff --git a/test/DeferredPromise.test.ts b/test/DeferredPromise.test.ts index a1e9aa2..bf1246e 100644 --- a/test/DeferredPromise.test.ts +++ b/test/DeferredPromise.test.ts @@ -1,36 +1,82 @@ import { DeferredPromise } from "../src"; -it('can be listened to with ".then()"', (done) => { - expect.assertions(1); +describe("Promise-compliance", () => { + it('can be listened to with ".then()"', (done) => { + expect.assertions(1); - const promise = new DeferredPromise(); + const promise = new DeferredPromise(); + + promise.then((data) => { + expect(data).toBe(123); + done(); + }); + + promise.resolve(123); + }); + + it('can be listened to with ".catch()"', (done) => { + expect.assertions(1); + + const promise = new DeferredPromise(); + promise.catch((reason) => { + expect(reason).toBe("error"); + done(); + }); - promise.then((data) => { + promise.reject("error"); + }); + + it("can be awaited with async/await", async () => { + const promise = new DeferredPromise(); + promise.resolve(123); + + const data = await promise; expect(data).toBe(123); - done(); }); - promise.resolve(123); -}); + it('allows data transformation in the ".then()" chain', async () => { + const promise = new DeferredPromise(); -it('can be listened to with ".catch()"', (done) => { - expect.assertions(1); + promise.then((value) => value * 2).then((value) => value + 10); + promise.resolve(5); - const promise = new DeferredPromise(); - promise.catch((reason) => { - expect(reason).toBe("error"); - done(); + const number = await promise; + + expect(number).toBe(20); }); - promise.reject("error"); -}); + it('allows ".catch().then()" chaining', async () => { + const promise = new DeferredPromise(); + + promise + .catch((value) => { + if (typeof value === "number") { + return value; + } + }) + .then((value) => value + 10); + + promise.reject(5); + const number = await promise; + + expect(number).toBe(15); + }); -it("can be awaited", async () => { - const promise = new DeferredPromise(); - promise.resolve(123); + it('does not alter resolved data with ".finally()"', async () => { + const promise = new DeferredPromise(); + + const finallyCallback = jest.fn(() => "unexpected"); + const wrapper = (): Promise => { + return promise.finally(finallyCallback); + }; + + promise.resolve(123); + const result = await wrapper(); - const data = await promise; - expect(data).toBe(123); + expect(result).toBe(123); + expect(finallyCallback).toHaveBeenCalledTimes(1); + expect(finallyCallback).toHaveBeenCalledWith(); + }); }); describe("resolve()", () => {