From 88577c6e804daa02b96e678a6265ed8e0c5c798f Mon Sep 17 00:00:00 2001 From: Siarhei Karol Date: Fri, 29 Nov 2024 19:32:40 +0300 Subject: [PATCH] tests refactoring --- src/test/__mocks__/store/index.ts | 7 +++ src/test/__mocks__/zustand.ts | 52 +++++++++++++++++++ .../common/hooks/useContainerEvents.test.ts | 8 +-- .../common/hooks/useRecordStatus.test.ts | 8 +-- .../components/CommonStatus.test.tsx | 8 +-- .../MarcPreviewComplexLookup.test.tsx | 8 +-- .../__tests__/components/SaveRecord.test.tsx | 8 +-- .../components/ViewMarcModal.test.tsx | 8 +-- 8 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 src/test/__mocks__/store/index.ts create mode 100644 src/test/__mocks__/zustand.ts diff --git a/src/test/__mocks__/store/index.ts b/src/test/__mocks__/store/index.ts new file mode 100644 index 00000000..57d1a916 --- /dev/null +++ b/src/test/__mocks__/store/index.ts @@ -0,0 +1,7 @@ +import { StoreApi } from 'zustand'; + +export const setInitialState = (store: StoreApi, initialState: any) => + store.setState({ + ...store.getInitialState(), + ...initialState, + }); diff --git a/src/test/__mocks__/zustand.ts b/src/test/__mocks__/zustand.ts new file mode 100644 index 00000000..64727d25 --- /dev/null +++ b/src/test/__mocks__/zustand.ts @@ -0,0 +1,52 @@ +import { act } from '@testing-library/react'; +import type * as ZustandExportedTypes from 'zustand'; +export * from 'zustand'; + +const { create: actualCreate, createStore: actualCreateStore } = + jest.requireActual('zustand'); + +// a variable to hold reset functions for all stores declared in the app +export const storeResetFns = new Set<() => void>(); + +const createUncurried = (stateCreator: ZustandExportedTypes.StateCreator) => { + const store = actualCreate(stateCreator); + const initialState = store.getInitialState(); + storeResetFns.add(() => { + store.setState(initialState, true); + }); + return store; +}; + +// when creating a store, we get its initial state, create a reset function and add it in the set +export const create = ((stateCreator: ZustandExportedTypes.StateCreator) => { + console.log('zustand create mock'); + + // to support curried version of create + return typeof stateCreator === 'function' ? createUncurried(stateCreator) : createUncurried; +}) as typeof ZustandExportedTypes.create; + +const createStoreUncurried = (stateCreator: ZustandExportedTypes.StateCreator) => { + const store = actualCreateStore(stateCreator); + const initialState = store.getInitialState(); + storeResetFns.add(() => { + store.setState(initialState, true); + }); + return store; +}; + +// when creating a store, we get its initial state, create a reset function and add it in the set +export const createStore = ((stateCreator: ZustandExportedTypes.StateCreator) => { + console.log('zustand createStore mock'); + + // to support curried version of createStore + return typeof stateCreator === 'function' ? createStoreUncurried(stateCreator) : createStoreUncurried; +}) as typeof ZustandExportedTypes.createStore; + +// reset all stores after each test run +afterEach(() => { + act(() => { + storeResetFns.forEach(resetFn => { + resetFn(); + }); + }); +}); diff --git a/src/test/__tests__/common/hooks/useContainerEvents.test.ts b/src/test/__tests__/common/hooks/useContainerEvents.test.ts index ec5ca576..1c3f10bb 100644 --- a/src/test/__tests__/common/hooks/useContainerEvents.test.ts +++ b/src/test/__tests__/common/hooks/useContainerEvents.test.ts @@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil'; import { useContainerEvents } from '@common/hooks/useContainerEvents'; import * as domHelper from '@common/helpers/dom.helper'; import { useStatusStore } from '@src/store'; +import { setInitialState } from '@src/test/__mocks__/store'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -18,17 +19,12 @@ jest.mock('recoil'); jest.mock('@common/constants/build.constants', () => ({ IS_EMBEDDED_MODE: true })); describe('useContainerEvents', () => { - const initialStatusStoreStates = useStatusStore.getState(); - const renderUseContainerEventsHook = (isEditedRecord: boolean) => { (domHelper.dispatchEventWrapper as jest.Mock) = mockDispatchEventWrapper; (useRecoilValue as jest.Mock).mockReturnValueOnce(false); (useRecoilValue as jest.Mock).mockReturnValueOnce(mockEvents); - useStatusStore.setState({ - ...initialStatusStoreStates, - isEditedRecord, - }); + setInitialState(useStatusStore, { isEditedRecord }); renderHook(() => useContainerEvents({ watchEditedState: true })); }; diff --git a/src/test/__tests__/common/hooks/useRecordStatus.test.ts b/src/test/__tests__/common/hooks/useRecordStatus.test.ts index a7dbd6d2..fa4097be 100644 --- a/src/test/__tests__/common/hooks/useRecordStatus.test.ts +++ b/src/test/__tests__/common/hooks/useRecordStatus.test.ts @@ -2,20 +2,16 @@ import { renderHook } from '@testing-library/react'; import { useStatusStore } from '@src/store'; import { useRecordStatus } from '@common/hooks/useRecordStatus'; import { useParams } from 'react-router-dom'; +import { setInitialState } from '@src/test/__mocks__/store'; const mockResourceId = 'mockResourceId'; jest.mock('react-router-dom'); describe('useRecordStatus', () => { - const initialStatusStoreStates = useStatusStore.getState(); - const renderUseRecordStatusHook = (lastSavedIdEqual = false) => { (useParams as jest.Mock).mockReturnValueOnce({ resourceId: mockResourceId }); - useStatusStore.setState({ - ...initialStatusStoreStates, - lastSavedRecordId: lastSavedIdEqual ? mockResourceId : 'anotherId', - }); + setInitialState(useStatusStore, { lastSavedRecordId: lastSavedIdEqual ? mockResourceId : 'anotherId' }); const { result: { current }, diff --git a/src/test/__tests__/components/CommonStatus.test.tsx b/src/test/__tests__/components/CommonStatus.test.tsx index 6233cd0a..cb357c53 100644 --- a/src/test/__tests__/components/CommonStatus.test.tsx +++ b/src/test/__tests__/components/CommonStatus.test.tsx @@ -2,15 +2,11 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { CommonStatus } from '@components/CommonStatus'; import { StatusType } from '@common/constants/status.constants'; import { useStatusStore } from '@src/store'; +import { setInitialState } from '@src/test/__mocks__/store'; describe('CommonStatus', () => { - const initialStatusStoreStates = useStatusStore.getState(); - const renderComponent = (commonMessagesState: StatusEntry[] = []) => { - useStatusStore.setState({ - ...initialStatusStoreStates, - statusMessages: commonMessagesState, - }); + setInitialState(useStatusStore, { statusMessages: commonMessagesState }); return render(); }; diff --git a/src/test/__tests__/components/MarcPreviewComplexLookup.test.tsx b/src/test/__tests__/components/MarcPreviewComplexLookup.test.tsx index 76c82b31..507d97c8 100644 --- a/src/test/__tests__/components/MarcPreviewComplexLookup.test.tsx +++ b/src/test/__tests__/components/MarcPreviewComplexLookup.test.tsx @@ -3,6 +3,7 @@ import { RecoilRoot } from 'recoil'; import { useSearchContext } from '@common/hooks/useSearchContext'; import { MarcPreviewComplexLookup } from '@components/ComplexLookupField/MarcPreviewComplexLookup'; import { useMarcPreviewStore } from '@src/store'; +import { setInitialState } from '@src/test/__mocks__/store'; import state from '@state'; jest.mock('@common/hooks/useSearchContext'); @@ -22,7 +23,6 @@ const marcPreviewMetadata = { } as MarcPreviewMetadata; describe('MarcPreviewComplexLookup', () => { - const initialMarcPreviewState = useMarcPreviewStore.getState(); const onClose = jest.fn(); const onAssignRecord = jest.fn(); @@ -35,11 +35,7 @@ describe('MarcPreviewComplexLookup', () => { marcPreviewData: MarcDTO, marcPreviewMetadata: MarcPreviewMetadata, ) => { - useMarcPreviewStore.setState({ - ...initialMarcPreviewState, - data: marcPreviewData, - metaData: marcPreviewMetadata, - }); + setInitialState(useMarcPreviewStore, { data: marcPreviewData, metaData: marcPreviewMetadata }); return render( { - const initialStatusStoreStates = useStatusStore.getState(); - function renderSaveRecordComponent(isEditedRecord = true) { - useStatusStore.setState({ - ...initialStatusStoreStates, - isEditedRecord, - }); + setInitialState(useStatusStore, { isEditedRecord }); render( diff --git a/src/test/__tests__/components/ViewMarcModal.test.tsx b/src/test/__tests__/components/ViewMarcModal.test.tsx index f3097cb6..3190ee33 100644 --- a/src/test/__tests__/components/ViewMarcModal.test.tsx +++ b/src/test/__tests__/components/ViewMarcModal.test.tsx @@ -1,5 +1,6 @@ import { ViewMarcModal } from '@components/ViewMarcModal'; import { useMarcPreviewStore } from '@src/store'; +import { setInitialState } from '@src/test/__mocks__/store'; import { render } from '@testing-library/react'; const { leader, subfieldContent } = { @@ -33,13 +34,8 @@ const mockMarcPreview = { }; describe('ViewMarcModal', () => { - const initialMarcPreviewState = useMarcPreviewStore.getState(); - test('renders modal and its contents', async () => { - useMarcPreviewStore.setState({ - ...initialMarcPreviewState, - value: mockMarcPreview, - }); + setInitialState(useMarcPreviewStore, { value: mockMarcPreview }); const { findByText } = render();