Skip to content

Commit a3d475d

Browse files
committed
feat: add tests
1 parent d863244 commit a3d475d

3 files changed

Lines changed: 289 additions & 0 deletions

File tree

test/mockAnimationFrame.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
class MockAnimationFrame {
2+
private originalRequestAnimationFrame: (callback: FrameRequestCallback) => number
3+
private originalCancelAnimationFrame: (handle: number) => void
4+
private queued: { [key: number]: FrameRequestCallback } = {}
5+
private currentAnimationFrame = 0
6+
7+
public mock(): void {
8+
this.originalRequestAnimationFrame = window.requestAnimationFrame
9+
window.requestAnimationFrame = this.requestAnimationFrame
10+
11+
this.originalCancelAnimationFrame = window.cancelAnimationFrame
12+
window.cancelAnimationFrame = this.cancelAnimationFrame
13+
}
14+
15+
public restore(): void {
16+
window.requestAnimationFrame = this.originalRequestAnimationFrame
17+
window.cancelAnimationFrame = this.originalCancelAnimationFrame
18+
}
19+
20+
public async runFrame(): Promise<void> {
21+
for await (const frame of Object.keys(this.queued)) {
22+
const callback = this.queued[frame]
23+
delete this.queued[frame]
24+
await callback(Date.now())
25+
}
26+
}
27+
28+
private requestAnimationFrame = (callback: FrameRequestCallback): number => {
29+
this.currentAnimationFrame += 1
30+
this.queued[this.currentAnimationFrame] = callback
31+
return this.currentAnimationFrame
32+
}
33+
34+
private cancelAnimationFrame = (frame: number): void => {
35+
delete this.queued[frame]
36+
}
37+
}
38+
39+
export const mockAnimationFrame = new MockAnimationFrame()

test/usePolling.test.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { act, renderHook } from '@testing-library/react'
2+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
3+
import { usePolling } from '../src/usePolling'
4+
import { mockAnimationFrame } from './mockAnimationFrame'
5+
6+
describe('usePolling', () => {
7+
beforeAll(() => {
8+
mockAnimationFrame.mock()
9+
})
10+
11+
afterAll(() => {
12+
mockAnimationFrame.restore()
13+
})
14+
15+
it('should run automatically on render.', () => {
16+
const mockFn = vi.fn(() => Promise.resolve())
17+
const { result } = renderHook(() => usePolling(mockFn, 1000))
18+
19+
expect(result.current.isPaused).toBeFalsy()
20+
})
21+
22+
it('should bew paused by executing pause().', () => {
23+
const mockFn = vi.fn(() => Promise.resolve())
24+
const { result } = renderHook(() => usePolling(mockFn, 1000))
25+
26+
act(() => {
27+
result.current.pause()
28+
})
29+
30+
expect(result.current.isPaused).toBeTruthy()
31+
})
32+
33+
it('should be resumed by executing resume() during pause.', () => {
34+
const mockFn = vi.fn(() => Promise.resolve())
35+
const { result } = renderHook(() => usePolling(mockFn, 1000))
36+
37+
act(() => {
38+
result.current.pause()
39+
})
40+
expect(result.current.isPaused).toBeTruthy()
41+
42+
act(() => {
43+
result.current.resume()
44+
})
45+
expect(result.current.isPaused).toBeFalsy()
46+
})
47+
48+
it('should not execute callback right after render.', () => {
49+
const mockFn = vi.fn(() => Promise.resolve())
50+
renderHook(() => usePolling(mockFn, 1000))
51+
52+
expect(mockFn).not.toBeCalled()
53+
})
54+
55+
it('should execute the callback after the specified timeout.', async () => {
56+
const INTERVAL = 1
57+
vi.useFakeTimers()
58+
const mockFn = vi.fn(() => Promise.resolve())
59+
renderHook(() => usePolling(mockFn, INTERVAL))
60+
61+
const interval = async (): Promise<void> => {
62+
await act(async () => {
63+
await mockAnimationFrame.runFrame()
64+
vi.advanceTimersByTime(INTERVAL)
65+
})
66+
}
67+
68+
for (let i = 1; i < 10; i++) {
69+
await interval()
70+
await act(async () => {
71+
await mockAnimationFrame.runFrame()
72+
vi.advanceTimersByTime(INTERVAL)
73+
})
74+
expect(mockFn).toHaveBeenCalledTimes(i)
75+
}
76+
})
77+
78+
it('should not execute the callback if it is paused.', async () => {
79+
const INTERVAL = 1
80+
vi.useFakeTimers()
81+
const mockFn = vi.fn(() => Promise.resolve())
82+
const { result } = renderHook(() => usePolling(mockFn, INTERVAL))
83+
84+
act(() => {
85+
result.current.pause()
86+
})
87+
88+
const interval = async (): Promise<void> => {
89+
await act(async () => {
90+
await mockAnimationFrame.runFrame()
91+
vi.advanceTimersByTime(INTERVAL)
92+
})
93+
}
94+
95+
for (let i = 1; i < 5; i++) {
96+
await interval()
97+
await act(async () => {
98+
await mockAnimationFrame.runFrame()
99+
vi.advanceTimersByTime(INTERVAL)
100+
})
101+
expect(mockFn).toHaveBeenCalledTimes(0)
102+
}
103+
104+
act(() => {
105+
result.current.resume()
106+
})
107+
108+
for (let i = 1; i < 5; i++) {
109+
await interval()
110+
await act(async () => {
111+
await mockAnimationFrame.runFrame()
112+
vi.advanceTimersByTime(INTERVAL)
113+
})
114+
expect(mockFn).toHaveBeenCalledTimes(i)
115+
}
116+
})
117+
118+
it('should run onError() on error in callback.', async () => {
119+
const INTERVAL = 1
120+
vi.useFakeTimers()
121+
const mockFn = vi.fn(() => Promise.reject())
122+
const mockErrorFn = vi.fn()
123+
renderHook(() =>
124+
usePolling(mockFn, INTERVAL, {
125+
onError: mockErrorFn,
126+
})
127+
)
128+
129+
const interval = async (): Promise<void> => {
130+
await act(async () => {
131+
await mockAnimationFrame.runFrame()
132+
vi.advanceTimersByTime(INTERVAL)
133+
})
134+
}
135+
136+
await interval()
137+
await act(async () => {
138+
await mockAnimationFrame.runFrame()
139+
vi.advanceTimersByTime(INTERVAL)
140+
})
141+
142+
expect(mockErrorFn).toHaveBeenCalledTimes(1)
143+
})
144+
})

test/usePollingForce.test.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { act, renderHook } from '@testing-library/react'
2+
import { describe, expect, it, vi } from 'vitest'
3+
import { usePollingForce } from '../src/usePollingForce'
4+
5+
describe('usePolling', () => {
6+
it('should run automatically on render.', () => {
7+
const mockFn = vi.fn(() => Promise.resolve())
8+
const { result } = renderHook(() => usePollingForce(mockFn, 1000))
9+
10+
expect(result.current.isPaused).toBeFalsy()
11+
})
12+
13+
it('should bew paused by executing pause().', () => {
14+
const mockFn = vi.fn(() => Promise.resolve())
15+
const { result } = renderHook(() => usePollingForce(mockFn, 1000))
16+
17+
act(() => {
18+
result.current.pause()
19+
})
20+
21+
expect(result.current.isPaused).toBeTruthy()
22+
})
23+
24+
it('should be resumed by executing resume() during pause.', () => {
25+
const mockFn = vi.fn(() => Promise.resolve())
26+
const { result } = renderHook(() => usePollingForce(mockFn, 1000))
27+
28+
act(() => {
29+
result.current.pause()
30+
})
31+
expect(result.current.isPaused).toBeTruthy()
32+
33+
act(() => {
34+
result.current.resume()
35+
})
36+
expect(result.current.isPaused).toBeFalsy()
37+
})
38+
39+
it('should not execute callback right after render.', () => {
40+
const mockFn = vi.fn(() => Promise.resolve())
41+
renderHook(() => usePollingForce(mockFn, 1000))
42+
43+
expect(mockFn).not.toBeCalled()
44+
})
45+
46+
it('should execute the callback after the specified timeout.', () => {
47+
const INTERVAL = 1
48+
vi.useFakeTimers()
49+
const mockFn = vi.fn(() => Promise.resolve())
50+
renderHook(() => usePollingForce(mockFn, INTERVAL))
51+
52+
for (let i = 1; i < 10; i++) {
53+
act(() => {
54+
vi.advanceTimersToNextTimer()
55+
})
56+
expect(mockFn).toHaveBeenCalledTimes(i)
57+
}
58+
})
59+
60+
it('should not execute the callback if it is paused.', () => {
61+
const INTERVAL = 1
62+
vi.useFakeTimers()
63+
const mockFn = vi.fn(() => Promise.resolve())
64+
const { result } = renderHook(() => usePollingForce(mockFn, INTERVAL))
65+
66+
act(() => {
67+
result.current.pause()
68+
})
69+
70+
for (let i = 1; i < 5; i++) {
71+
act(() => {
72+
vi.advanceTimersToNextTimer()
73+
})
74+
expect(mockFn).toHaveBeenCalledTimes(0)
75+
}
76+
77+
act(() => {
78+
result.current.resume()
79+
})
80+
81+
for (let i = 1; i < 5; i++) {
82+
act(() => {
83+
vi.advanceTimersToNextTimer()
84+
})
85+
expect(mockFn).toHaveBeenCalledTimes(i)
86+
}
87+
})
88+
89+
it('should run onError() on error in callback.', async () => {
90+
const INTERVAL = 1
91+
vi.useFakeTimers()
92+
const mockFn = vi.fn(() => Promise.reject())
93+
const mockErrorFn = vi.fn()
94+
renderHook(() =>
95+
usePollingForce(mockFn, INTERVAL, {
96+
onError: mockErrorFn,
97+
})
98+
)
99+
100+
await act(async () => {
101+
vi.advanceTimersToNextTimer()
102+
})
103+
104+
expect(mockErrorFn).toHaveBeenCalledTimes(1)
105+
})
106+
})

0 commit comments

Comments
 (0)