Merge autosave probes and reconcile the complete verification guide
This commit is contained in:
65
frontend/autosave.test.js
Normal file
65
frontend/autosave.test.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
|
||||
// Exercise the actual app functions without DOM/player/network dependencies.
|
||||
const source = fs.readFileSync(require('node:path').join(__dirname, 'app.js'), 'utf8');
|
||||
function harness({ autoPreload = true, cached = [], result = { ok: true, cached: true }, error } = {}) {
|
||||
const calls = [], jobs = new Map();
|
||||
const video = { id: 'benchmedia01', title: 'Local clip' };
|
||||
const context = vm.createContext({
|
||||
data: { settings: { autoPreload }, playlists: [{ id: 'pl', videos: [video] }], pinnedPlaylists: [] },
|
||||
cachedIds: new Set(cached), downloading: new Set(), downloadMeta: new Map(), saveControllers: new Map(),
|
||||
view: { type: 'home' }, current: null, window: {}, AbortController,
|
||||
slim: v => v, markCardCacheState() {}, renderSidebar() {}, updateDownloadBadge() {}, toast() {},
|
||||
SaveSlots: { acquire: async () => {}, release() {} },
|
||||
SaveQueue: { add: v => jobs.set(v.id, { status: 'queued' }), remove: id => jobs.delete(id), progress() {}, state: (id, status, message) => jobs.set(id, { status, message }) },
|
||||
API: { cacheDownload: async id => { calls.push(id); if (error) throw error; return result; } },
|
||||
recordDeviceFile() {}, warmThumb() {}, thumbUrlFor: () => '', cacheMutations: 0,
|
||||
});
|
||||
const preload = source.slice(source.indexOf('async function preload(video,'), source.indexOf('// A device copy that failed to play.'));
|
||||
const playlists = source.slice(source.indexOf('function preloadPlaylist(pl)'), source.indexOf('// Update a single card\'s saved badge'));
|
||||
vm.runInContext(preload + playlists, context);
|
||||
return { context, calls, jobs, video };
|
||||
}
|
||||
|
||||
test('auto preload tops up playlists, while pinned playlists save with the setting off', async () => {
|
||||
const h = harness({ autoPreload: false });
|
||||
h.context.preloadPlaylist(h.context.data.playlists[0]);
|
||||
await new Promise(r => setImmediate(r));
|
||||
assert.deepEqual(h.calls, []);
|
||||
h.context.data.pinnedPlaylists = ['pl'];
|
||||
h.context.preloadPinnedPlaylists();
|
||||
await new Promise(r => setImmediate(r));
|
||||
assert.deepEqual(h.calls, [h.video.id]);
|
||||
assert.ok(h.context.cachedIds.has(h.video.id));
|
||||
assert.equal(h.jobs.size, 0);
|
||||
assert.equal(h.context.downloading.size, 0);
|
||||
const enabled = harness();
|
||||
enabled.context.preloadPlaylist(enabled.context.data.playlists[0]);
|
||||
await new Promise(r => setImmediate(r));
|
||||
assert.deepEqual(enabled.calls, [enabled.video.id]);
|
||||
});
|
||||
|
||||
test('a repeated save shares the in-flight operation and does not download an existing copy', async () => {
|
||||
const h = harness();
|
||||
await Promise.all([h.context.preload(h.video), h.context.preload(h.video)]);
|
||||
await h.context.preload(h.video);
|
||||
assert.deepEqual(h.calls, [h.video.id]);
|
||||
assert.ok(h.context.cachedIds.has(h.video.id));
|
||||
assert.equal(h.context.saveControllers.size, 0);
|
||||
});
|
||||
|
||||
test('failed and interrupted saves do not acquire a Saved badge and can be attempted again', async () => {
|
||||
for (const options of [{ result: { ok: false, error: 'server failed' } }, { error: new Error('storage quota') }, { result: { ok: false, paused: true } }]) {
|
||||
const h = harness(options);
|
||||
await h.context.preload(h.video, { quiet: true });
|
||||
assert.equal(h.context.cachedIds.has(h.video.id), false);
|
||||
assert.equal(h.context.downloading.size, 0);
|
||||
assert.equal(h.context.saveControllers.size, 0);
|
||||
assert.ok(h.jobs.has(h.video.id));
|
||||
await h.context.preload(h.video, { quiet: true });
|
||||
assert.equal(h.calls.length, 2);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user