From 7d8445805c637ae46f2b370df9cadb58ad7ea195 Mon Sep 17 00:00:00 2001 From: Abrahem Alhofe Date: Mon, 13 Jun 2022 14:55:41 +0200 Subject: [PATCH 1/2] feat: fire component's events Promised fires 'resolved' event when the promise is resolved, fires 'rejected' event when the promise is rejected and fires 'pending' event when pending delay time is elapsed. close #311 --- src/Promised.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Promised.ts b/src/Promised.ts index 4fae618..b4ea443 100644 --- a/src/Promised.ts +++ b/src/Promised.ts @@ -1,3 +1,4 @@ +import { watch } from 'vue' import { defineComponent, isVue3, @@ -24,13 +25,23 @@ export const PromisedImpl = /*#__PURE__*/ defineComponent({ default: 200, }, }, - - setup(props, { slots }) { + emits: ['resolved', 'rejected', 'pending'], + setup(props, { slots, emit, attrs }) { const propsAsRefs = toRefs(props) const promiseState = reactive( usePromise(propsAsRefs.promise, propsAsRefs.pendingDelay) ) + watch(promiseState, (promiseState) => + promiseState.isRejected + ? emit('rejected', promiseState.error) + : !promiseState.isPending + ? emit('resolved', promiseState.data) + : promiseState.isDelayElapsed + ? emit('pending', promiseState.data) + : null + ) + return () => { if ('combined' in slots) { return slots.combined!(promiseState) From 7b06d439bd13029b9764b14342ebb0ac3fe4d92d Mon Sep 17 00:00:00 2001 From: Abrahem Alhofe Date: Mon, 13 Jun 2022 15:02:40 +0200 Subject: [PATCH 2/2] test: fire component's events --- __tests__/Promised.spec.ts | 81 +++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/__tests__/Promised.spec.ts b/__tests__/Promised.spec.ts index 077f244..8e24c09 100644 --- a/__tests__/Promised.spec.ts +++ b/__tests__/Promised.spec.ts @@ -10,10 +10,15 @@ const timeout = setTimeout const tick = () => new Promise((resolve) => timeout(resolve, 0)) describe('Promised', () => { - function factory(propsData: any = undefined, slots: any = undefined) { + function factory( + propsData: any = undefined, + slots: any = undefined, + listeners: any = undefined + ) { const [promise, resolve, reject] = fakePromise() const wrapper = mount(Promised, { propsData: { promise, pendingDelay: 0, ...propsData }, + attrs: listeners, slots: { pending: (oldData) => h('span', 'pending: ' + oldData), default: (data) => h('span', {}, data), @@ -190,6 +195,80 @@ describe('Promised', () => { }) }) + describe("component's events", () => { + beforeEach(() => { + jest.useFakeTimers('modern') + jest.spyOn(global, 'setTimeout') + jest.spyOn(global, 'clearTimeout') + }) + + afterEach(() => { + // @ts-expect-error: mocked + setTimeout.mockClear() + // @ts-expect-error: mocked + clearTimeout.mockClear() + }) + + afterAll(() => { + jest.useRealTimers() + }) + + it("fires 'resolved' event", async () => { + const onResolved = jest.fn() + + const { resolve } = factory(undefined, undefined, { onResolved }) + + resolve('fire') + + await tick() + + expect(onResolved).toHaveBeenCalledWith('fire') + }) + + it("fires 'rejected' event", async () => { + const onResolved = jest.fn() + const onRejected = jest.fn() + + const { reject } = factory(undefined, undefined, { + onResolved, + onRejected, + }) + + reject(new Error('fire')) + + await tick() + + expect(onRejected).toHaveBeenCalledWith(new Error('fire')) + + expect(onResolved).not.toHaveBeenCalled() + }) + + it("fires 'pending' event on pending delay time elapsed", async () => { + const onResolved = jest.fn() + const onRejected = jest.fn() + const onPending = jest.fn() + + factory({ pendingDelay: 100 }, undefined, { + onPending, + onResolved, + onRejected, + }) + + expect(onResolved).not.toHaveBeenCalled() + expect(onRejected).not.toHaveBeenCalled() + expect(onPending).not.toHaveBeenCalled() + + jest.runAllTimers() + + await tick() + + expect(onPending).toHaveBeenCalledWith(undefined) + + expect(onResolved).not.toHaveBeenCalled() + expect(onRejected).not.toHaveBeenCalled() + }) + }) + mockWarn() it('warns on missing slot', async () => {