import assert from 'node:assert/strict'; import { test } from 'node:test'; import { install, server, settle } from './flow-helpers.mjs'; const ui = install(); const { startPolling, stopPolling, watchJobs } = await import('../../bindery/static/js/jobs.js'); test('polling adapts to work and visibility, retries failures, and stops after an in-flight request', async (t) => { t.mock.timers.enable({ apis: ['setTimeout'] }); const api = server(t); const rendered = []; const unsubscribe = watchJobs({ render: (jobs) => rendered.push(jobs) }); t.after(() => { unsubscribe(); stopPolling(); }); let jobs = []; api.on('GET', 'jobs', () => jobs); startPolling(); await settle(); t.mock.timers.tick(9999); assert.equal(api.requests.length, 1); jobs = [{ id: 'job', status: 'running' }]; t.mock.timers.tick(1); await settle(); assert.equal(api.requests.length, 2); t.mock.timers.tick(1999); assert.equal(api.requests.length, 2); t.mock.timers.tick(1); await settle(); assert.equal(api.requests.length, 3); document.hidden = true; ui.documentEvents.visibilitychange(); assert.equal(api.requests.length, 3); t.mock.timers.tick(2000); await settle(); t.mock.timers.tick(59999); assert.equal(api.requests.length, 4); t.mock.timers.tick(1); await settle(); assert.equal(api.requests.length, 5); document.hidden = false; api.on('GET', 'jobs', () => { throw new Error('Connection lost'); }); const previous = rendered.length; ui.documentEvents.visibilitychange(); await settle(); assert.equal(rendered.length, previous); jobs = [{ id: 'job', status: 'done' }]; api.on('GET', 'jobs', () => jobs); t.mock.timers.tick(2000); await settle(); assert.deepEqual(rendered.at(-1), jobs); let resolve; api.on('GET', 'jobs', () => new Promise((done) => { resolve = done; })); t.mock.timers.tick(10000); stopPolling(); resolve([]); await settle(); const total = api.requests.length; t.mock.timers.tick(60000); await settle(); assert.equal(api.requests.length, total); });