import { effectScope, nextTick, ref } from 'vue' import { afterEach, describe, expect, it, vi } from 'vitest' import { usePolling } from '@/composables/usePolling' afterEach(() => vi.useRealTimers()) describe('异步轮询生命周期', () => { it('关闭定时刷新后仍支持首次读取、手动刷新及查询目标切换', async () => { vi.useFakeTimers() const key = ref('first') const loader = vi.fn<(id: string) => Promise>(async id => id) const scope = effectScope() const query = scope.run(() => usePolling(key, loader, false))! await Promise.resolve() expect(query.data.value).toBe('first') await vi.advanceTimersByTimeAsync(30_000) expect(loader).toHaveBeenCalledTimes(1) await query.refresh() expect(loader).toHaveBeenCalledTimes(2) key.value = 'second' await nextTick() await Promise.resolve() expect(query.data.value).toBe('second') await vi.advanceTimersByTimeAsync(30_000) expect(loader).toHaveBeenCalledTimes(3) scope.stop() }) it('响应式暂停清除旧定时器,恢复只创建一个轮询链', async () => { vi.useFakeTimers() const interval = ref(1000) const loader = vi.fn<() => Promise>(async () => '数据') const scope = effectScope() scope.run(() => usePolling(ref('p'), loader, interval)) await Promise.resolve() interval.value = false await nextTick() await vi.advanceTimersByTimeAsync(5000) expect(loader).toHaveBeenCalledTimes(1) interval.value = 1000 await nextTick() await vi.advanceTimersByTimeAsync(2000) expect(loader).toHaveBeenCalledTimes(3) scope.stop() await vi.advanceTimersByTimeAsync(5000) expect(loader).toHaveBeenCalledTimes(3) }) it('切换项目忽略旧响应,卸载时不再排队请求', async () => { vi.useFakeTimers() const key = ref('first') const finish = new Map void>() const loader = vi.fn<(id: string) => Promise>( (id: string) => new Promise(resolve => { finish.set(id, resolve) }) ) const scope = effectScope() const query = scope.run(() => usePolling(key, loader, 1000))! key.value = 'second' await nextTick() finish.get('second')!('新项目') await Promise.resolve() finish.get('first')!('旧项目') await Promise.resolve() expect(query.data.value).toBe('新项目') scope.stop() await vi.advanceTimersByTimeAsync(10_000) expect(loader).toHaveBeenCalledTimes(2) }) it('失败时保留已有数据,并向页面显示错误', async () => { const scope = effectScope() const loader = vi .fn<() => Promise>() .mockResolvedValueOnce('上次成功数据') .mockRejectedValueOnce(new Error('连接已断开')) const query = scope.run(() => usePolling(ref('p'), loader))! await Promise.resolve() await query.refresh() expect(query.data.value).toBe('上次成功数据') expect(query.error.value).toBe('连接已断开') scope.stop() }) })