Files
Nodecast/test/t23Frontend.test.js
blomios 638a31d9ef feat(ui): surface live language filters and built-in EPG sources (#23)
Add language filter controls for projected live channels, preserve backend language views in the browser, and separate built-in EPG sources from custom sources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-06 11:42:23 +02:00

72 lines
3.6 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { JSDOM } = require('jsdom');
const root = path.resolve(__dirname, '..');
const projection = require('../public/js/services/liveChannelProjection.js');
function script(relativePath) {
return fs.readFileSync(path.join(root, 'public', relativePath), 'utf8');
}
test('projection adapter preserves backend channel-language views and their variants', () => {
const mapped = projection.mapResponse({
projection: { stale: false },
filters: { availableLanguages: [{ code: 'fr', short: 'FR', label: 'French' }] },
channels: [{
id: 'logical-1:fr', logicalChannelId: 'logical-1', displayName: 'beIN Sports 1',
language: { code: 'fr', short: 'FR', label: 'French', confidence: 95 },
selectedVariantId: '7:hd', epg: { channelId: 'BeInSports1.fr', language: 'fr' },
variants: [
{ id: 'sd', sourceId: 7, name: 'beIN 1 SD', categoryName: 'Sports', isHidden: false },
{ id: 'hd', sourceId: 7, name: 'beIN 1 HD', categoryName: 'Sports', isHidden: false }
]
}]
}, [{ id: 7, type: 'm3u', name: 'Provider' }]);
assert.equal(mapped.logicalChannels.length, 1);
assert.equal(mapped.logicalChannels[0].fingerprint, 'logical-1:fr');
assert.equal(mapped.logicalChannels[0].language.code, 'fr');
assert.equal(mapped.logicalChannels[0].preferredVariant.id, 'hd');
assert.equal(mapped.logicalChannels[0].variants.length, 2);
assert.equal(mapped.logicalChannels[0].tvgId, 'BeInSports1.fr');
assert.deepEqual(mapped.availableLanguages.map(language => language.code), ['fr']);
});
test('live API sends flexible search and selected language filters', async () => {
const dom = new JSDOM('', { url: 'http://nodecast.test/', runScripts: 'outside-only' });
let requestedUrl = '';
dom.window.fetch = async url => {
requestedUrl = url;
return { ok: true, headers: { get: () => 'application/json' }, json: async () => ({ channels: [] }) };
};
dom.window.eval(script('js/api.js'));
await dom.window.API.live.getChannels({ q: 'Bein Sport 1', languages: ['fr', 'en'] });
const params = new URL(requestedUrl, 'http://nodecast.test').searchParams;
assert.equal(params.get('q'), 'Bein Sport 1');
assert.equal(params.get('languages'), 'fr,en');
});
test('built-in EPG sources expose only refresh and enable controls', () => {
const dom = new JSDOM('<div id="builtin-epg-list"></div>', { runScripts: 'outside-only' });
dom.window.Icons = { series: '<i></i>', refresh: '<i></i>' };
dom.window.API = { sources: {} };
dom.window.eval(`${script('js/components/SourceManager.js')}\nwindow.SourceManager = SourceManager;`);
const manager = Object.create(dom.window.SourceManager.prototype);
manager.builtinEpgList = dom.window.document.getElementById('builtin-epg-list');
manager.refreshSource = () => {};
manager.toggleSource = () => {};
manager.renderBuiltinEpgSources([{
id: 4, name: 'EPG.PW France', builtinKey: 'epgpw-fr', enabled: false,
language: 'fr', country: 'FR', readonly: true, removable: false
}]);
assert.match(manager.builtinEpgList.textContent, /Built-in/);
assert.match(manager.builtinEpgList.textContent, /Free EPG data/);
assert.equal(manager.builtinEpgList.querySelector('[data-action="delete"]'), null);
assert.equal(manager.builtinEpgList.querySelector('[data-action="edit"]'), null);
assert.equal(manager.builtinEpgList.querySelector('[data-action="toggle"]').getAttribute('aria-pressed'), 'false');
});