/* * @since 2020-11-04 12:43:20 * @author acrazing */ import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks'; import React, { FC, memo } from 'react'; import { addGreet } from './action.spec'; import { countBox, increment, testBox } from './box.spec'; import { Provider } from './context'; import { MapSelector, useDispatch, useSelector, useStore } from './hooks'; import { selector } from './selector'; import { selectCount, selectDoubleCount, selectMultipleCount } from './selector.spec'; import { createStore, Select, Selectable, Snapshot, Store } from './store'; import fn = jest.fn; export const TestCount = memo(() => { const [count, count2] = useSelector(selectCount, selectDoubleCount()); return
{count + '-' + count2}
; }); describe('useStore & useDispatch', () => { const store = createStore(); const wrapper: FC = (props) => {props.children}; it('should use store & dispatch', async () => { const renderStore = renderHook(() => useStore(), { wrapper }); expect(renderStore.result.current).toBe(store); const renderDispatch = renderHook(() => useDispatch(), { wrapper }); expect(renderDispatch.result.current).toBe(store.dispatch); const renderEmpty = renderHook(() => useStore()); expect(renderEmpty.result.error).toBeInstanceOf(Error); }); }); function renderUseSelector( fn: (props: P) => Rs, preloaded?: Snapshot, initialProps?: P, ): RenderHookResult> & Store { const store = createStore(preloaded); const hook = renderHook((props: P) => useSelector(...fn(props)), { wrapper: (props) => {props.children}, initialProps, }); return Object.assign(hook, store); } describe('useSelector', () => { it('should select state', () => { const { result } = renderUseSelector( () => [countBox, selectCount, selectDoubleCount, selectMultipleCount(3)] as const, { count: 1, test: { greets: ['PRELOAD'], count: 1 } }, ); expect(result.current).toEqual([1, 1, 2, 3]); }); it('should update', async () => { const { result, waitForNextUpdate, dispatch, rerender } = renderUseSelector( (props: { multiply: number }) => [selectDoubleCount, selectMultipleCount(props.multiply)], { count: 1, test: { greets: ['PRELOAD'], count: 1 } }, { multiply: 3 }, ); expect(result.current).toEqual([2, 3]); dispatch(addGreet('hello')); await waitForNextUpdate(); expect(result.current).toEqual([4, 6]); rerender({ multiply: 4 }); expect(result.current).toEqual([4, 8]); }); it('should update - box', async () => { const { result, dispatch } = renderUseSelector(() => [countBox] as const); expect(result.current).toEqual([0]); act(() => dispatch(increment())); expect(result.current).toEqual([1]); }); it('should respect deps', async () => { const defaultFn = fn((select: Select, times: number) => select(testBox).count * times); const strictFn = fn((select: Select, times: number) => select(testBox).count * times); const defaultSelector = selector(defaultFn); const strictSelector = selector(strictFn, (select, times) => [select(testBox).count, times]); const inlineFn = fn((select: Select) => select(testBox).count); const expectCalled = (isDefault: boolean, isStrict: boolean, isInline: boolean) => { expect(defaultFn).toBeCalledTimes(isDefault ? 1 : 0); defaultFn.mockClear(); expect(strictFn).toBeCalledTimes(isStrict ? 1 : 0); strictFn.mockClear(); expect(inlineFn).toBeCalledTimes(isInline ? 1 : 0); inlineFn.mockClear(); }; const { result, dispatch, rerender, waitForNextUpdate } = renderUseSelector( (props: { multiply: number }) => [ defaultSelector(props.multiply), strictSelector(props.multiply), inlineFn, countBox, ] as const, { count: 1, test: { count: 1, greets: ['PRELOAD'] } }, { multiply: 1 }, ); expectCalled(true, true, true); expect(result.current).toEqual([1, 1, 1, 1]); dispatch(addGreet('HELLO')); await waitForNextUpdate(); expectCalled(true, true, true); expect(result.current).toEqual([2, 2, 2, 1]); rerender({ multiply: 2 }); expectCalled(true, true, true); expect(result.current).toEqual([4, 4, 2, 1]); act(() => dispatch(increment())); expectCalled(false, false, false); expect(result.current).toEqual([4, 4, 2, 2]); }); });