Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

userEvent.click does not wait for useEffect changes to be flushed #255

Closed
jessethomson opened this issue May 2, 2020 · 5 comments · Fixed by testing-library/react-testing-library#685 or #302
Labels

Comments

@jessethomson
Copy link

Scenario

I have a component that executes a callback inside of useEffect any time there is a state change. In my tests, if I use userEvent.click to trigger that state change, the callback is executed after my test has already finished and the test fails. If I use fireEvent.click instead, the test passes.

Maybe this is expected and fireEvent.click is synchronous and userEvent.click is asynchronous and people are just expected to convert all their tests to be async when using userEvent (I hope that's not the case, but maybe I'm missing something).

Full disclosure, I don't actually know what the problem is and my understanding of the problem and the title of this issue could be wayyy off.

Example

App.js

const App = ({ onChange = noop }) => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    onChange(count);
  }, [count, onChange]);

  return <button onClick={() => setCount(c => c + 1)}>Increment</button>;
};

App.test.js

test("passes if I use fireEvent.click", () => {
  const onChangeMock = jest.fn();
  const { getByRole } = render(<App onChange={onChangeMock} />);

  expect(onChangeMock).toHaveBeenCalledTimes(1);

  fireEvent.click(getByRole("button"));
  expect(onChangeMock).toHaveBeenCalledTimes(2);
});

test("fails if I use userEvent.click", () => {
  const onChangeMock = jest.fn();
  const { getByRole } = render(<App onChange={onChangeMock} />);

  expect(onChangeMock).toHaveBeenCalledTimes(1);

  userEvent.click(getByRole("button"));
  expect(onChangeMock).toHaveBeenCalledTimes(2);
});

test("passes if I use userEvent.click with waitFor", async () => {
  const onChangeMock = jest.fn();
  const { getByRole } = render(<App onChange={onChangeMock} />);

  expect(onChangeMock).toHaveBeenCalledTimes(1);

  userEvent.click(getByRole("button"));
  await waitFor(() => expect(onChangeMock).toHaveBeenCalledTimes(2));
});

Expected

  • Replacing fireEvent.click for userEvent.click should not break tests of components with useEffect

Actual

  • Replacing fireEvent.click for userEvent.click does break tests of components with useEffect

Reproduction

@calebeby
Copy link
Contributor

calebeby commented May 6, 2020

This is closely related to #128

@calebeby
Copy link
Contributor

calebeby commented May 6, 2020

@jessethomson if you wrap userEvent calls with act, it should work. fireEvent from react-testing-library does this automatically, but fireEvent from dom-testing-library does not (because act is a react feature). user-event calls fireEvent from dom-testing-library, since this library is not react-specific. Once we resolve #128, this issue should also be resolved.

Adding act on all your user-event calls is kind of a workaround, eventually I hope that user-event can do this automatically for react projects.

I made a fork of your codesandbox that uses act: https://codesandbox.io/s/blissful-cache-z9r5h?file=/src/App.test.js:1062-1113

@gcvfi
Copy link

gcvfi commented May 31, 2020

Another work around, after doing userEvent.click(some_element_object);
and before checking the UI effect for above click action, add zero second delay using
await new Promise(r=>setTimeout(()=>r(), 0));

I dont know, doing this work around has any other side effects.
If not, can we make it part of userEvent.click()

kentcdodds added a commit to testing-library/dom-testing-library that referenced this issue Jun 1, 2020
This is intended for supporting `act` in React, but should be useful for
other frameworks (I think it could help with triggering change detection
for angular for example).

Ref: testing-library/user-event#188,
testing-library/user-event#255,
https://github.com/testing-library/user-event/issues/277
kentcdodds added a commit to testing-library/dom-testing-library that referenced this issue Jun 1, 2020
This is intended for supporting `act` in React, but should be useful for
other frameworks (I think it could help with triggering change detection
for angular for example).

Ref: testing-library/user-event#188,
testing-library/user-event#255,
https://github.com/testing-library/user-event/issues/277
kentcdodds added a commit to testing-library/react-testing-library that referenced this issue Jun 1, 2020
Now not only will React Testing Library's `fireEvent` be wrapped in
`act`, but so will DOM Testing Library's `fireEvent` (if
`@testing-library/react` is imported). It works very similar to async
act for the `asyncWrapper` config.

Closes: testing-library/user-event#188
Closes: testing-library/user-event#255
Reference: https://github.com/testing-library/user-event/issues/277
@kentcdodds
Copy link
Member

🎉 This issue has been resolved in version 10.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@kentcdodds
Copy link
Member

This is now fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants