Skip to content

Instantly share code, notes, and snippets.

@ivikash
Created December 19, 2019 23:08
Show Gist options
  • Save ivikash/33cd30229a3ddaea153ccc29c91a41e9 to your computer and use it in GitHub Desktop.
Save ivikash/33cd30229a3ddaea153ccc29c91a41e9 to your computer and use it in GitHub Desktop.
import { renderHook, act } from "@testing-library/react-hooks";
import { useDebounce } from "@ui/utils/hooks";
describe("useDebounce", () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it("should be defined", () => {
expect(useDebounce).toBeDefined();
});
it("should execute immediately for the first time", () => {
const value = "Hello World";
const { result } = renderHook(() => useDebounce(value, 0));
expect(result.current).toBe("Hello World");
});
it("should execute callback after specified intervals", () => {
const callback = jest.fn();
renderHook(() => useDebounce(callback, 1000));
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(100);
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(2);
});
it("should update values after defined intervals", () => {
let value = "Hello World";
const { rerender, result } = renderHook(() => useDebounce(value, 1000));
value = "Foo Bar";
rerender();
expect(result.current).toBe("Hello World");
act(() => {
// Timer has not crossed delay
jest.advanceTimersByTime(100);
});
expect(result.current).toBe("Hello World");
act(() => {
// Timer has crossed delay
jest.advanceTimersByTime(1000);
});
expect(result.current).toBe("Foo Bar");
});
it("should cancel function call on unmount", () => {
const value = "Hello World";
const { result, rerender, unmount } = renderHook(() =>
useDebounce(value, 0)
);
expect(result.current).toBe(value);
act(() => {
unmount();
});
act(() => {
rerender({ value: "Delayed" });
});
act(() => {
jest.advanceTimersByTime(1000);
});
expect(result.current).toBe(value);
});
it("should reset timeout on delay change", () => {
const value = "Hello World";
const delay = 1000;
const { result, rerender } = renderHook(
({ value = "TEST", delay = 1000 }) => useDebounce(value, delay),
{
initialProps: {
value: value,
delay: delay,
},
}
);
act(() => {
rerender({ value: "Delayed", delay: 1000 });
});
expect(result.current).toBe(value);
act(() => {});
expect(result.current).toBe(value);
act(() => {
jest.advanceTimersByTime(1000);
});
expect(result.current).toBe("Delayed");
});
it("should reset timeout on value change", () => {
const value = "Hello World";
const delay = 1000;
const { result, rerender } = renderHook(
({ value = "TEST", delay = 1000 }) => useDebounce(value, delay),
{
initialProps: {
value: value,
delay: delay,
},
}
);
jest.advanceTimersByTime(900);
expect(result.current).toBe(value);
act(() => {
rerender({ delay: 1100, value: "Try Again!" });
});
act(() => {
jest.advanceTimersByTime(1000);
});
expect(result.current).toBe(value);
act(() => {
jest.advanceTimersByTime(100);
});
expect(result.current).toBe("Try Again!");
});
it("should merge arguments in array", () => {
const arr = [];
const callback = (x: number) => arr.push(x);
const { rerender } = renderHook(({ arg }) =>
useDebounce(callback(arg), 1000)
);
rerender({ arg: 1 });
rerender({ arg: 2 });
rerender({ arg: 3 });
expect(arr).toEqual([1, 2, 3]);
});
});
export function useDebounce(value: any, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment