vitjbr
vitjbr

Reputation: 1166

Jest + react-testing-library: Warning update was not wrapped in act()

I am testing my component wit react-testing-library and test works well. I just can't get rid of this warning, fireEvent should by wrapped in act out-of-the-box, but I tried to wrap it again and it did'nt help.

Here is my test case.

it.only("should start file upload if file is added to the field", async () => {
    jest.useFakeTimers();
    const { getByTestId } = wrapper;
    const file = new File(["filefilefile"], "videoFile.mxf");

    const fileInput = getByTestId("drop-zone").querySelector(
      "input[type='file']"
    );

    fireEvent.change(fileInput, { target: { files: [file] } });

    act(() => {
      jest.runAllTimers();
    });

    await wait(() => {
      expect(
        initialProps.uploadItemVideoFileConnect.calledWith(file, 123)
      ).toBe(true);
    });
  });

Here is the warning

Warning: An update to UploadButtonGridComponent inside a test was not wrapped in act(...).

    When testing, code that causes React state updates should be wrapped into act(...):

    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */

Upvotes: 37

Views: 59617

Answers (5)

x-magix
x-magix

Reputation: 2860

Shortened version of @jaredsk answer:

await act(() => fireEvent.submit(form));

Since async do not have to be define while returning promise

Upvotes: 0

Sun
Sun

Reputation: 173

I had a strange bug where it was giving this error and freezing for a subcomponent and the error was thrown in the parent's render function in the test.

I was simply missing brackets[]s in a useEffect.

Upvotes: 0

chidimo
chidimo

Reputation: 2958

So this is hard to summarise but I'll try.

The act warning is simply telling you that there is something happening in your functional component which you're not testing.

Let's say we're rendering a list of todos like so

    <ul>
      {loading ? (
        <p>Fetching todos</p>
      ) : (
        <>
          {appData.todoList.slice(0, 15).map((item) => {
            const { id, title } = item;
            return (
              <li key={id} data-testid={id}>
                <Link to={`/item/${id}`}>{title}</Link>
              </li>
            );
          })}
        </>
      )}
    </ul>

The below test case will throw the act warning

import { waitFor, screen, waitForElementToBeRemoved } from "@testing-library/react";

it("Renders <TodoList /> component", async () => {
    render(<TodoList />);
    await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1));
    await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));

    expect(axios.get).toHaveBeenCalledTimes(1);
    todos.slice(0, 15).forEach((td) => {
      expect(screen.getByText(td.title)).toBeInTheDocument();
    });
  });

But if you re-order the await lines like so

await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));
await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1));

The act warning goes away. This makes sense. You get to make sure your users no longer see the loading indicator.

There are other cases so go ahead and read this post from kent Dodds.

https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning

Upvotes: 15

Shaphan
Shaphan

Reputation: 1073

That issue is caused by many updates inside Component.

I got the same, this is how I solved the issue.

await act( async () => {
 fireEvent.change(fileInput, { target: { files: [file] } });
});

Upvotes: 54

jaredsk
jaredsk

Reputation: 2786

In the source code, fireEvent is already wrapped in act().

The problem may be related to this issue, in which async logic (such as a useEffect) is triggering state changes outside of fireEvent:

https://github.com/kentcdodds/react-testing-library/issues/281

(Without seeing your component implementation it's hard to be sure if this is exactly what's happening in your case.)

Apparently there are plans to include async handling in a future release, so that this won't be a problem.

Upvotes: 17

Related Questions