【初始化】前端工程项目

This commit is contained in:
chudong
2025-05-09 15:11:21 +08:00
parent c012704c9a
commit d7c556c3b0
524 changed files with 55595 additions and 112 deletions

View File

@@ -0,0 +1,225 @@
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