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
fireEvent.click for userEvent.click should not break tests of components with useEffectActual
fireEvent.click for userEvent.click _does_ break tests of components with useEffectReproduction
This is closely related to #128
@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
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()
:tada: This issue has been resolved in version 10.4.1 :tada:
The release is available on:
npm package (@latest dist-tag)Your semantic-release bot :package::rocket:
This is now fixed!
Most helpful comment
This is now fixed!