user-event: `userEvent.click` fails due to timeout when used with `jest.useFakeTimers`
@testing-library/user-eventversion:14.0.0-beta.7
- Testing Framework and version:
jestversion:27.4.7,@testing-library/jest-domversion5.15.1,@testing-library/reactversion12.1.2 - DOM Environment:
jsdomversion19.0.0
Relevant code or config
// Dummy.js
import React from "react";
const Dummy = (props) => {
const clickHandler = () => {
setTimeout(() => {
props.onClick();
}, 500);
};
return <button onClick={clickHandler}>Click me</button>;
};
export default Dummy;
// Dummy.test.js
import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import userEvent from "@testing-library/user-event";
import Dummy from "./Dummy";
// Fails
test("onClick prop is called on button (userEvent, fakeTimer)", async () => {
jest.useFakeTimers();
const user = userEvent.setup();
const onClick = jest.fn();
render(<Dummy onClick={onClick} />);
const button = screen.getByRole("button");
await user.click(button);
jest.runOnlyPendingTimers();
expect(onClick).toHaveBeenCalledTimes(1);
jest.useRealTimers();
});
What you did:
Dummy component calls onClick prop after a delay when its button is clicked. I tried testing it using userEvent.click alongside jest.fakeTimers to avoid waiting for the delay.
What happened:
The test onClick prop is called on button (userEvent, fakeTimer) fails due to timeout.
npx jest output
FAIL client/src/dummy/Dummy.test.js (11.923 s)
✕ onClick prop is called on button (userEvent, fakeTimer) (5014 ms)
● onClick prop is called on button (userEvent, fakeTimer)
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
19 |
20 | // Fails
> 21 | test.only("onClick prop is called on button (userEvent, fakeTimer)", async () => {
| ^
22 | jest.useFakeTimers();
23 | const user = userEvent.setup();
24 | const onClick = jest.fn();
at Object.<anonymous> (client/src/dummy/Dummy.test.js:21:6)
Reproduction repository: https://codesandbox.io/s/user-event-fake-timers-lwb65 (Source files are provided but I couldn’t get the tests to run on codesandbox)
Problem description:
The test fails due to timeout. The problem arises when using jest.useFakeTimers. The line await user.click(button) never completes.
Suggested solution:
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 9
- Comments: 17 (4 by maintainers)
Commits related to this issue
- WIP: upgrade user-event in SidePanel components - awaited userEvent methods and removed unnecessary act blocks - migrated useSidePanel to newRenderHook and removed unnecessary actHook blocks - added ... — committed to sumup-oss/circuit-ui by deleted user 2 years ago
- WIP: upgrade user-event in SidePanel components - awaited userEvent methods and removed unnecessary act blocks - migrated useSidePanel to newRenderHook and removed unnecessary actHook blocks - added ... — committed to sumup-oss/circuit-ui by deleted user 2 years ago
- Upgrade user-event to v14 (#1605) * Upgrade user-event to v14 * Address breaking changes in Popover spec - removed the mocked document.createRange. This isn't necessary anymore. See https://git... — committed to sumup-oss/circuit-ui by deleted user 2 years ago
- Make selectOption work when fake timers are enabled (#4604) # Motivation While cleaning up a test in another branch, I discovered that `userEvent.selectOption` doesn't work when fake timers are enab... — committed to dfinity/nns-dapp by dskloetd 4 months ago
I wound up here looking for an answer to this problem. It seems to me that a better solution than described above is to make use of
userEvent.setup({ advanceTimers: jest.advanceTimersByTime }): instead of disabling the delay entirely, use the provided mechanism to advance the fake timers when needed.The implementation waits for delay seconds per
setTimeoutbetween actions. When fake timers are activated,new Promise(r => setTimeout(r, delay))only resolves when the timer is advanced.As delaying to the next macrotask is default behavior in
v14, maybe #585 is more pressing now. Maybe we should add a paragraph about fake timers in a more prominent place in the documentation - maybe in the introduction or in an extra FAQ section.For now you can disable delaying the next action per
userEvent.setup({delay: null}).Edit: fixed link
userEvent.setup({delay: null})solved the problem. Thank you!By the way, the documentation link wasn’t working. Maybe you meant this one: delay.
@sschneider-ihre-pvs this worked for me, could you verify that this also works for you please.
the above (also as vi. advanceTimersByTime) didn’t work for vitest, but this worked for me
vi.useFakeTimers({ shouldAdvanceTime: true });@s10mcow The documentation for
advanceTimersis here: https://testing-library.com/docs/user-event/options#advancetimers The documentation forjest.advanceTimersByTimeis here: https://jestjs.io/docs/timer-mocks#advance-timers-by-timesetting delay to null does not work for me, the test still does not complete in within the timeout bounds with
.typeHas anyone come across the vitest equivalent of
jest.advanceTimersByTime? I’m struggling to find much help from the docsI think it might be worth mentioning it in https://testing-library.com/docs/using-fake-timers/