test-utils.tsx 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // See https://redux.js.org/usage/writing-tests#setting-up-a-reusable-test-render-function for more information
  2. import React, { PropsWithChildren } from "react";
  3. import { Provider } from "react-redux";
  4. import { configureStore } from "@reduxjs/toolkit";
  5. // eslint-disable-next-line import/no-extraneous-dependencies
  6. import { RenderOptions, render } from "@testing-library/react";
  7. import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
  8. import { I18nextProvider } from "react-i18next";
  9. import i18n from "i18next";
  10. import { initReactI18next } from "react-i18next";
  11. import { AppStore, RootState, rootReducer } from "./src/store";
  12. import { vi } from "vitest";
  13. import { AuthProvider } from "#/context/auth-context";
  14. import { UserPrefsProvider } from "#/context/user-prefs-context";
  15. import { ConversationProvider } from "#/context/conversation-context";
  16. // Mock useParams before importing components
  17. vi.mock("react-router", async () => {
  18. const actual =
  19. await vi.importActual<typeof import("react-router")>("react-router");
  20. return {
  21. ...actual,
  22. useParams: () => ({ conversationId: "test-conversation-id" }),
  23. };
  24. });
  25. // Initialize i18n for tests
  26. i18n
  27. .use(initReactI18next)
  28. .init({
  29. lng: "en",
  30. fallbackLng: "en",
  31. ns: ["translation"],
  32. defaultNS: "translation",
  33. resources: {
  34. en: {
  35. translation: {},
  36. },
  37. },
  38. interpolation: {
  39. escapeValue: false,
  40. },
  41. });
  42. const setupStore = (preloadedState?: Partial<RootState>): AppStore =>
  43. configureStore({
  44. reducer: rootReducer,
  45. preloadedState,
  46. });
  47. // This type interface extends the default options for render from RTL, as well
  48. // as allows the user to specify other things such as initialState, store.
  49. interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
  50. preloadedState?: Partial<RootState>;
  51. store?: AppStore;
  52. }
  53. // Export our own customized renderWithProviders function that creates a new Redux store and renders a <Provider>
  54. // Note that this creates a separate Redux store instance for every test, rather than reusing the same store instance and resetting its state
  55. export function renderWithProviders(
  56. ui: React.ReactElement,
  57. {
  58. preloadedState = {},
  59. // Automatically create a store instance if no store was passed in
  60. store = setupStore(preloadedState),
  61. ...renderOptions
  62. }: ExtendedRenderOptions = {},
  63. ) {
  64. function Wrapper({ children }: PropsWithChildren<object>): JSX.Element {
  65. return (
  66. <Provider store={store}>
  67. <UserPrefsProvider>
  68. <AuthProvider>
  69. <ConversationProvider>
  70. <QueryClientProvider client={new QueryClient()}>
  71. <I18nextProvider i18n={i18n}>
  72. {children}
  73. </I18nextProvider>
  74. </QueryClientProvider>
  75. </ConversationProvider>
  76. </AuthProvider>
  77. </UserPrefsProvider>
  78. </Provider>
  79. );
  80. }
  81. return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
  82. }