Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to receive the mocked response in my component using MSW, redux toolkit query and react testing library. #4783

Open
mohit-92 opened this issue Dec 20, 2024 · 0 comments

Comments

@mohit-92
Copy link

Hello,
I am trying to mock the API's using MSW v2 but somehow I am unable to retrieve the mocked response back in my component. Below are the details,

"@testing-library/react": "^16.0.1",
"msw": "^2.7.0",
"vitest": "^2.1.4"

Redux toolkit query,

export const myApi = createApi({
  reducerPath: "partners",
  baseQuery: fetchBaseQuery({
    baseUrl:
      import.meta.env.MODE === "test"
        ? "https://localhost:3000"
        : 'https://mygatewayurl',
  }),
  tagTypes: ["data"],

  endpoints: (builder) => ({
    getData: builder.query<myResponse, Page>({
      query: (paginationOptions) => ({
        url: API_ENDPOINTS.allPartners,
        method: "GET",
        params: {
          page: paginationOptions.page,
          pageSize: paginationOptions.pageSize,
        },
      }),
      providesTags: (_result, _error, { page }) => [
        { type: "data", id: page },
      ],
      transformResponse: handleTransformResponse<myResponse>,
      transformErrorResponse: handleTransformErrorResponse,
    }),
});

export const {
  useGetDataQuery} = myApi;

Taken from redux toolkit documentation

export const handleTransformResponse = <T>(
  response: unknown,
  meta: FetchBaseQueryMeta
): T => {
  console.log("response", response);
  if (response === null || response === undefined) {
    const { status } = meta?.response || {};
    if (status === 200 || status === 204) {
      return {} as T;
    }
  }

  return response as T;
};

Store setup is like this,

const rootReducer = combineReducers({
[partnersApi.reducerPath]: partnersApi.reducer
})

export const setupStore = (preloadedState?: Partial<RootState>) => {
  return configureStore({
    reducer: rootReducer,
    preloadedState,
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware()
        .concat(myApi.middleware)
  });
};

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export default store;
export type AppStore = ReturnType<typeof setupStore>;

Test renderer picked from Redux toolkit documentation

import { render } from "@testing-library/react";
import type { RenderOptions } from "@testing-library/react";
import type { ReactElement } from "react";
import { Provider } from "react-redux";
import { createMemoryRouter, RouterProvider } from "react-router-dom";

import { createAppRouter } from "@/routes";
import type { AppStore, RootState } from "@/store";
import { setupStore } from "@/store";

interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
  preloadedState?: Partial<RootState>;
  store?: AppStore;
  initialEntries?: string[];
}

export function renderWithProviders(
  ui: ReactElement,
  {
    preloadedState = {},
    store = setupStore(preloadedState),
    initialEntries = ["/"],
    ...renderOptions
  }: ExtendedRenderOptions = {}
) {
  const routes = createAppRouter().routes;
  const router = createMemoryRouter(routes, { initialEntries });
  function Wrapper() {
    return (
      <Provider store={store}>
        <RouterProvider router={router} />
      </Provider>
    );
  }
  return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}

MSW setup,

Handler.ts file

const baseUrl = "https://localhost:3000";

export const handlers = [{
http.get(`${baseUrl}${API_ENDPOINTS.allPartners}`, async () => {
    const mockData = [
      {
        data: [
          {
            id: "6",
            partnerId: "1",
            name: "ABC",
          },
        ],
        currentPage: 1,
        pageCount: 1,
        pageSize: 50,
        rowCount: 1,
        firstRowOnPage: 0,
        lastRowOnPage: 0,
      },
    ];
    await delay(1000);
    return HttpResponse.text("hello", {
      status: 200,
    });
}];

Setup test file

import "@testing-library/jest-dom";
import { expect, afterEach, beforeAll, afterAll } from "vitest";
import * as matchers from "@testing-library/jest-dom/matchers";
import { cleanup } from "@testing-library/react";
import { server } from "./mocks/server";

expect.extend(matchers);

afterEach(() => {
  cleanup();
});

// Start the MSW server before all tests
beforeAll(() => {
  console.log("listening****");
  server.listen({
    onUnhandledRequest: "error",
  });
});

// Reset handlers after each test to ensure no tests are affected by previous ones
afterEach(() => {
  console.log("resetting****");
  server.resetHandlers();
});

// Clean up and close the server after all tests
afterAll(() => server.close());

Object.defineProperty(window, "scrollTo", {
  writable: true,
  value: vi.fn(),
});

Component where I am not getting the data is being rendered from root using the useNavigate() hook from react router dom v6

export const MyComp = () => {
  const navigate = useNavigate();
  const { isLoading, data } = useGetDataQuery({
    page: 1,
    pageSize: 50,
  });

  useEffect(() => {
    console.log("myComp mounted");
  }, []);

  console.log("partnersList ****", data);
  console.log("isLoading ****", isLoading);

  if (isLoading) {
    return <FullPageLoader />;
  }

  if (!isLoading && !partnersList) {
    toast.error("Something went wrong");
    return null;
  }

return(
<div>***</div>
)

Test file,

import App from "./App";

vi.mock("jwt-decode");

beforeEach(() => {
  vi.clearAllMocks();
});

test("renders the app component - logged in user", async () => {
  const decodedToken = { exp: Math.floor(Date.now() / 1000) + 1000 };

  vi.mocked(jwtDecode).mockImplementation(() => decodedToken);

  const { debug } = renderWithProviders(<App />, {
    initialEntries: [`/?token=some-token`], // my home route
  });

  await waitFor(() => {
    console.log(debug());
    expect(true).toBe(true);
  });
});

Vite config

test: {
      globals: true,
      environment: "jsdom",
      setupFiles: "./src/setupTests.ts",
      mockReset: true,
      clearMocks: true,
      coverage: {
        provider: "istanbul",
        include: ["src/**/*.{ts,tsx,js,jsx}"],
        exclude: [
          ...coverageConfigDefaults.exclude,
          "**/config/**",
          "**/main.tsx",
          "src/ui/icons",
        ],
        reporter: ["text", "html", "json"],
      },
    },

Now i can the logs from the response that it's being sent and also logs from useEffect that component is mounted, But I am unable to receive the mocked response in the component.

Please assist if I am missing anything in the setup or the configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant