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 () => { 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)