💻
Building and hosting a WebApp
  • Getting started
  • Project setup
    • Requirements
    • Files organisation
    • Lerna
    • Linter
    • Prettier
    • GitHook
    • Testing
    • Conclusion
  • Backend
    • Files organisation
    • Environment config
    • Express API
    • Security
    • Database
    • GraphQL
    • User authentication
    • Conclusion
  • Frontend
    • Create React App
    • Files organisation
    • Styles
    • Apollo Hooks
    • Form management
    • User authentication
    • Writing tests
    • Types generation
    • Conclusion
  • DevOps
    • CI/CD
    • AWS
      • Managing secrets
      • Pricing
      • RDS
      • S3
      • Route53
      • CloudFront
      • Serverless
      • Security
      • CloudFormation
    • Conclusion
  • 🚧Stripe payment
  • 🚧File upload
Powered by GitBook
On this page
  • Simulate keyboard and mouse events
  • Mock Apollo provider
  • Mock local storage
  • Mock history
  • Bring it together

Was this helpful?

  1. Frontend

Writing tests

This is what we want to test in SignupPage.tsx:

  • The form is filled up correctly.

  • The values submitted come from the form.

  • The token is added to the local storage.

  • The user is redirected to the home page.

To achieve this, we will need to:

  • Simulate keyboard and mouse events.

  • Mock Apollo provider.

  • Mock localStorage.

  • Mock history.

Simulate keyboard and mouse events

There are two ways to do this: Simulate from react-dom/test-utils or using dispatchEvent:

import { act, Simulate } from "react-dom/test-utils";

const emailInput = container.querySelector("input[name=emailAddress]")!;
emailInput.setAttribute("value", "test@example.com");
Simulate.change(emailInput);

const submitButton = container.querySelector("button[type=submit]")!;
expect(submitButton.textContent).toEqual("Submit");
submitButton.dispatchEvent(new MouseEvent("click"));

Mock Apollo provider

We need to export the mutation from the page to be able to use it in our test:

pages/SignupPage.tsx
export const SIGNUP_MUTATION = gql`
  mutation signUp($input: UserAuthInput!) {
    signUp(input: $input) {
      id
      emailAddress
      jwt
    }
  }
`;

export const SignupPage = () => { ... }
const mocks = [
    {
      request: {
        query: SIGNUP_MUTATION,
        variables: {
          input: {
            emailAddress: "test@example.com",
            password: "qwerty123"
          }
        }
      },
      result: {
        data: {
          signUp: {
            id: "userid",
            emailAddress: "test@example.com",
            jwt: "faketoken"
          }
        }
      }
    }
  ];
  
await act(async () => {
   render(
     <MockedProvider mocks={mocks} addTypename={false}>
       <SignupPage />
     </MockedProvider />
    );
});

Mock local storage

Jest comes with ways to easily mock functions:

const spy = jest.spyOn(Storage.prototype, "setItem");
// ... expect(window.localStorage.setItem).to..;
spy.mockRestore();

The important part is to know how to mock things. (e.g Storage.prototype for localStorage).

Mock history

const history = createMemoryHistory({ initialEntries: ["/signup"] });
<Router history={history}>
  <Route path="/signup" component={SignupPage} />
</Router>

Bring it together

pages/SignupPage.spec.tsx
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act, Simulate } from "react-dom/test-utils";
import { SignupPage, SIGNUP_MUTATION } from "./SignupPage";
import { createMemoryHistory } from "history";
import { Router, Route } from "react-router";
import { MockedProvider, wait } from "@apollo/react-testing";
import { ThemeProvider } from "styled-components";

let container: HTMLDivElement;
beforeEach(() => {
  // setup a DOM element as a render target
  container = document.createElement("div");
  document.body.appendChild(container);
});

afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = (null as unknown) as HTMLDivElement; // prevent memory leaks
});

it("renders user data", async () => {
  // setup the test
  const history = createMemoryHistory({ initialEntries: ["/signup"] });
  let signUpMutationCalled = false;
  const spy = jest.spyOn(Storage.prototype, "setItem");

  const mocks = [
    {
      request: {
        query: SIGNUP_MUTATION,
        variables: {
          input: {
            emailAddress: "test@example.com",
            password: "qwerty123"
          }
        }
      },
      result: () => {
        signUpMutationCalled = true;
        return {
          data: {
            signUp: {
              id: "userid",
              emailAddress: "test@example.com",
              jwt: "faketoken"
            }
          }
        };
      }
    }
  ];

  await act(async () => {
    render(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ThemeProvider theme={{}}>
          <Router history={history}>
            <Route path="/signup" component={SignupPage} />
          </Router>
        </ThemeProvider>
      </MockedProvider>,
      container
    );

    // fill up the form
    const emailInput = container.querySelector("input[name=emailAddress]")!;
    emailInput.setAttribute("value", "test@example.com");
    Simulate.change(emailInput);

    const passwordInput = container.querySelector("input[name=password]")!;
    passwordInput.setAttribute("value", "qwerty123");
    Simulate.change(passwordInput);
  });

  await act(async () => {
    // submit the form
    // triggering the hook has to be in another `act`
    const submitButton = container.querySelector("button[type=submit]")!;
    expect(submitButton.textContent).toEqual("Submit");
    submitButton.dispatchEvent(new MouseEvent("click"));
    await wait(0);
    // check loading state
    expect(submitButton.textContent).not.toEqual("Submit");
  });

  // check if the mutation has been  called
  expect(signUpMutationCalled).toBeTruthy();

  // check if the token has been set correctly
  expect(window.localStorage.setItem).toHaveBeenCalledWith(
    "token",
    "faketoken"
  );
  spy.mockRestore();

  // check if the user has been redirected
  expect(history.location.pathname).toBe("/");
});
PreviousUser authenticationNextTypes generation

Last updated 5 years ago

Was this helpful?

Learn more about rendering components with test-utils from the .

We can now use MockedProvider from.

Learn more about MockedProviderwith .

We can use createMemoryHistory from.

branch and available on GitHub.

Testing Recipes
@apollo/react-testing
Testing React components
history
react-unit-test
pull request