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

Closed
mohit-92 opened this issue Dec 20, 2024 · 3 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.

@kettanaito
Copy link

Hi, @mohit-92. Could you please share a reproduction repository with your issue? I appreciate extensive code snippets, but unless I can run the code, there's little I can do.

As a rule of thumb, if something doesn't work as expected in MSW, follow the Debugging runbook. It's designed as a step-by-step guide to walk you through the most common mistakes people make when using MSW. Once you do that, please share your results. Thanks.

@kettanaito
Copy link

Please, if you've found the culprit, share it here for others who may be experiencing the same issue. Thanks!

@mohit-92
Copy link
Author

No sorry I haven't found the root cause for this issue. I will be creating a repo to reproduce this issue, but for now we have switched from msw to playwright for testing.

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

2 participants