@qazuor/react-hooks
Version:
A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti
174 lines (130 loc) • 5.36 kB
text/typescript
import { act, renderHook } from '@testing-library/react';
import { useWindowWidth } from '../src/hooks/useWindowWidth';
describe('useWindowWidth', () => {
let onChange: jest.Mock;
let initialInnerWidth: number;
beforeEach(() => {
jest.useFakeTimers();
jest.clearAllTimers();
jest.clearAllMocks();
onChange = jest.fn();
initialInnerWidth = window.innerWidth;
Object.defineProperty(window, 'innerWidth', { value: 1200, configurable: true });
});
afterEach(() => {
jest.clearAllTimers();
jest.clearAllMocks();
Object.defineProperty(window, 'innerWidth', { value: initialInnerWidth, configurable: true });
});
it('should initialize with current window width', () => {
const hook = renderHook(() => useWindowWidth({ onChange }));
expect(hook.result.current.width).toBe(window.innerWidth);
expect(onChange).not.toHaveBeenCalled();
hook.unmount();
});
it('should update width on resize with debounce', () => {
const hook = renderHook(() => useWindowWidth({ onChange }));
expect(hook.result.current.width).toBe(1200);
const newWidth = 1024;
act(() => {
window.innerWidth = newWidth;
window.dispatchEvent(new Event('resize'));
});
expect(hook.result.current.width).toBe(1200);
expect(onChange).not.toHaveBeenCalled();
act(() => {
jest.advanceTimersByTime(250);
});
expect(hook.result.current.width).toBe(newWidth);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(newWidth);
hook.unmount();
});
it('should handle custom debounce delay', () => {
const customDelayHook = renderHook(() => useWindowWidth({ debounceDelay: 500, onChange }));
expect(customDelayHook.result.current.width).toBe(1200);
act(() => {
window.innerWidth = 800;
window.dispatchEvent(new Event('resize'));
jest.advanceTimersByTime(250);
});
expect(customDelayHook.result.current.width).toBe(1200);
expect(onChange).not.toHaveBeenCalled();
act(() => {
jest.advanceTimersByTime(250);
});
expect(customDelayHook.result.current.width).toBe(800);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(800);
customDelayHook.unmount();
});
it('should handle start/stop monitoring', () => {
const hook = renderHook(() => useWindowWidth({ onChange }));
expect(hook.result.current.width).toBe(1200);
act(() => {
hook.result.current.stop();
});
expect(hook.result.current.width).toBe(1200);
window.innerWidth = 1400;
act(() => {
window.dispatchEvent(new Event('resize'));
jest.advanceTimersByTime(250);
});
expect(hook.result.current.width).toBe(1200);
expect(onChange).not.toHaveBeenCalled();
act(() => {
hook.result.current.start();
});
expect(hook.result.current.width).toBe(1200);
act(() => {
window.innerWidth = 1300;
window.dispatchEvent(new Event('resize'));
jest.advanceTimersByTime(250);
});
expect(hook.result.current.width).toBe(1300);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(1300);
hook.unmount();
});
it('should respect startImmediately option', () => {
const nonAutoStartHook = renderHook(() =>
useWindowWidth({
startImmediately: false,
onChange
})
);
expect(nonAutoStartHook.result.current.width).toBe(1200);
expect(onChange).not.toHaveBeenCalled();
Object.defineProperty(window, 'innerWidth', { value: 1500, configurable: true });
act(() => {
document.dispatchEvent(new Event('resize'));
jest.advanceTimersByTime(250);
});
expect(nonAutoStartHook.result.current.width).toBe(1200);
expect(onChange).not.toHaveBeenCalled();
nonAutoStartHook.unmount();
});
it('should cleanup event listeners on unmount', () => {
const hook = renderHook(() => useWindowWidth({ onChange }));
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
hook.unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.any(Function));
removeEventListenerSpy.mockRestore();
});
it('should handle rapid resize events', () => {
const hook = renderHook(() => useWindowWidth({ onChange }));
act(() => {
window.innerWidth = 800;
window.dispatchEvent(new Event('resize'));
window.innerWidth = 900;
window.dispatchEvent(new Event('resize'));
window.innerWidth = 1000;
window.dispatchEvent(new Event('resize'));
jest.advanceTimersByTime(250);
});
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(1000);
expect(hook.result.current.width).toBe(1000);
hook.unmount();
});
});