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>
This commit is contained in:
71
test/t23Frontend.test.js
Normal file
71
test/t23Frontend.test.js
Normal file
@ -0,0 +1,71 @@
|
||||
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');
|
||||
});
|
||||
Reference in New Issue
Block a user