117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { ref } from 'vue'
|
|
import { createVisibilityPoller, type PollingHost } from './useVisibilityPolling'
|
|
|
|
function fakeHost() {
|
|
let hidden = false
|
|
let intervalFn: (() => void) | null = null
|
|
let intervalId = 0
|
|
const listeners: Array<() => void> = []
|
|
const host: PollingHost = {
|
|
setInterval: (fn) => {
|
|
intervalFn = fn
|
|
return ++intervalId
|
|
},
|
|
clearInterval: () => {
|
|
intervalFn = null
|
|
},
|
|
isHidden: () => hidden,
|
|
addVisibilityListener: (fn) => {
|
|
listeners.push(fn)
|
|
},
|
|
removeVisibilityListener: (fn) => {
|
|
const index = listeners.indexOf(fn)
|
|
if (index >= 0) listeners.splice(index, 1)
|
|
},
|
|
}
|
|
return {
|
|
host,
|
|
setHidden: (value: boolean) => {
|
|
hidden = value
|
|
},
|
|
fireInterval: () => intervalFn?.(),
|
|
fireVisibility: () => listeners.forEach((fn) => fn()),
|
|
hasTimer: () => intervalFn !== null,
|
|
listenerCount: () => listeners.length,
|
|
}
|
|
}
|
|
|
|
describe('createVisibilityPoller', () => {
|
|
it('页面隐藏时不执行', async () => {
|
|
const h = fakeHost()
|
|
let calls = 0
|
|
const poller = createVisibilityPoller(() => { calls++ }, 1000, {}, h.host)
|
|
h.setHidden(true)
|
|
await poller.tick()
|
|
expect(calls).toBe(0)
|
|
})
|
|
|
|
it('回到前台立即补一次', async () => {
|
|
const h = fakeHost()
|
|
let calls = 0
|
|
const poller = createVisibilityPoller(() => { calls++ }, 1000, {}, h.host)
|
|
poller.attach()
|
|
h.fireVisibility()
|
|
await Promise.resolve()
|
|
expect(calls).toBe(1)
|
|
})
|
|
|
|
it('上一轮未结束时跳过下一轮', async () => {
|
|
const h = fakeHost()
|
|
let calls = 0
|
|
let release: () => void = () => {}
|
|
const pending = new Promise<void>((resolve) => { release = resolve })
|
|
const poller = createVisibilityPoller(() => {
|
|
calls++
|
|
return calls === 1 ? pending : Promise.resolve()
|
|
}, 1000, {}, h.host)
|
|
const first = poller.tick()
|
|
await Promise.resolve()
|
|
await poller.tick()
|
|
expect(calls).toBe(1)
|
|
release()
|
|
await first
|
|
await poller.tick()
|
|
expect(calls).toBe(2)
|
|
})
|
|
|
|
it('enabled=false 时不执行', async () => {
|
|
const h = fakeHost()
|
|
const enabled = ref(false)
|
|
let calls = 0
|
|
const poller = createVisibilityPoller(() => { calls++ }, 1000, { enabled }, h.host)
|
|
await poller.tick()
|
|
expect(calls).toBe(0)
|
|
enabled.value = true
|
|
await poller.tick()
|
|
expect(calls).toBe(1)
|
|
})
|
|
|
|
it('卸载后不再保留计时器与监听', () => {
|
|
const h = fakeHost()
|
|
const poller = createVisibilityPoller(() => {}, 1000, {}, h.host)
|
|
poller.attach()
|
|
expect(h.hasTimer()).toBe(true)
|
|
expect(h.listenerCount()).toBe(1)
|
|
poller.detach()
|
|
expect(h.hasTimer()).toBe(false)
|
|
expect(h.listenerCount()).toBe(0)
|
|
})
|
|
|
|
it('onError 被调用且不影响后续轮询', async () => {
|
|
const h = fakeHost()
|
|
const errors: unknown[] = []
|
|
let calls = 0
|
|
const poller = createVisibilityPoller(() => {
|
|
calls++
|
|
if (calls === 1) throw new Error('boom')
|
|
}, 1000, { onError: (error) => errors.push(error) }, h.host)
|
|
await poller.tick()
|
|
expect(errors).toHaveLength(1)
|
|
expect(poller.lastError.value).toBeInstanceOf(Error)
|
|
await poller.tick()
|
|
expect(calls).toBe(2)
|
|
expect(poller.lastError.value).toBeNull()
|
|
})
|
|
})
|