// Tests for the job polling in bindery/static/js/jobs.js. Run with: node --test tests/js/ import assert from 'node:assert/strict'; import { after, beforeEach, test } from 'node:test'; // jobs.js listens for tab visibility changes when it loads. globalThis.document = { hidden: false, addEventListener() {} }; let jobs = []; globalThis.fetch = async () => ({ ok: true, status: 200, json: async () => jobs }); const { refreshJobs, startPolling, stopPolling, watchJobs } = await import('../../bindery/static/js/jobs.js'); const job = (id, storyId, status) => ({ id, story_id: storyId, status }); const tick = () => new Promise((resolve) => setTimeout(resolve, 0)); beforeEach(async () => { jobs = []; startPolling(); await tick(); }); after(() => stopPolling()); test('watchJobs renders the jobs of one story', async () => { const rendered = []; const stop = watchJobs({ storyId: 1, render: (list) => rendered.push(list.map((j) => j.id)) }); jobs = [job('a', 1, 'queued'), job('b', 2, 'queued')]; await refreshJobs(); assert.deepEqual(rendered.at(-1), ['a']); stop(); jobs = [job('c', 1, 'queued')]; await refreshJobs(); assert.deepEqual(rendered.at(-1), ['a']); }); test('watchJobs reports a finished job once', async () => { let finished = 0; const stop = watchJobs({ storyId: 1, onFinished: () => finished++ }); jobs = [job('a', 1, 'queued')]; await refreshJobs(); jobs = [job('a', 1, 'running')]; await refreshJobs(); assert.equal(finished, 0); jobs = [job('a', 1, 'done')]; await refreshJobs(); assert.equal(finished, 1); await refreshJobs(); assert.equal(finished, 1); stop(); }); test('watchJobs ignores jobs that finished before it started, and other stories', async () => { let finished = 0; jobs = [job('old', 1, 'done')]; await refreshJobs(); const stop = watchJobs({ storyId: 1, onFinished: () => finished++ }); jobs = [job('old', 1, 'done'), job('other', 2, 'running')]; await refreshJobs(); jobs = [job('old', 1, 'done'), job('other', 2, 'error')]; await refreshJobs(); assert.equal(finished, 0); stop(); }); test('watchJobs without a story watches every job', async () => { let finished = 0; const stop = watchJobs({ onFinished: () => finished++ }); jobs = [job('a', 1, 'running'), job('b', null, 'queued')]; await refreshJobs(); jobs = [job('a', 1, 'cancelled'), job('b', null, 'error')]; await refreshJobs(); assert.equal(finished, 1); stop(); }); test('stopPolling stops the requests', async () => { stopPolling(); let calls = 0; const saved = globalThis.fetch; globalThis.fetch = async () => { calls++; return { ok: true, status: 200, json: async () => [] }; }; await refreshJobs(); assert.equal(calls, 0); globalThis.fetch = saved; });