I have a website made with React.JS that is continuously emitting events for everything [and nothing] that is happening. For example, users typed something in the form, emit an event. Users set focus on a field and do not do anything for some time, we again emit an event. The idea is to understand customers' behavior.
I have a button what used to work as <Button onClick={(e)=>handler(e)}/>
. I had to debounce the button and change it to <Button onClick={(e)=>setTimeout(handler(e), 1000)}/>
. Otherwise, users did not understand what was happening.
Now, I am trying to adjust the tests written as:
it('displays similar listings', async () => {
const renderResult = renderVdp(renderParams);
await new Promise((resolve) => setTimeout(resolve, 1000));
await waitFor(async () => {
expect(renderResult.getAllByText('2017 BMW M3')).toHaveLength(4);
});
});
I was able to keep the original tests working by adding the await new Promise((resolve) => setTimeout(resolve, 1000));
before relevant tests. Without it, tests receive the next emitted event, which is not the event emitted as a response to clicking on the button.
I would like to use jest timer mocking or something similar, instead of actually introducing delays inside my tests. I have tried using jest.useFakeTimers()
, but it does not work as I need it to. My expects are failing.
Suggestions?
I have a website made with React.JS that is continuously emitting events for everything [and nothing] that is happening. For example, users typed something in the form, emit an event. Users set focus on a field and do not do anything for some time, we again emit an event. The idea is to understand customers' behavior.
I have a button what used to work as <Button onClick={(e)=>handler(e)}/>
. I had to debounce the button and change it to <Button onClick={(e)=>setTimeout(handler(e), 1000)}/>
. Otherwise, users did not understand what was happening.
Now, I am trying to adjust the tests written as:
it('displays similar listings', async () => {
const renderResult = renderVdp(renderParams);
await new Promise((resolve) => setTimeout(resolve, 1000));
await waitFor(async () => {
expect(renderResult.getAllByText('2017 BMW M3')).toHaveLength(4);
});
});
I was able to keep the original tests working by adding the await new Promise((resolve) => setTimeout(resolve, 1000));
before relevant tests. Without it, tests receive the next emitted event, which is not the event emitted as a response to clicking on the button.
I would like to use jest timer mocking or something similar, instead of actually introducing delays inside my tests. I have tried using jest.useFakeTimers()
, but it does not work as I need it to. My expects are failing.
Suggestions?
Share Improve this question asked Sep 2, 2021 at 14:51 Igor ShmuklerIgor Shmukler 2,2164 gold badges25 silver badges61 bronze badges2 Answers
Reset to default 10Once you use fake timers, you control when to run them. Try to run them after you render:
it('displays similar listings', async () => {
jest.useFakeTimers()
const renderResult = renderVdp(renderParams);
jest.runAllTimers();
expect(renderResult.getAllByText('2017 BMW M3')).toHaveLength(4);
});
If that's not working, you can always use a more "aggressive" mock:
global.setTimeout = jest.fn(cb => cb());
I would do it in the test setup beforeEach
/ beforeAll
and rollback to original setTimeout in the test teardown afterEach
/ afterAll
It is hard to tell how the test should look like if I can't see the code.
If your ponent works a little bit like this:
const SetTimeoutComponent = () => {
const [content, setContent] = React.useState<string[]>([]);
const handleButtonClick = (event: React.MouseEvent<HTMLElement>) => {
setContent([
'2017 BMW M3',
'2017 BMW M3',
'2017 BMW M3',
'2017 BMW M3'
])
}
return (
<div>
<button onClick={(e)=>setTimeout(() => handleButtonClick(e), 1000)}>
Set timeout with state update
</button>
<div>
{content.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
</div>
);
};
then you can test it as follows:
it('displays similar listings', async () => {
render(<TestedComponent />);
const setTimeoutButton = screen.getByRole('button');
jest.useFakeTimers();
fireEvent.click(setTimeoutButton);
act(() => {
jest.runAllTimers();
});
expect(screen.getAllByText('2017 BMW M3')).toHaveLength(4);
jest.useRealTimers();
});
You can check more in my post.