I know there are many other questions regarding setInterval and jest. I've read all that I could find, I've read the docs and I think the following should work, but it does not:
// useInterval.js
import React, { useEffect } from "react";
/**
* Calls function in an interval. Unregisters the interval when component is unmounted
* @param {function} callback the function to be executed
* @param {Array} dependencyArray the dependency array you would pass to useEffect(because that's what this hook is). Be very careful about this, unfor
* @param {number} intervalInMs
* @param {boolean}} runImmediately if the function should run immediately as well. if false, the function will only run after intervalInMs has passed
*/
const useInterval = (
callback,
dependencyArray,
intervalInMs,
runImmediately = true
) => {
useEffect(() => {
// schedule interval
const intervalHandle = setInterval(callback, intervalInMs);
// run function immediately
if (runImmediately) {
callback();
}
return () => clearInterval(intervalHandle);
}, [...dependencyArray, intervalInMs]);
};
export default useInterval;
//useInterval.test.js
import { renderHook, act } from "@testing-library/react-hooks";
import useInterval from "../hooks/useInterval";
describe("useInterval hook", () => {
let callback;
beforeEach(() => {
callback = jest.fn();
});
beforeAll(() => {
jest.useFakeTimers();
});
afterEach(() => {
callback.mockRestore();
jest.clearAllTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it("calls callback repeatedly", async () => {
const { result } = renderHook(() => useInterval(callback, [], 15));
expect(result.current).toBeUndefined();
expect(setInterval).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledTimes(1);
expect(jest.getTimerCount()).toEqual(1); // nope, the test fails with expected: 1 received: 0
jest.advanceTimersByTime(10);
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(5);
expect(callback).toHaveBeenCalledTimes(2); // if getTimerCount is commented out, this fails with expected: 2 received: 1
});
});
I've also tried wrapping the jest.advanceTimersByTime into act: act(()=>jest.advanceTimersByTime(10)) but this does not change the output.
Is there something I'm missing about this? The hook does work when used in a component.