Skip to content

Commit b1d644f

Browse files
committed
test: add unit tests for usePointerFine hook
1 parent efc55c4 commit b1d644f

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { renderHook, act } from '@testing-library/react';
2+
import { usePointerFine } from '../../hooks/usePointerFine';
3+
4+
describe('usePointerFine', () => {
5+
let mockMatchMedia: jest.Mock;
6+
let mediaQueryListMock: {
7+
matches: boolean;
8+
media: string;
9+
addEventListener: jest.Mock;
10+
removeEventListener: jest.Mock;
11+
onchange: null;
12+
addListener: jest.Mock;
13+
removeListener: jest.Mock;
14+
dispatchEvent: jest.Mock;
15+
};
16+
17+
beforeEach(() => {
18+
mediaQueryListMock = {
19+
matches: false,
20+
media: '(pointer: fine)',
21+
addEventListener: jest.fn(),
22+
removeEventListener: jest.fn(),
23+
onchange: null,
24+
addListener: jest.fn(),
25+
removeListener: jest.fn(),
26+
dispatchEvent: jest.fn(),
27+
};
28+
29+
mockMatchMedia = jest.fn().mockImplementation((query: string) => {
30+
mediaQueryListMock.media = query;
31+
return mediaQueryListMock;
32+
});
33+
34+
Object.defineProperty(window, 'matchMedia', {
35+
writable: true,
36+
enumerable: true,
37+
configurable: true,
38+
value: mockMatchMedia,
39+
});
40+
41+
// Mock resize and orientationchange events
42+
window.addEventListener = jest.fn();
43+
window.removeEventListener = jest.fn();
44+
});
45+
46+
afterEach(() => {
47+
jest.clearAllMocks();
48+
});
49+
50+
it('should return true when fine pointer is detected', () => {
51+
mediaQueryListMock.matches = true;
52+
53+
const { result } = renderHook(() => usePointerFine());
54+
55+
expect(mockMatchMedia).toHaveBeenCalledWith('(pointer: fine)');
56+
expect(result.current).toBe(true);
57+
});
58+
59+
it('should return false when fine pointer is not detected', () => {
60+
mediaQueryListMock.matches = false;
61+
62+
const { result } = renderHook(() => usePointerFine());
63+
64+
expect(result.current).toBe(false);
65+
});
66+
67+
it('should return false when window is undefined', () => {
68+
const originalWindow = globalThis.window;
69+
// @ts-expect-error - intentionally undefined for testing
70+
globalThis.window = undefined;
71+
72+
const { result } = renderHook(() => usePointerFine());
73+
74+
expect(result.current).toBe(false);
75+
76+
globalThis.window = originalWindow;
77+
});
78+
79+
it('should add event listeners for change, resize, and orientationchange', () => {
80+
renderHook(() => usePointerFine());
81+
82+
expect(mediaQueryListMock.addEventListener).toHaveBeenCalledWith(
83+
'change',
84+
expect.any(Function),
85+
);
86+
expect(window.addEventListener).toHaveBeenCalledWith(
87+
'resize',
88+
expect.any(Function),
89+
);
90+
expect(window.addEventListener).toHaveBeenCalledWith(
91+
'orientationchange',
92+
expect.any(Function),
93+
);
94+
});
95+
96+
it('should update when change event fires', () => {
97+
const { result } = renderHook(() => usePointerFine());
98+
99+
// Initially false
100+
expect(result.current).toBe(false);
101+
102+
// Simulate media query change to true
103+
mediaQueryListMock.matches = true;
104+
const changeHandler = mediaQueryListMock.addEventListener.mock.calls.find(
105+
([event]) => event === 'change',
106+
)?.[1];
107+
108+
act(() => {
109+
changeHandler?.();
110+
});
111+
112+
expect(result.current).toBe(true);
113+
});
114+
115+
it('should update when resize event fires', () => {
116+
const { result } = renderHook(() => usePointerFine());
117+
118+
// Initially false
119+
expect(result.current).toBe(false);
120+
121+
// Simulate media query change to true on resize
122+
mediaQueryListMock.matches = true;
123+
const resizeHandler = (
124+
window.addEventListener as jest.Mock
125+
).mock.calls.find(([event]) => event === 'resize')?.[1];
126+
127+
act(() => {
128+
resizeHandler?.();
129+
});
130+
131+
expect(result.current).toBe(true);
132+
});
133+
134+
it('should update when orientationchange event fires', () => {
135+
const { result } = renderHook(() => usePointerFine());
136+
137+
// Initially false
138+
expect(result.current).toBe(false);
139+
140+
// Simulate media query change to true on orientation change
141+
mediaQueryListMock.matches = true;
142+
const orientationHandler = (
143+
window.addEventListener as jest.Mock
144+
).mock.calls.find(([event]) => event === 'orientationchange')?.[1];
145+
146+
act(() => {
147+
orientationHandler?.();
148+
});
149+
150+
expect(result.current).toBe(true);
151+
});
152+
153+
it('should cleanup event listeners on unmount', () => {
154+
const { unmount } = renderHook(() => usePointerFine());
155+
156+
unmount();
157+
158+
const changeHandler = mediaQueryListMock.addEventListener.mock.calls.find(
159+
([event]) => event === 'change',
160+
)?.[1];
161+
const resizeHandler = (
162+
window.addEventListener as jest.Mock
163+
).mock.calls.find(([event]) => event === 'resize')?.[1];
164+
const orientationHandler = (
165+
window.addEventListener as jest.Mock
166+
).mock.calls.find(([event]) => event === 'orientationchange')?.[1];
167+
168+
expect(mediaQueryListMock.removeEventListener).toHaveBeenCalledWith(
169+
'change',
170+
changeHandler,
171+
);
172+
expect(window.removeEventListener).toHaveBeenCalledWith(
173+
'resize',
174+
resizeHandler,
175+
);
176+
expect(window.removeEventListener).toHaveBeenCalledWith(
177+
'orientationchange',
178+
orientationHandler,
179+
);
180+
});
181+
182+
it('should call handlePointerChange on initial render', () => {
183+
mediaQueryListMock.matches = true;
184+
185+
const { result } = renderHook(() => usePointerFine());
186+
187+
// Verify the initial state handler was called
188+
mediaQueryListMock.addEventListener.mock.calls.find(
189+
([event]) => event === 'change',
190+
)?.[1];
191+
192+
// We can't easily test the initial call to handlePointerChange
193+
// but we can verify the hook returns the correct initial value
194+
expect(result.current).toBe(true);
195+
});
196+
});

0 commit comments

Comments
 (0)