Skip to content

Commit c722577

Browse files
committed
test: onWindowFocus 添加单元测试
1 parent dddcf6c commit c722577

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/**
2+
* @vitest-environment happy-dom
3+
*/
4+
import { describe, expect, it, afterEach, beforeEach, vi } from 'vitest'
5+
6+
import { onWindowFocus } from './onWindowFocus'
7+
8+
describe('onWindowFocus', () => {
9+
let mockCallback: ReturnType<typeof vi.fn>
10+
let addEventListenerSpy: ReturnType<typeof vi.spyOn>
11+
let removeEventListenerSpy: ReturnType<typeof vi.spyOn>
12+
13+
beforeEach(() => {
14+
mockCallback = vi.fn()
15+
addEventListenerSpy = vi.spyOn(window, 'addEventListener')
16+
removeEventListenerSpy = vi.spyOn(window, 'removeEventListener')
17+
})
18+
19+
afterEach(() => {
20+
vi.restoreAllMocks()
21+
vi.clearAllTimers()
22+
})
23+
24+
it('should add event listeners for focus and visibilitychange', () => {
25+
/* 测试事件监听器是否正确添加 */
26+
onWindowFocus(mockCallback)
27+
28+
expect(addEventListenerSpy).toHaveBeenCalledWith('focus', expect.any(Function), false)
29+
expect(addEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function), false)
30+
expect(addEventListenerSpy).toHaveBeenCalledTimes(2)
31+
})
32+
33+
it('should execute callback when window focus event is triggered', () => {
34+
/* 测试 focus 事件触发回调 */
35+
vi.useFakeTimers()
36+
onWindowFocus(mockCallback)
37+
38+
/* 触发 focus 事件 */
39+
window.dispatchEvent(new Event('focus'))
40+
41+
/* 等待 debounce 延迟 */
42+
vi.advanceTimersByTime(100)
43+
44+
expect(mockCallback).toHaveBeenCalledTimes(1)
45+
})
46+
47+
it('should execute callback when document becomes visible', () => {
48+
/* 测试文档可见时的 visibilitychange 事件 */
49+
vi.useFakeTimers()
50+
onWindowFocus(mockCallback)
51+
52+
/* 设置文档为可见状态 */
53+
Object.defineProperty(document, 'visibilityState', {
54+
value: 'visible',
55+
writable: true,
56+
})
57+
58+
/* 触发 visibilitychange 事件 - 在 window 对象上触发 */
59+
window.dispatchEvent(new Event('visibilitychange'))
60+
61+
/* 等待 debounce 延迟 */
62+
vi.advanceTimersByTime(100)
63+
64+
expect(mockCallback).toHaveBeenCalledTimes(1)
65+
})
66+
67+
it('should not execute callback when document is hidden', () => {
68+
/* 测试文档隐藏时不触发回调 */
69+
vi.useFakeTimers()
70+
onWindowFocus(mockCallback)
71+
72+
/* 设置文档为隐藏状态 */
73+
Object.defineProperty(document, 'visibilityState', {
74+
value: 'hidden',
75+
writable: true,
76+
})
77+
78+
/* 触发 visibilitychange 事件 - 在 window 对象上触发 */
79+
window.dispatchEvent(new Event('visibilitychange'))
80+
81+
/* 等待 debounce 延迟 */
82+
vi.advanceTimersByTime(100)
83+
84+
expect(mockCallback).not.toHaveBeenCalled()
85+
})
86+
87+
it('should debounce multiple rapid focus events', () => {
88+
/* 测试防抖功能 */
89+
vi.useFakeTimers()
90+
onWindowFocus(mockCallback)
91+
92+
/* 快速触发多次 focus 事件 */
93+
window.dispatchEvent(new Event('focus'))
94+
window.dispatchEvent(new Event('focus'))
95+
window.dispatchEvent(new Event('focus'))
96+
97+
/* 在防抖时间内,回调不应该被执行 */
98+
vi.advanceTimersByTime(50)
99+
expect(mockCallback).not.toHaveBeenCalled()
100+
101+
/* 等待完整的防抖延迟后,回调应该只被执行一次 */
102+
vi.advanceTimersByTime(50)
103+
expect(mockCallback).toHaveBeenCalledTimes(1)
104+
})
105+
106+
it('should return a cleanup function that removes event listeners', () => {
107+
/* 测试清理函数正确移除事件监听器 */
108+
const cleanup = onWindowFocus(mockCallback)
109+
110+
/* 执行清理函数 */
111+
cleanup()
112+
113+
expect(removeEventListenerSpy).toHaveBeenCalledWith('focus', expect.any(Function))
114+
expect(removeEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function))
115+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2)
116+
})
117+
118+
it('should not execute callback after cleanup', () => {
119+
/* 测试清理后不再执行回调 */
120+
vi.useFakeTimers()
121+
const cleanup = onWindowFocus(mockCallback)
122+
123+
/* 执行清理 */
124+
cleanup()
125+
126+
/* 触发事件 */
127+
window.dispatchEvent(new Event('focus'))
128+
vi.advanceTimersByTime(100)
129+
130+
expect(mockCallback).not.toHaveBeenCalled()
131+
})
132+
133+
it('should handle multiple callback arguments', () => {
134+
/* 测试回调函数接收多个参数 */
135+
vi.useFakeTimers()
136+
const mockCallbackWithArgs = vi.fn((...args: any[]) => args)
137+
onWindowFocus(mockCallbackWithArgs)
138+
139+
window.dispatchEvent(new Event('focus'))
140+
vi.advanceTimersByTime(100)
141+
142+
expect(mockCallbackWithArgs).toHaveBeenCalledTimes(1)
143+
})
144+
145+
it('should work with async callbacks', async () => {
146+
/* 测试异步回调函数 */
147+
vi.useFakeTimers()
148+
const asyncCallback = vi.fn(async () => {
149+
await new Promise(resolve => setTimeout(resolve, 10))
150+
})
151+
152+
onWindowFocus(asyncCallback)
153+
154+
window.dispatchEvent(new Event('focus'))
155+
vi.advanceTimersByTime(100)
156+
157+
expect(asyncCallback).toHaveBeenCalledTimes(1)
158+
})
159+
160+
it('should handle visibility state edge cases', () => {
161+
/* 测试 visibilityState 边界情况 */
162+
vi.useFakeTimers()
163+
onWindowFocus(mockCallback)
164+
165+
/* 测试 prerender 状态 */
166+
Object.defineProperty(document, 'visibilityState', {
167+
value: 'prerender',
168+
writable: true,
169+
})
170+
171+
window.dispatchEvent(new Event('visibilitychange'))
172+
vi.advanceTimersByTime(100)
173+
174+
expect(mockCallback).not.toHaveBeenCalled()
175+
})
176+
})

0 commit comments

Comments
 (0)