Files
syndarix/frontend/tests/hooks/usePrefersReducedMotion.test.ts
Felipe Cardoso b2f3ec8f25 Refactor ESLint configuration and update test rules for clarity and consistency
- Consolidated and modularized `eslint.config.mjs` with defined rules for source, test, E2E, and scripts.
- Improved test and E2E rules with relaxed settings for flexibility and enhanced mocking.
- Standardized variable naming and removed redundant imports in unit and E2E tests.
- Updated error handling and comments to align with modern TypeScript best practices (e.g., `@ts-expect-error`).
2025-11-10 10:57:43 +01:00

179 lines
5.0 KiB
TypeScript

/**
* Tests for usePrefersReducedMotion hook
* Tests media query detection for accessibility preferences
*/
import { renderHook, act } from '@testing-library/react';
import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion';
describe('usePrefersReducedMotion', () => {
let mockMatchMedia: jest.Mock;
let mockListeners: ((event: MediaQueryListEvent) => void)[];
beforeEach(() => {
mockListeners = [];
mockMatchMedia = jest.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
if (event === 'change') {
mockListeners.push(listener);
}
}),
removeEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
if (event === 'change') {
const index = mockListeners.indexOf(listener);
if (index > -1) {
mockListeners.splice(index, 1);
}
}
}),
dispatchEvent: jest.fn(),
}));
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: mockMatchMedia,
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('returns false when user does not prefer reduced motion', () => {
mockMatchMedia.mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
}));
const { result } = renderHook(() => usePrefersReducedMotion());
expect(result.current).toBe(false);
expect(mockMatchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)');
});
it('returns true when user prefers reduced motion', () => {
mockMatchMedia.mockImplementation((query: string) => ({
matches: true,
media: query,
onchange: null,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
}));
const { result } = renderHook(() => usePrefersReducedMotion());
expect(result.current).toBe(true);
});
it('updates when media query preference changes to true', () => {
const mockMediaQuery = {
matches: false,
media: '(prefers-reduced-motion: reduce)',
onchange: null,
addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
if (event === 'change') {
mockListeners.push(listener);
}
}),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
};
mockMatchMedia.mockReturnValue(mockMediaQuery);
const { result } = renderHook(() => usePrefersReducedMotion());
expect(result.current).toBe(false);
// Simulate media query change
act(() => {
mockListeners.forEach(listener => {
listener({ matches: true } as MediaQueryListEvent);
});
});
expect(result.current).toBe(true);
});
it('updates when media query preference changes to false', () => {
const mockMediaQuery = {
matches: true,
media: '(prefers-reduced-motion: reduce)',
onchange: null,
addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
if (event === 'change') {
mockListeners.push(listener);
}
}),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
};
mockMatchMedia.mockReturnValue(mockMediaQuery);
const { result } = renderHook(() => usePrefersReducedMotion());
expect(result.current).toBe(true);
// Simulate media query change
act(() => {
mockListeners.forEach(listener => {
listener({ matches: false } as MediaQueryListEvent);
});
});
expect(result.current).toBe(false);
});
it('cleans up event listener on unmount', () => {
const removeEventListenerSpy = jest.fn();
const mockMediaQuery = {
matches: false,
media: '(prefers-reduced-motion: reduce)',
onchange: null,
addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
if (event === 'change') {
mockListeners.push(listener);
}
}),
removeEventListener: removeEventListenerSpy,
dispatchEvent: jest.fn(),
};
mockMatchMedia.mockReturnValue(mockMediaQuery);
const { unmount } = renderHook(() => usePrefersReducedMotion());
expect(mockMediaQuery.addEventListener).toHaveBeenCalledWith('change', expect.any(Function));
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('change', expect.any(Function));
});
it('handles SSR environment safely', () => {
const originalWindow = global.window;
// @ts-expect-error - Simulating SSR
delete global.window;
const { result } = renderHook(() => usePrefersReducedMotion());
// Should return false in SSR environment
expect(result.current).toBe(false);
// Restore window
global.window = originalWindow;
});
});