Files
KefinTweaks/tests/collection-progress.test.cjs

73 lines
3.3 KiB
JavaScript

// Run: node --test tests/collection-progress.test.cjs
// Exercise the actual progress renderer with DOM-shaped elements, not a copy of it.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const vm = require('node:vm');
const source = fs.readFileSync(path.join(__dirname, '../scripts/collections.js'), 'utf8');
const renderer = source.match(/^([ \t]*)async function updateCollectionPage\(item\) \{[\s\S]*?^\1\}/m)?.[0];
assert.ok(renderer, 'Collection progress renderer not found');
function fixture({ video = false, active = true } = {}) {
const page = { dataset: {} };
const info = { children: [], appendChild(child) { this.children.push(child); } };
// querySelector returns one Element or null. An Element has no list length.
const videoCollection = video ? { tagName: 'DIV' } : null;
const document = {
querySelector(selector) {
if (selector === '.libraryPage:not(.hide)') return active ? page : null;
if (selector.endsWith(' .collectionItems:has(.card[data-mediatype="Video"])')) return videoCollection;
if (selector.endsWith(' .itemMiscInfo-primary')) return info;
throw new Error('Unexpected selector: ' + selector);
},
createElement(tagName) { return { tagName, className: '', textContent: '' }; }
};
const render = vm.runInNewContext(renderer + '\nupdateCollectionPage;', { document }, { timeout: 1000 });
return { page, render, texts: () => info.children.map(child => child.textContent) };
}
test('an empty collection finishes without throwing or adding progress counts', async () => {
const f = fixture();
await f.render({ ChildCount: 0, UserData: { UnplayedItemCount: 0 } });
assert.deepEqual(f.texts(), []);
assert.equal(f.page.dataset.kefinCollectionPage, 'true');
});
test('video collections show watched and unwatched counts', async () => {
const f = fixture({ video: true });
await f.render({ RecursiveItemCount: 3, UserData: { UnplayedItemCount: 2 } });
assert.deepEqual(f.texts(), ['1 watched', '2 unwatched']);
});
test('collections without video cards retain complete and incomplete counts', async () => {
const f = fixture();
await f.render({ ChildCount: 4, UserData: { UnplayedItemCount: 2 } });
assert.deepEqual(f.texts(), ['2 complete', '2 incomplete']);
});
test('fully watched video collections add no extra progress counts', async () => {
const f = fixture({ video: true });
await f.render({ ChildCount: 3, UserData: { UnplayedItemCount: 0 } });
assert.deepEqual(f.texts(), []);
assert.equal(f.page.dataset.kefinCollectionPage, 'true');
});
test('repeated rendering does not duplicate the progress counts', async () => {
const f = fixture({ video: true });
const item = { ChildCount: 2, UserData: { UnplayedItemCount: 1 } };
await f.render(item);
const first = f.texts();
await f.render(item);
assert.equal(first.length, 2);
assert.deepEqual(f.texts(), first);
});
test('leaving the collection page does not add progress counts', async () => {
const f = fixture({ active: false, video: true });
await f.render({ ChildCount: 2, UserData: { UnplayedItemCount: 1 } });
assert.deepEqual(f.texts(), []);
assert.equal(f.page.dataset.kefinCollectionPage, undefined);
});