# 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`:

```typescript
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"));
```

{% hint style="info" %}
Learn more about rendering components with `test-utils` from the [Testing Recipes](https://reactjs.org/docs/testing-recipes.html).
{% endhint %}

### Mock Apollo provider

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

{% code title="pages/SignupPage.tsx" %}

```typescript
export const SIGNUP_MUTATION = gql`
  mutation signUp($input: UserAuthInput!) {
    signUp(input: $input) {
      id
      emailAddress
      jwt
    }
  }
`;

export const SignupPage = () => { ... }
```

{% endcode %}

We can now use `MockedProvider` from[`@apollo/react-testing`](https://www.apollographql.com/docs/react/api/react-testing/).

```typescript
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 />
    );
});
```

{% hint style="info" %}
Learn more about `MockedProvider`with [Testing React components](https://www.apollographql.com/docs/react/development-testing/testing/).
{% endhint %}

### Mock local storage

Jest comes with ways to easily mock functions:

```typescript
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

We can use `createMemoryHistory` from[`history`](https://github.com/ReactTraining/history).

```typescript
const history = createMemoryHistory({ initialEntries: ["/signup"] });
```

```typescript
<Router history={history}>
  <Route path="/signup" component={SignupPage} />
</Router>
```

### Bring it together

{% code title="pages/SignupPage.spec.tsx" %}

```typescript
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("/");
});
```

{% endcode %}

{% hint style="info" %}
[`react-unit-test`](https://github.com/florianherrengt/book-code/tree/react-unit-test) branch and [pull request](https://github.com/florianherrengt/book-code/tree/react-unit-test) available on GitHub.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tutorial.specian.co.uk/frontend/end-to-end-tests.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
