29

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 */
skyboyer
  • 22,209
  • 7
  • 57
  • 64
iamwtk
  • 1,031
  • 3
  • 13
  • 24
  • A temporary workaround you can refer the link https://github.com/testing-library/react-testing-library/issues/281#issuecomment-480349256 – srinatht Jul 25 '19 at 05:42

3 Answers3

47

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] } });
});
Shaphan
  • 1,003
  • 11
  • 10
  • 2
    To note that if you have two fireEvent right next to each other, you need two `act()`, one for each. – Ambroise Rabier Aug 12 '20 at 16:10
  • 1
    This gives me `Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);` – Dimitri Kopriwa Feb 02 '21 at 15:51
  • 2
    This solution solved my issue when testing a component that types into a Formik form. I was getting this warning: Warning: An update to Formik inside a test was not wrapped in act(...) – St John Oct 12 '21 at 04:04
  • 1
    This was also helpful when rendering a component that triggered internal state changes – Eran Goldin Nov 23 '21 at 17:19
  • I almost removed Formik until I found this solution. Thank you – Batman Apr 21 '22 at 03:22
  • This answer is incorrect. `fireEvent` already uses `act` internally. All you're doing by adding `await act...` is letting the promises tick through once more. You can achieve the same thing by taking the `fireEvent` call back out of the `act` and adding `await act(()=>{});` because this will also let one level of promises resolve. If your component has another promise resolution, you'll have the same problem. – lmat - Reinstate Monica Aug 26 '22 at 15:43
  • @lmat-ReinstateMonica - Reinstate Monica You lost me. Could you post a code line? – DarkTrick Nov 19 '22 at 08:01
  • I think this would be equivalent to the answer: `fireEvent.change(fileInput, ...); await act(()=>{});`. Sorry, this stuff is very confusing. Good luck out there! – lmat - Reinstate Monica Nov 20 '22 at 20:38
16

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.

jaredsk
  • 2,617
  • 2
  • 18
  • 18
  • Your issue seems like related to following, please see https://github.com/facebook/react/issues/14769 – fernando Jul 18 '19 at 02:53
15

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

Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67
chidimo
  • 2,684
  • 3
  • 32
  • 47
  • 3
    I don't know if this worked for me or not, but this is an excellent answer! "You've got to make sure your users no longer see the loading indicator." <-- yes – Ziggy Feb 03 '22 at 16:59
  • Thanks for this. Great explanation and I was able to fix my warnings. – CaptRespect Aug 25 '22 at 19:53