import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { pollVisible } from '../../src/lib/ui/poll.js'; let documentStub: EventTarget & { visibilityState: string }; let stop: (() => void) | undefined; beforeEach(() => { vi.useFakeTimers(); documentStub = Object.assign(new EventTarget(), { visibilityState: 'visible' }); vi.stubGlobal('document', documentStub); }); afterEach(() => { stop?.(); stop = undefined; vi.useRealTimers(); vi.unstubAllGlobals(); vi.restoreAllMocks(); }); const visibility = (state: string) => { documentStub.visibilityState = state; documentStub.dispatchEvent(new Event('visibilitychange')); }; describe('visible page polling', () => { it('waits for a request to finish before scheduling the next request', async () => { let finish!: () => void; const task = vi.fn(() => new Promise(resolve => { finish = resolve; })); stop = pollVisible(task, 1000); await vi.advanceTimersByTimeAsync(5000); visibility('visible'); expect(task).toHaveBeenCalledTimes(1); finish(); await vi.advanceTimersByTimeAsync(999); expect(task).toHaveBeenCalledTimes(1); await vi.advanceTimersByTimeAsync(1); expect(task).toHaveBeenCalledTimes(2); }); it('pauses when hidden and refreshes on return, including hiding during a request', async () => { let finish!: () => void; const task = vi.fn(() => new Promise(resolve => { finish = resolve; })); stop = pollVisible(task, 1000); visibility('hidden'); finish(); await vi.advanceTimersByTimeAsync(5000); expect(task).toHaveBeenCalledTimes(1); visibility('visible'); expect(task).toHaveBeenCalledTimes(2); finish(); stop(); visibility('visible'); await vi.advanceTimersByTimeAsync(5000); expect(task).toHaveBeenCalledTimes(2); }); it('recovers after a failed request and supports a delayed initial refresh', async () => { vi.spyOn(console, 'error').mockImplementation(() => {}); const task = vi.fn().mockRejectedValueOnce(new Error('offline')).mockResolvedValue(undefined); stop = pollVisible(task, 1000, false); expect(task).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(2000); expect(task).toHaveBeenCalledTimes(2); }); });