UNPKG

@stackoverfloweth/vue-compositions

Version:

A collection of reusable vue compositions.

455 lines 17.9 kB
import { mount } from '@vue/test-utils'; import { test, describe, it, expect, afterEach, vi } from 'vitest'; import { computed, h, reactive, ref } from 'vue'; import { useSubscription } from '../useSubscription'; import { SubscriptionManager } from '../useSubscription/models/manager'; import { timeout, uniqueSubscribe } from '../utilities/tests'; function numberEqualsOne(number) { return number === 1; } function numberEqualsOnePromise(number, delay) { return new Promise(resolve => { setTimeout(() => resolve(numberEqualsOne(number)), delay); }); } afterEach(() => { vi.useRealTimers(); }); describe('subscribe', () => { it('returns the correct response', async () => { const subscription = await uniqueSubscribe(numberEqualsOne, [1]); expect(subscription.response).toBe(true); }); it('returns the correct response when awaiting the promise', async () => { vi.useFakeTimers(); const promise = uniqueSubscribe(numberEqualsOnePromise, [1, 1000]).promise(); vi.runAllTimers(); const subscription = await promise; expect(subscription.response).toBe(true); }); it('returns response undefined until action promise resolves', () => { const subscription = uniqueSubscribe(numberEqualsOnePromise, [1, 10]); expect(subscription.response).toBe(undefined); }); it('returns the correct response when action promise resolves', async () => { vi.useFakeTimers(); const subscription = uniqueSubscribe(numberEqualsOnePromise, [1, 10]); vi.runAllTimers(); vi.useRealTimers(); await timeout(); expect(subscription.response).toBe(true); }); it('sets loading to true', () => { const subscription = uniqueSubscribe(numberEqualsOne, [1]); expect(subscription.loading).toBe(true); }); it('sets loading to false', async () => { const subscription = await uniqueSubscribe(numberEqualsOne, [1]); expect(subscription.loading).toBe(false); }); it('defaults errored to false', () => { const action = vi.fn(); const subscription = uniqueSubscribe(action, []); expect(subscription.errored).toBe(false); }); it('sets errored to true', () => { function errors() { throw 'look! an error'; } const subscription = uniqueSubscribe(errors, []); expect(subscription.errored).toBe(true); }); it('sets error if an error is thrown', () => { const error = 'look! an error'; function errors() { throw error; } const subscription = uniqueSubscribe(errors, []); expect(subscription.error).toBe(error); }); it('rejects promise', async () => { const error = 'error'; function action() { throw error; } let caught; try { await uniqueSubscribe(action).promise(); } catch (err) { caught = err; } expect(caught).toBe(error); }); it('executes the action once when two subscriptions are created', () => { const manager = new SubscriptionManager(); const action = vi.fn(); useSubscription(action, [], { manager }); useSubscription(action, [], { manager }); expect(action).toBeCalledTimes(1); }); it('calculates the poll interval correctly', () => { vi.useFakeTimers(); const manager = new SubscriptionManager(); const action = vi.fn(); const initialExecutions = 1; const additionalExecutions = 3; const minInterval = 10; const maxInterval = 20; useSubscription(action, [], { interval: minInterval, manager }); useSubscription(action, [], { interval: maxInterval, manager }); vi.advanceTimersByTime(additionalExecutions * minInterval); expect(action).toBeCalledTimes(initialExecutions + additionalExecutions); }); it('calculates the poll interval correctly when a subscription is unsubscribed', () => { vi.useFakeTimers(); const manager = new SubscriptionManager(); const action = vi.fn(); const initialExecutions = 1; const additionalExecutions = 3; const minInterval = 10; const maxInterval = 20; const subscription1 = useSubscription(action, [], { interval: minInterval, manager }); useSubscription(action, [], { interval: maxInterval, manager }); subscription1.unsubscribe(); vi.advanceTimersByTime(additionalExecutions * maxInterval); expect(action).toBeCalledTimes(initialExecutions + additionalExecutions); }); it('stops polling when all subscriptions with interval unsubscribe', () => { vi.useFakeTimers(); const manager = new SubscriptionManager(); const action = vi.fn(); const minInterval = 10; const maxInterval = 20; const subscription1 = useSubscription(action, [], { interval: minInterval, manager }); const subscription2 = useSubscription(action, [], { interval: maxInterval, manager }); useSubscription(action, [], { manager }); subscription1.unsubscribe(); subscription2.unsubscribe(); vi.advanceTimersByTime(maxInterval); expect(action).toBeCalledTimes(1); }); it('executes the correct number of times when interval is set', () => { vi.useFakeTimers(); const initialExecutions = 1; const additionalExecutions = Math.floor(Math.random() * 10) + 1; const action = vi.fn(); uniqueSubscribe(action, [], { interval: 50 }); for (let i = 0; i < additionalExecutions; i++) { vi.runOnlyPendingTimers(); } expect(action).toBeCalledTimes(initialExecutions + additionalExecutions); }); it('stops executing when unsubscribed', () => { vi.useFakeTimers(); const action = vi.fn(); const subscription = uniqueSubscribe(action, [], { interval: 50 }); subscription.unsubscribe(); vi.runOnlyPendingTimers(); expect(action).toBeCalledTimes(1); }); describe('executes action again when args change', () => { it('when using reactive args', async () => { const action = vi.fn(); const args = reactive([0]); uniqueSubscribe(action, args); args[0] = 1; await timeout(); expect(action).toBeCalledTimes(2); }); it('when using ref args', async () => { const action = vi.fn(); const args = ref([0]); uniqueSubscribe(action, args); args.value = [1]; await timeout(); expect(action).toBeCalledTimes(2); }); it('when using args containing a ref value', async () => { const action = vi.fn(); const number = ref(0); const args = [number]; uniqueSubscribe(action, args); number.value = 1; await timeout(); expect(action).toBeCalledTimes(2); }); it('when using args containing a ref value and a non reactive value', async () => { const action = vi.fn(); const number = ref(0); const args = [number, 0]; uniqueSubscribe(action, args); number.value = 1; await timeout(); expect(action).toBeCalledTimes(2); }); it('when using args containing a reactive value', async () => { const action = vi.fn(); const argument = reactive({ number: 0 }); const args = [argument]; uniqueSubscribe(action, args); argument.number = 1; await timeout(); expect(action).toBeCalledTimes(2); }); it('when using args containing a reactive value and a non reactive value', async () => { const action = vi.fn(); const argument = reactive({ number: 0 }); const args = [argument, 0]; uniqueSubscribe(action, args); argument.number = 1; await timeout(); expect(action).toBeCalledTimes(2); }); }); it('updates response when args change', async () => { const number = ref(0); const subscription = uniqueSubscribe(numberEqualsOne, [number]); number.value = 1; await timeout(); expect(subscription.response).toBe(true); }); test.each([ undefined, {}, { interval: 3000 }, { lifecycle: 'component' }, ])('calls unsubscribe when component is unmounted with default lifecycle', (options) => { const action = vi.fn(); let subscription; const wrapper = mount({ setup() { subscription = uniqueSubscribe(action, [], options); return () => h('p'); }, }); const spy = vi.spyOn(subscription, 'unsubscribe'); wrapper.unmount(); expect(spy).toBeCalledTimes(1); }); it('does not call unsubscribe when component is unmounted with app lifecycle', () => { const action = vi.fn(); let subscription; const wrapper = mount({ setup() { subscription = uniqueSubscribe(action, [], { lifecycle: 'app', }); return () => h('p'); }, }); const spy = vi.spyOn(subscription, 'unsubscribe'); wrapper.unmount(); expect(spy).not.toBeCalled(); }); it('does not update subscription when unsubscribed', async () => { const manager = new SubscriptionManager(); let int = 0; function action() { return ++int; } const subscription1 = useSubscription(action, [], { manager }); const subscription2 = useSubscription(action, [], { manager }); await timeout(); subscription1.unsubscribe(); await subscription2.refresh(); expect(subscription1.response).toBe(1); expect(subscription2.response).toBe(2); }); it('does not update subscription when args change if unsubscribed', async () => { const number = ref(0); const subscription = uniqueSubscribe(numberEqualsOne, [number]); await timeout(); subscription.unsubscribe(); number.value = 1; await timeout(); expect(subscription.response).toBe(false); }); it('when using computed args', async () => { const action = vi.fn(); const valueRef = ref(0); const valueComputed = computed(() => valueRef.value); uniqueSubscribe(action, [valueComputed]); valueRef.value = 1; await timeout(); expect(action).toBeCalledTimes(2); }); it('fails when using nested computed args', () => { const action = vi.fn(); const valueRef = ref(0); const valueComputed = computed(() => valueRef); const valueComputedComputed = computed(() => valueComputed); expect(() => { uniqueSubscribe(action, [valueComputedComputed]); }).toThrowError(); }); it('it does not return undefined when a subscription changes', async () => { vi.useFakeTimers(); function action(value) { return new Promise((resolve) => setTimeout(() => resolve(value), 10)); } const originalValue = 0; const valueArg = ref(originalValue); const subscription = useSubscription(action, [valueArg]); vi.runAllTimers(); vi.useRealTimers(); valueArg.value = 1; await timeout(); expect(subscription.response).toBe(originalValue); await timeout(15); expect(subscription.response).toBe(1); }); it('it does retain previous subscription response when arguments change', async () => { vi.useFakeTimers(); function action(value) { return new Promise((resolve) => setTimeout(() => resolve(value), 10)); } const valueArg = ref(0); const subscription = useSubscription(action, [valueArg]); vi.runAllTimers(); vi.useRealTimers(); while (valueArg.value < 2) { valueArg.value++; // eslint-disable-next-line no-await-in-loop await timeout(); expect(subscription.response).toBe(valueArg.value - 1); // eslint-disable-next-line no-await-in-loop await timeout(15); expect(subscription.response).toBe(valueArg.value); } }); it('correctly sets response on additional subscriptions', async () => { function action() { return 0; } const manager = new SubscriptionManager(); useSubscription(action, [], { manager }); await timeout(); const subscription = useSubscription(action, [], { manager }); expect(subscription.response).toBe(0); }); it('correctly sets executed', async () => { const action = vi.fn(); const subscription = uniqueSubscribe(action); await timeout(); expect(subscription.executed).toBe(true); }); it('correctly sets executed on additional subscriptions', async () => { const action = vi.fn(); const manager = new SubscriptionManager(); useSubscription(action, [], { manager }); await timeout(); const subscription = useSubscription(action, [], { manager }); expect(subscription.executed).toBe(true); }); it('stops executing when action errors', () => { const action = vi.fn(() => { throw 'error'; }); vi.useFakeTimers(); uniqueSubscribe(action, [], { interval: 10000 }); vi.advanceTimersByTime(10000 * 2); expect(action).toBeCalledTimes(1); }); it('is retains subscription when reactive options change', () => { vi.useFakeTimers(); const action = vi.fn(); const interval = ref(1000); const options = computed(() => { return { interval: interval.value, }; }); uniqueSubscribe(action, [], options); expect(action).toBeCalledTimes(1); interval.value = Infinity; expect(action).toBeCalledTimes(1); interval.value = 1000; vi.advanceTimersByTime(1000); expect(action).toBeCalledTimes(2); }); it('unsubscribes nested subscriptions automatically', () => { const manager = new SubscriptionManager(); const action = vi.fn(); let childSubscription; const subscription = useSubscription(() => { childSubscription = useSubscription(action); }, [], { manager }); const spy = vi.spyOn(childSubscription, 'unsubscribe'); subscription.unsubscribe(); expect(spy).toBeCalledTimes(1); }); it('unsubscribes nested subscriptions when changing args', async () => { const number = ref(0); const manager = new SubscriptionManager(); const action = vi.fn(); let childSubscription; useSubscription((number) => { childSubscription = useSubscription(action, [], { manager }); return number; }, [number], { manager }); const spy = vi.spyOn(childSubscription, 'unsubscribe'); number.value++; await timeout(); expect(spy).toBeCalledTimes(1); }); it('it refreshes nested subscriptions when calling refresh', async () => { const action = vi.fn(); const manager = new SubscriptionManager(); const subscription = useSubscription(() => { useSubscription(action, [], { manager }); }, [], { manager }); await timeout(); subscription.refresh(); await timeout(); expect(action).toBeCalledTimes(2); }); it('calls the onError callback when the action errors', async () => { const error = new Error(); const onError = vi.fn(); const action = () => { throw error; }; uniqueSubscribe(action, [], { onError }); await timeout(); expect(onError).toBeCalledTimes(1); expect(onError).toBeCalledWith(error); }); it('allows refresh to be called when the a subscription is created with .promise()', async () => { const action = vi.fn(); const subscription = await uniqueSubscribe(action).promise(); subscription.refresh(); expect(action).toBeCalledTimes(2); }); it('response is reactive when using .promise()', async () => { let value = 0; const action = () => ++value; const subscription = await uniqueSubscribe(action).promise(); expect(subscription.response).toBe(1); subscription.refresh(); await timeout(); expect(subscription.response).toBe(2); }); it('doest not execute if paused', async () => { vi.useFakeTimers(); const interval = 1000; const manager = new SubscriptionManager(); const action = vi.fn(); const subscription = useSubscription(action, [], { manager, interval }); expect(action).toBeCalledTimes(1); vi.advanceTimersByTime(interval); expect(action).toBeCalledTimes(2); manager.pause(); expect(subscription.paused).toBe(true); vi.advanceTimersByTime(interval); expect(subscription.late).toBe(true); expect(action).toBeCalledTimes(2); manager.resume(); expect(action).toBeCalledTimes(3); expect(subscription.paused).toBe(false); vi.useRealTimers(); await timeout(); expect(subscription.late).toBe(false); }); }); //# sourceMappingURL=useSubscription.spec.js.map