Testing login with Typescript and Jest

Viewed 453

I am having trouble trying to set up a test for an attempted login with axios. I want to be able to mock a situation where a user attempts a login and then I pass them fake data to resolve this post call, but I am not sure on how to implement the mock correctly. Where am I make a mistake? login.tsx (normal login form implementation)

  <Grid className={classes.loginContainer} container>
      <Grid sx={{ margin: "auto" }} xs={6} md={6} item>
        {!loading ? (
          <>
            <Typography
              sx={{
                color: "purple",
                margin: "20px 30px",
                padding: "20px 0",
                textAlign: "justify",
              }}
              variant="h4"
            >
              Please Login
            </Typography>
            {invalid && (
              <Grid className={classes.invalid}>
                <Typography variant="h6">Invalid email or password</Typography>
              </Grid>
            )}
            <LoginForm
              isMobile={isMobile}
              submit={submitHandler}
              password={PASSWORD}
              email={EMAIL}
            />
            <NavLink
              style={{
                textDecoration: "none",
                margin: "0 30px",
                width: "50%",
              }}
              to={"/signup"}
            >
              <Typography
                sx={{ margin: "0 30px", fontSize: "1.2rem", color: "green" }}
                variant="body1"
              >
                Open an account
              </Typography>
            </NavLink>
          </>
        ) : (
          <Box sx={{ textAlign: "center" }}>
            <Loader />
          </Box>
        )}
      </Grid>
      <Grid className={classes.imgContainer} xs={6} md={6} item />
    </Grid>

login.test.tsx

import { ThemeProvider } from "@emotion/react";
import { createTheme } from "@mui/material";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import App from "../App";
import { store } from "../store/store";
import mockAxios from "./mocks/axios";

const theme = createTheme({
  palette: {
    primary: {
      main: "#03a9f4",
    },
    secondary: {
      main: "#FEFDFC",
    },
  },
  typography: {
    fontFamily: "Noto JP Sans",
  },
  breakpoints: {
    values: {
      xs: 0,
      sm: 600,
      md: 900,
      lg: 1200,
      xl: 1536,
    },
  },
});

describe("Navigation test suite. Profile exclusive", () => {
  beforeEach(() => {
    // IntersectionObserver isn't available in test environment
    const mockIntersectionObserver = jest.fn();
    mockIntersectionObserver.mockReturnValue({
      observe: () => null,
      unobserve: () => null,
      disconnect: () => null,
    });
    window.IntersectionObserver = mockIntersectionObserver;
  });
  test("find login page.", async () => {
    render(
      <Provider store={store}>
        <ThemeProvider theme={theme}>
          <BrowserRouter>
            <App />
          </BrowserRouter>
        </ThemeProvider>
      </Provider>
    );
    const loginLink: HTMLElement = screen.getByRole("link", {
      name: /Login/i,
    });

    userEvent.click(loginLink);
    const loginPage = await screen.findByText(/Please login/i, {
      exact: false,
    });
    expect(loginPage).toBeInTheDocument();
  });
  test("login attempt", async () => {
    mockAxios.post.mockResolvedValueOnce(() =>
      Promise.resolve({
        data: {
          data: {
            token: "abdb23231232jdsaWEDwdxaCDA",
            expiresIn: 100000,
            isEnabled: true,
            isLocked: false,
          },
        },
      })
    );
    render(
      <Provider store={store}>
        <ThemeProvider theme={theme}>
          <BrowserRouter>
            <App />
          </BrowserRouter>
        </ThemeProvider>
      </Provider>
    );
    const email = "something@email.com";
    const pw = "pass123";
    const username = screen.getByPlaceholderText(/enter email/i);
    const password = screen.getByPlaceholderText(/password/i);
    const submit = screen.getByRole("button", { name: /Login/i });
    userEvent.click(username);
    userEvent.type(username, email);
    userEvent.click(password);
    userEvent.type(password, pw);
    userEvent.click(submit);
    const profile = await screen.findByRole(
      "link",
      { name: "Account" },
      { timeout: 2000 }
    );
    console.log(profile);
    expect(profile).toBeInTheDocument();
  });

mocked axios (axios.tsx)

export default {
  get: jest.fn(() => Promise.resolve({})),
  post: jest.fn(() => Promise.resolve({})),
};
1 Answers

TL:DR ---> I seemed to have found an alternative to using Jest.mock in the form of the npm libray msw. Below is the code I used.

This library has been recommended by the creator of react testing library for mocking Async Fetch calls and allows for better control of request. It also allows you to control status codes and set the urls you want as well. Links to tutorial and example below as well.

Mock Ex: https://testing-library.com/docs/react-testing-library/example-intro/#mock Mock Ex in Video Form: https://www.youtube.com/watch?v=v77fjkKQTH0&t=998s

Server set up (login.test.tsx)

const server: SetupServerApi = setupServer(
  rest.post(
    `${LB_AUTH_DOMAIN}/${API_VERSION}/authentication/login`,
    (req, res, ctx) => {
      return res(
        ctx.status(200),
        ctx.json({
          token: "abdb23231232jdsaWEDwdxaCDA",
          expiresIn: 100000,
          isEnabled: true,
          isLocked: false,
        })
      );
    }
  )
);

You will then run the test according to what you need (entered fake login data) and the server will return the resolved ctx you set.

I would still appreciate an example in Jest.mock if anyone is willing. Thank you.

Related