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>
72 lines
3.6 KiB
JavaScript
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');
|
|
});
|