import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { poll } from './poll'; beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); describe('poll', () => { it('fetches until done, then stops', async () => { const values = ['queued', 'running', 'succeeded', 'never']; const fetch = vi.fn(async () => values.shift()!); const seen: string[] = []; poll(fetch, { intervalMs: 1000, done: (v) => v === 'succeeded', onValue: (v) => seen.push(v), onError: () => {} }); await vi.advanceTimersByTimeAsync(5000); expect(seen).toEqual(['queued', 'running', 'succeeded']); expect(fetch).toHaveBeenCalledTimes(3); }); it('never has two requests in flight when the API is slower than the interval', async () => { let inFlight = 0; let maxInFlight = 0; const fetch = async () => { inFlight++; maxInFlight = Math.max(maxInFlight, inFlight); await new Promise((r) => setTimeout(r, 2500)); inFlight--; return 'running'; }; poll(fetch, { intervalMs: 1000, done: () => false, onValue: () => {}, onError: () => {} }); await vi.advanceTimersByTimeAsync(10_000); expect(maxInFlight).toBe(1); }); it('reports an error once and stops', async () => { const fetch = vi.fn(async () => { throw new Error('502'); }); const onError = vi.fn(); poll(fetch, { intervalMs: 1000, done: () => false, onValue: () => {}, onError }); await vi.advanceTimersByTimeAsync(5000); expect(onError).toHaveBeenCalledOnce(); expect(fetch).toHaveBeenCalledOnce(); }); it('stop() cancels future fetches, e.g. when the page unmounts', async () => { const fetch = vi.fn(async () => 'running'); const stop = poll(fetch, { intervalMs: 1000, done: () => false, onValue: () => {}, onError: () => {} }); await vi.advanceTimersByTimeAsync(1500); stop(); await vi.advanceTimersByTimeAsync(10_000); expect(fetch).toHaveBeenCalledTimes(1); }); });