allinssl/frontend/packages/vue/hooks/test/throttle-fn.spec.js

226 lines
7.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { useThrottleFn } from '../src/throttle-fn'
import { mount } from '@vue/test-utils'
import { ref, nextTick } from 'vue'
describe('useThrottleFn', () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.restoreAllMocks()
vi.useRealTimers()
})
it('应该立即执行第一次调用', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100)
throttled('参数1', '参数2')
expect(mockFn).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenCalledWith('参数1', '参数2')
})
it('在规定延迟内多次调用应该只执行一次', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100)
throttled()
throttled()
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
})
it('延迟时间过后应该能再次执行', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100)
// 第一次调用立即执行
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 延迟内的调用应该被忽略
throttled()
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 前进100ms
vi.advanceTimersByTime(100)
// 延迟后再次调用应该能执行
throttled()
expect(mockFn).toHaveBeenCalledTimes(2)
})
it('应该在节流期间保留最后一次调用,并在延迟后执行', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100)
// 第一次调用立即执行
throttled('第一次')
expect(mockFn).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenLastCalledWith('第一次')
// 前进50ms此时仍在节流期间
vi.advanceTimersByTime(50)
// 节流期间的调用应该被延迟
throttled('第二次')
expect(mockFn).toHaveBeenCalledTimes(1)
// 前进剩余的50ms
vi.advanceTimersByTime(50)
// 延迟后执行最后一次调用
expect(mockFn).toHaveBeenCalledTimes(2)
expect(mockFn).toHaveBeenLastCalledWith('第二次')
})
it('在组件卸载时应该清除定时器', () => {
const mockFn = vi.fn()
// 模拟组件中使用hook
const wrapper = mount({
template: '<div></div>',
setup() {
const throttled = useThrottleFn(mockFn, 100)
// 触发第一次调用
throttled()
// 50ms后再次调用此时在节流期间
setTimeout(() => {
throttled()
}, 50)
return { throttled }
},
})
// 前进50ms触发第二次调用
vi.advanceTimersByTime(50)
expect(mockFn).toHaveBeenCalledTimes(1)
// 卸载组件,此时应该清除定时器
wrapper.unmount()
// 前进剩余的50ms由于组件已卸载定时器应该被清除不会执行第二次调用
vi.advanceTimersByTime(50)
expect(mockFn).toHaveBeenCalledTimes(1)
})
it('应该使用默认延迟时间', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn) // 使用默认延迟200ms
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 前进100ms应该还不会再次调用
vi.advanceTimersByTime(100)
expect(mockFn).toHaveBeenCalledTimes(1)
// 再前进100ms达到默认的200ms
vi.advanceTimersByTime(100)
// 此时已经可以再次调用
throttled()
expect(mockFn).toHaveBeenCalledTimes(2)
})
it('支持leading选项为false首次调用不立即执行', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100, { leading: false })
throttled()
expect(mockFn).not.toHaveBeenCalled()
// 前进100ms首次调用应该被延迟执行
vi.advanceTimersByTime(100)
expect(mockFn).toHaveBeenCalledTimes(1)
})
it('支持trailing选项为false延迟期间最后一次调用不会被保留执行', () => {
const mockFn = vi.fn()
const throttled = useThrottleFn(mockFn, 100, { trailing: false })
// 第一次调用立即执行
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 前进50ms此时仍在节流期间
vi.advanceTimersByTime(50)
// 节流期间的调用不会被保留
throttled()
// 前进剩余的50ms
vi.advanceTimersByTime(50)
// 由于trailing为false最后一次调用不会延迟执行
expect(mockFn).toHaveBeenCalledTimes(1)
})
it('支持在执行期间获取this上下文', () => {
const obj = {
value: '测试值',
method() {
return this.value
},
}
// 监控method方法
const spy = vi.spyOn(obj, 'method')
// 创建节流函数
const throttled = useThrottleFn(obj.method.bind(obj), 100)
// 调用并检查返回值
const result = throttled()
expect(result).toBe('测试值')
expect(spy).toHaveBeenCalledTimes(1)
})
it('支持取消功能,取消后待执行的调用不会被执行', () => {
const mockFn = vi.fn()
const { run, cancel } = useThrottleFn(mockFn, 100)
// 第一次调用立即执行
run()
expect(mockFn).toHaveBeenCalledTimes(1)
// 前进50ms此时仍在节流期间
vi.advanceTimersByTime(50)
// 节流期间的调用应该被延迟
run()
expect(mockFn).toHaveBeenCalledTimes(1)
// 取消待执行的调用
cancel()
// 前进剩余的50ms
vi.advanceTimersByTime(50)
// 由于已取消,最后一次调用不会被执行
expect(mockFn).toHaveBeenCalledTimes(1)
})
it('支持flush功能立即执行待执行的调用', () => {
const mockFn = vi.fn()
const { run, flush } = useThrottleFn(mockFn, 100)
// 第一次调用立即执行
run('初始参数')
expect(mockFn).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenLastCalledWith('初始参数')
// 前进50ms此时仍在节流期间
vi.advanceTimersByTime(50)
// 节流期间的调用应该被延迟
run('待执行参数')
expect(mockFn).toHaveBeenCalledTimes(1)
// 立即执行待执行的调用
flush()
// 待执行的调用应该立即被执行
expect(mockFn).toHaveBeenCalledTimes(2)
expect(mockFn).toHaveBeenLastCalledWith('待执行参数')
})
it('支持动态修改延迟时间', async () => {
const mockFn = vi.fn()
const delay = ref(100)
// 使用响应式延迟时间
const throttled = useThrottleFn(mockFn, delay)
// 第一次调用立即执行
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 修改延迟时间为200ms
delay.value = 200
await nextTick()
// 前进100ms由于延迟已变为200ms还不能执行下一次调用
vi.advanceTimersByTime(100)
throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 再前进100ms总共200ms此时应该可以执行下一次调用
vi.advanceTimersByTime(100)
throttled()
expect(mockFn).toHaveBeenCalledTimes(2)
})
it('支持节流函数返回Promise', async () => {
const mockFn = vi.fn().mockResolvedValue('结果')
const throttled = useThrottleFn(mockFn, 100)
// 调用并等待Promise解析
const promise = throttled()
expect(mockFn).toHaveBeenCalledTimes(1)
// 验证Promise解析结果
const result = await promise
expect(result).toBe('结果')
})
it('在微任务队列中执行,保持事件顺序', async () => {
// 记录事件顺序
const events = []
const mockFn = vi.fn(() => {
events.push('函数执行')
})
const throttled = useThrottleFn(mockFn, 0)
events.push('调用前')
throttled()
events.push('调用后')
// 等待微任务队列完成
await Promise.resolve()
// 验证事件顺序即使延迟为0也应该在当前事件循环结束后执行
expect(events).toEqual(['调用前', '函数执行', '调用后'])
})
})
//# sourceMappingURL=throttle-fn.spec.js.map