@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
135 lines (101 loc) • 4.52 kB
text/typescript
import { act, renderHook } from '@testing-library/react';
import { useVisibilityChange } from '../src/hooks/useVisibilityChange';
describe('useVisibilityChange', () => {
let onVisible: jest.Mock;
let onHidden: jest.Mock;
beforeEach(() => {
onVisible = jest.fn();
onHidden = jest.fn();
Object.defineProperty(document, 'hidden', { value: false, configurable: true });
});
afterEach(() => {
jest.clearAllMocks();
Object.defineProperty(document, 'hidden', { value: false, configurable: true });
});
it('should initialize with correct visibility state', () => {
const hook = renderHook(() => useVisibilityChange({ onVisible, onHidden }));
expect(hook.result.current.isVisible).toBe(!document.hidden);
expect(onVisible).not.toHaveBeenCalled();
expect(onHidden).not.toHaveBeenCalled();
hook.unmount();
});
it('should handle visibility changes', () => {
const hook = renderHook(() => useVisibilityChange({ onVisible, onHidden }));
expect(hook.result.current.isVisible).toBe(true);
expect(onVisible).not.toHaveBeenCalled();
expect(onHidden).not.toHaveBeenCalled();
act(() => {
Object.defineProperty(document, 'hidden', { value: true, configurable: true });
document.dispatchEvent(new Event('visibilitychange'));
});
expect(hook.result.current.isVisible).toBe(false);
expect(onHidden).toHaveBeenCalledTimes(1);
expect(onVisible).not.toHaveBeenCalled();
act(() => {
Object.defineProperty(document, 'hidden', { value: false, configurable: true });
document.dispatchEvent(new Event('visibilitychange'));
});
expect(hook.result.current.isVisible).toBe(true);
expect(onVisible).toHaveBeenCalledTimes(1);
expect(onHidden).toHaveBeenCalledTimes(1);
hook.unmount();
});
it('should handle start/stop monitoring', () => {
const hook = renderHook(() => useVisibilityChange({ onVisible, onHidden }));
expect(hook.result.current.isVisible).toBe(true);
act(() => {
hook.result.current.stop();
});
act(() => {
Object.defineProperty(document, 'hidden', { value: true, configurable: true });
document.dispatchEvent(new Event('visibilitychange'));
});
expect(onHidden).not.toHaveBeenCalled();
expect(onVisible).not.toHaveBeenCalled();
expect(hook.result.current.isVisible).toBe(true);
act(() => {
hook.result.current.start();
});
act(() => {
Object.defineProperty(document, 'hidden', { value: true, configurable: true });
document.dispatchEvent(new Event('visibilitychange'));
});
expect(hook.result.current.isVisible).toBe(false);
expect(onHidden).toHaveBeenCalledTimes(1);
hook.unmount();
});
it('should respect startImmediately option', () => {
const nonAutoStartHook = renderHook(() =>
useVisibilityChange({
onVisible,
onHidden,
startImmediately: false
})
);
expect(nonAutoStartHook.result.current.isVisible).toBe(true);
act(() => {
Object.defineProperty(document, 'hidden', { value: true, configurable: true });
document.dispatchEvent(new Event('visibilitychange'));
});
expect(onVisible).not.toHaveBeenCalled();
expect(onHidden).not.toHaveBeenCalled();
expect(nonAutoStartHook.result.current.isVisible).toBe(true);
act(() => {
nonAutoStartHook.result.current.start();
});
expect(nonAutoStartHook.result.current.isVisible).toBe(true);
act(() => {
document.dispatchEvent(new Event('visibilitychange'));
});
expect(nonAutoStartHook.result.current.isVisible).toBe(false);
expect(onHidden).toHaveBeenCalledTimes(1);
nonAutoStartHook.unmount();
});
it('should cleanup event listeners on unmount', () => {
const hook = renderHook(() => useVisibilityChange({ onVisible, onHidden }));
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
hook.unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function));
removeEventListenerSpy.mockRestore();
});
});