testHelpers.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import React, { PropsWithChildren, ReactElement, useMemo } from 'react';
  2. import {
  3. MemoryRouter,
  4. MemoryRouterProps,
  5. Route,
  6. Routes,
  7. } from 'react-router-dom';
  8. import fetchMock from 'fetch-mock';
  9. import { Provider } from 'react-redux';
  10. import { ThemeProvider } from 'styled-components';
  11. import { theme } from 'theme/theme';
  12. import {
  13. render,
  14. renderHook,
  15. RenderOptions,
  16. waitFor,
  17. } from '@testing-library/react';
  18. import { AnyAction, Store } from 'redux';
  19. import { RootState } from 'redux/interfaces';
  20. import { configureStore } from '@reduxjs/toolkit';
  21. import rootReducer from 'redux/reducers';
  22. import {
  23. QueryClient,
  24. QueryClientProvider,
  25. UseQueryResult,
  26. } from '@tanstack/react-query';
  27. import { ConfirmContextProvider } from 'components/contexts/ConfirmContext';
  28. import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
  29. import { GlobalSettingsContext } from 'components/contexts/GlobalSettingsContext';
  30. import { UserInfoRolesAccessContext } from 'components/contexts/UserInfoRolesAccessContext';
  31. import { RolesType, modifyRolesData } from './permissions';
  32. interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
  33. preloadedState?: Partial<RootState>;
  34. store?: Store<Partial<RootState>, AnyAction>;
  35. initialEntries?: MemoryRouterProps['initialEntries'];
  36. userInfo?: {
  37. roles?: RolesType;
  38. rbacFlag: boolean;
  39. };
  40. globalSettings?: {
  41. hasDynamicConfig: boolean;
  42. };
  43. }
  44. interface WithRouteProps {
  45. children: React.ReactNode;
  46. path: string;
  47. }
  48. export const expectQueryWorks = async (
  49. mock: fetchMock.FetchMockStatic,
  50. result: { current: UseQueryResult<unknown, unknown> }
  51. ) => {
  52. await waitFor(() => expect(result.current.isFetched).toBeTruthy());
  53. expect(mock.calls()).toHaveLength(1);
  54. expect(result.current.data).toBeDefined();
  55. };
  56. export const WithRoute: React.FC<WithRouteProps> = ({ children, path }) => {
  57. return (
  58. <Routes>
  59. <Route path={path} element={children} />
  60. </Routes>
  61. );
  62. };
  63. export const TestQueryClientProvider: React.FC<PropsWithChildren<unknown>> = ({
  64. children,
  65. }) => {
  66. // use new QueryClient instance for each test run to avoid issues with cache
  67. const queryClient = new QueryClient({
  68. defaultOptions: { queries: { retry: false } },
  69. });
  70. return (
  71. <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  72. );
  73. };
  74. /**
  75. * @description it will create a UserInfo Provider that will actually
  76. * disable the rbacFlag , to user if you can pass it as an argument
  77. * */
  78. const TestUserInfoProvider: React.FC<
  79. PropsWithChildren<{ data?: { roles?: RolesType; rbacFlag: boolean } }>
  80. > = ({ children, data }) => {
  81. const contextValue = useMemo(() => {
  82. const roles = modifyRolesData(data?.roles);
  83. return {
  84. username: 'test',
  85. rbacFlag: !!(typeof data?.rbacFlag === 'undefined'
  86. ? false
  87. : data?.rbacFlag),
  88. roles,
  89. };
  90. }, [data]);
  91. return (
  92. <UserInfoRolesAccessContext.Provider value={contextValue}>
  93. {children}
  94. </UserInfoRolesAccessContext.Provider>
  95. );
  96. };
  97. const customRender = (
  98. ui: ReactElement,
  99. {
  100. preloadedState,
  101. store = configureStore<RootState>({
  102. reducer: rootReducer,
  103. preloadedState,
  104. }),
  105. initialEntries,
  106. userInfo,
  107. globalSettings,
  108. ...renderOptions
  109. }: CustomRenderOptions = {}
  110. ) => {
  111. // overrides @testing-library/react render.
  112. const AllTheProviders: React.FC<PropsWithChildren<unknown>> = ({
  113. children,
  114. }) => (
  115. <TestQueryClientProvider>
  116. <GlobalSettingsContext.Provider
  117. value={globalSettings || { hasDynamicConfig: false }}
  118. >
  119. <ThemeProvider theme={theme}>
  120. <TestUserInfoProvider data={userInfo}>
  121. <ConfirmContextProvider>
  122. <Provider store={store}>
  123. <MemoryRouter initialEntries={initialEntries}>
  124. <div>
  125. {children}
  126. <ConfirmationModal />
  127. </div>
  128. </MemoryRouter>
  129. </Provider>
  130. </ConfirmContextProvider>
  131. </TestUserInfoProvider>
  132. </ThemeProvider>
  133. </GlobalSettingsContext.Provider>
  134. </TestQueryClientProvider>
  135. );
  136. return render(ui, { wrapper: AllTheProviders, ...renderOptions });
  137. };
  138. const customRenderHook = (hook: () => UseQueryResult<unknown, unknown>) =>
  139. renderHook(hook, { wrapper: TestQueryClientProvider });
  140. export { customRender as render, customRenderHook as renderQueryHook };
  141. export class EventSourceMock {
  142. url: string;
  143. close: () => void;
  144. open: () => void;
  145. error: () => void;
  146. onmessage: () => void;
  147. constructor(url: string) {
  148. this.url = url;
  149. this.open = jest.fn();
  150. this.error = jest.fn();
  151. this.onmessage = jest.fn();
  152. this.close = jest.fn();
  153. }
  154. }