/** * @jest-environment jsdom */ import { renderHook, act } from '@testing-library/react' import { useDebounce, useDebouncedCallback } from '../useDebounce' describe('useDebounce', () => { beforeEach(() => { jest.useFakeTimers() }) afterEach(() => { jest.runOnlyPendingTimers() jest.useRealTimers() }) describe('基本功能', () => { it('应该返回初始值', () => { const { result } = renderHook(() => useDebounce('initial', 300)) expect(result.current).toBe('initial') }) it('应该在延迟后更新值', () => { const { result, rerender } = renderHook( ({ value, delay }) => useDebounce(value, delay), { initialProps: { value: 'initial', delay: 300 } } ) expect(result.current).toBe('initial') // 更新值 rerender({ value: 'updated', delay: 300 }) expect(result.current).toBe('initial') // 还未更新 // 快进时间 act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toBe('updated') // 已更新 }) it('应该在值快速变化时只更新最后一个值', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 300), { initialProps: { value: 'initial' } } ) // 快速更新多次 rerender({ value: 'update1' }) rerender({ value: 'update2' }) rerender({ value: 'update3' }) expect(result.current).toBe('initial') // 快进时间 act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toBe('update3') // 只更新到最后一个值 }) }) describe('延迟时间配置', () => { it('应该支持自定义延迟时间', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 500), { initialProps: { value: 'initial' } } ) rerender({ value: 'updated' }) // 300ms后还未更新 act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toBe('initial') // 再过200ms(总共500ms)后更新 act(() => { jest.advanceTimersByTime(200) }) expect(result.current).toBe('updated') }) it('应该支持0延迟(立即更新)', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 0), { initialProps: { value: 'initial' } } ) rerender({ value: 'updated' }) act(() => { jest.advanceTimersByTime(0) }) expect(result.current).toBe('updated') }) }) describe('不同数据类型', () => { it('应该支持字符串类型', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 300), { initialProps: { value: 'test' } } ) rerender({ value: 'updated' }) act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toBe('updated') }) it('应该支持数字类型', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 300), { initialProps: { value: 0 } } ) rerender({ value: 42 }) act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toBe(42) }) it('应该支持对象类型', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 300), { initialProps: { value: { name: 'initial' } } } ) const newValue = { name: 'updated' } rerender({ value: newValue }) act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toEqual(newValue) }) it('应该支持数组类型', () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 300), { initialProps: { value: [1, 2, 3] } } ) const newValue = [4, 5, 6] rerender({ value: newValue }) act(() => { jest.advanceTimersByTime(300) }) expect(result.current).toEqual(newValue) }) }) }) describe('useDebouncedCallback', () => { beforeEach(() => { jest.useFakeTimers() }) afterEach(() => { jest.runOnlyPendingTimers() jest.useRealTimers() }) describe('基本功能', () => { it('应该返回一个函数', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) expect(typeof result.current).toBe('function') }) it('应该在延迟后调用回调函数', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) act(() => { result.current('test') }) expect(callback).not.toHaveBeenCalled() act(() => { jest.advanceTimersByTime(300) }) expect(callback).toHaveBeenCalledWith('test') expect(callback).toHaveBeenCalledTimes(1) }) it('应该在快速调用时只执行最后一次', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) // 分别调用,每次调用都会重置定时器 act(() => { result.current('call1') }) act(() => { result.current('call2') }) act(() => { result.current('call3') }) expect(callback).not.toHaveBeenCalled() act(() => { jest.advanceTimersByTime(300) }) expect(callback).toHaveBeenCalledWith('call3') expect(callback).toHaveBeenCalledTimes(1) }) }) describe('参数传递', () => { it('应该正确传递单个参数', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) act(() => { result.current('test') }) act(() => { jest.advanceTimersByTime(300) }) expect(callback).toHaveBeenCalledWith('test') }) it('应该正确传递多个参数', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) act(() => { result.current('arg1', 'arg2', 'arg3') }) act(() => { jest.advanceTimersByTime(300) }) expect(callback).toHaveBeenCalledWith('arg1', 'arg2', 'arg3') }) it('应该正确传递对象参数', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) const obj = { name: 'test', value: 42 } act(() => { result.current(obj) }) act(() => { jest.advanceTimersByTime(300) }) expect(callback).toHaveBeenCalledWith(obj) }) }) describe('延迟时间配置', () => { it('应该支持自定义延迟时间', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 500)) act(() => { result.current('test') }) // 300ms后还未调用 act(() => { jest.advanceTimersByTime(300) }) expect(callback).not.toHaveBeenCalled() // 再过200ms(总共500ms)后调用 act(() => { jest.advanceTimersByTime(200) }) expect(callback).toHaveBeenCalledWith('test') }) }) describe('定时器清理', () => { it('应该在新调用时清除之前的定时器', () => { const callback = jest.fn() const { result } = renderHook(() => useDebouncedCallback(callback, 300)) act(() => { result.current('call1') }) // 快进150ms act(() => { jest.advanceTimersByTime(150) }) // 再次调用,应该清除之前的定时器 act(() => { result.current('call2') }) // 再快进150ms(距离第一次调用已经300ms,但距离第二次调用只有150ms) act(() => { jest.advanceTimersByTime(150) }) // 此时callback还不应该被调用 expect(callback).not.toHaveBeenCalled() // 再快进150ms(距离第二次调用已经300ms) act(() => { jest.advanceTimersByTime(150) }) // 现在应该只调用一次,参数是call2 expect(callback).toHaveBeenCalledWith('call2') expect(callback).toHaveBeenCalledTimes(1) }) }) })