import assert from "node:assert/strict"; import { test } from "node:test"; import { PlayerStateClient } from "../public/player-state.js"; test("polls reuse tracks and accept edits from another browser", async () => { const tracks = [{ title: "First" }]; const changedTracks = [{ title: "Second" }]; const responses = [ { playlistVersion: "first", tracks, position: 0 }, { playlistVersion: "first", tracks: null, position: 1 }, { playlistVersion: "changed", tracks: changedTracks, position: 2 }, { playlistVersion: "changed", tracks: null, position: 3 }, ]; const paths = []; const client = new PlayerStateClient(async (path) => { paths.push(path); return responses.shift(); }); assert.equal((await client.request("/api/state")).tracks, tracks); const unchanged = await client.request("/api/state"); assert.equal(unchanged.tracks, tracks); assert.equal(unchanged.position, 1); assert.equal((await client.request("/api/state")).tracks, changedTracks); assert.equal((await client.request("/api/state")).tracks, changedTracks); assert.deepEqual(paths, [ "/api/state", "/api/state?playlistVersion=first", "/api/state?playlistVersion=first", "/api/state?playlistVersion=changed", ]); }); test("commands and discovery wait for polling and use its latest version", async () => { let finishPoll; const paths = []; const commandBody = { index: 1 }; const client = new PlayerStateClient((path, body) => { paths.push(path); if (paths.length === 1) { return new Promise((resolve) => { finishPoll = resolve; }); } if (paths.length === 2) { assert.equal(body, commandBody); return { playlistVersion: "other-tab", tracks: [] }; } return { playlistVersion: "other-tab", tracks: null }; }); const poll = client.request("/api/state"); const command = client.request("/api/command/tab-select", commandBody); const discovery = client.request("/api/discover", {}); await Promise.resolve(); assert.deepEqual(paths, ["/api/state"]); finishPoll({ playlistVersion: "original-tab", tracks: [{ title: "First" }] }); await poll; assert.deepEqual((await command).tracks, []); assert.deepEqual((await discovery).tracks, []); assert.deepEqual(paths, [ "/api/state", "/api/command/tab-select?playlistVersion=original-tab", "/api/discover?playlistVersion=other-tab", ]); }); test("a failed request leaves the queue usable", async () => { let calls = 0; const client = new PlayerStateClient(async () => { if (++calls === 1) throw new Error("Offline"); return { playlistVersion: "recovered", tracks: [] }; }); await assert.rejects(client.request("/api/state"), /Offline/); assert.deepEqual((await client.request("/api/state")).tracks, []); }); test("mismatched omitted tracks force a full refresh instead of stale reuse", async () => { const paths = []; const responses = [ { playlistVersion: "before", tracks: [{ title: "Before" }] }, { playlistVersion: "after", tracks: null }, { playlistVersion: "after", tracks: [] }, ]; const client = new PlayerStateClient(async (path) => { paths.push(path); return responses.shift(); }); await client.request("/api/state"); await assert.rejects(client.request("/api/state"), /Playlist cache/); assert.deepEqual((await client.request("/api/state")).tracks, []); assert.equal(paths[2], "/api/state"); });