mirror of https://github.com/allinssl/allinssl
226 lines
7.4 KiB
JavaScript
226 lines
7.4 KiB
JavaScript
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
|