feedBack/tests/js/play_queue_peek.test.js
ChrisBeWithYou 28b0319e27
play-queue: peekNext() — expose the following track for queue-aware UIs (#719)
A results screen that offers "Up next: <song> — starting in 10s" needs to
know WHAT follows without reaching into queue internals. peekNext() returns
{filename, index, total} for the next track (null when nothing follows),
pure — peeking never plays or mutates.

First consumer: the note_detect results card's queue-advance strip (the
"Playlist Play All has no way to progress" tester issue).


Claude-Session: https://claude.ai/code/session_01Nm7tHs1Yvjjtnnu4nzJgdN

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 20:50:56 +02:00

52 lines
2.2 KiB
JavaScript

// playQueue.peekNext() (queue-advance UX): consumers that render "Up next"
// (the results card's countdown strip) need to know WHAT follows without
// reaching into queue internals. Extract the playQueue IIFE from app.js and
// drive it against a playSong stub.
'use strict';
const test = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
function makeQueue() {
const src = fs.readFileSync(path.join(__dirname, '..', '..', 'static', 'app.js'), 'utf8');
const start = src.indexOf('window.feedBack.playQueue = (function () {');
assert.ok(start !== -1, 'playQueue IIFE found in app.js');
const end = src.indexOf('})();', start);
assert.ok(end !== -1, 'playQueue IIFE terminator found');
const iife = src.slice(start, end + 5);
const played = [];
const sandbox = {
window: {
feedBack: {},
playSong: (fn, arr, opts) => played.push({ fn, arr, opts }),
fbNotify: null,
},
encodeURIComponent,
};
// eslint-disable-next-line no-new-func
new Function('window', 'encodeURIComponent', iife)(sandbox.window, encodeURIComponent);
return { q: sandbox.window.feedBack.playQueue, played };
}
test('peekNext exposes the following track without mutating the queue', () => {
const { q, played } = makeQueue();
assert.strictEqual(q.peekNext(), null); // idle queue → null
q.start(['a.sloppak', 'b.sloppak', 'c.sloppak'], { source: 'My list' });
assert.deepStrictEqual(q.peekNext(), { filename: 'b.sloppak', index: 1, total: 3 });
assert.deepStrictEqual(q.peekNext(), { filename: 'b.sloppak', index: 1, total: 3 }); // pure
assert.strictEqual(played.length, 1); // peeking never plays
q.advance();
assert.deepStrictEqual(q.peekNext(), { filename: 'c.sloppak', index: 2, total: 3 });
q.advance();
assert.strictEqual(q.peekNext(), null); // last track → nothing next
assert.strictEqual(q.remaining(), 0);
});
test('peekNext is null after clear', () => {
const { q } = makeQueue();
q.start(['a.sloppak', 'b.sloppak']);
q.clear();
assert.strictEqual(q.peekNext(), null);
});