| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 |
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
- import { renderHook, act } from '@testing-library/react';
- import { useCancellableTimeout } from '../../hooks/useCancellableTimeout';
- describe('useCancellableTimeout', () => {
- beforeEach(() => vi.useFakeTimers());
- afterEach(() => vi.useRealTimers());
- it('runs the callback after the delay', () => {
- const fn = vi.fn();
- const { result } = renderHook(() => useCancellableTimeout());
- act(() => result.current.schedule(fn, 1500));
- expect(fn).not.toHaveBeenCalled();
- act(() => void vi.advanceTimersByTime(1500));
- expect(fn).toHaveBeenCalledTimes(1);
- });
- it('does not run the callback after unmount', () => {
- // The bug this exists for: a modal that defers its own close by 1.5s fired
- // setState and onClose after the component was gone — which throws outright
- // once the DOM around it has been torn down.
- const fn = vi.fn();
- const { result, unmount } = renderHook(() => useCancellableTimeout());
- act(() => result.current.schedule(fn, 1500));
- unmount();
- act(() => void vi.advanceTimersByTime(5000));
- expect(fn).not.toHaveBeenCalled();
- });
- it('cancel() stops a pending callback', () => {
- const fn = vi.fn();
- const { result } = renderHook(() => useCancellableTimeout());
- act(() => result.current.schedule(fn, 1000));
- act(() => result.current.cancel());
- act(() => void vi.advanceTimersByTime(2000));
- expect(fn).not.toHaveBeenCalled();
- });
- it('scheduling again replaces the pending callback', () => {
- const first = vi.fn();
- const second = vi.fn();
- const { result } = renderHook(() => useCancellableTimeout());
- act(() => result.current.schedule(first, 1000));
- act(() => result.current.schedule(second, 1000));
- act(() => void vi.advanceTimersByTime(1000));
- expect(first).not.toHaveBeenCalled();
- expect(second).toHaveBeenCalledTimes(1);
- });
- it('is safe to cancel when nothing is pending', () => {
- const { result } = renderHook(() => useCancellableTimeout());
- expect(() => act(() => result.current.cancel())).not.toThrow();
- });
- });
|