Bound synchronous live channel enrichment and tolerate minimal projection payloads so analysis and listings do not blank the UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
3.3 KiB
JavaScript
70 lines
3.3 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');
|
|
const script = relativePath => fs.readFileSync(path.join(root, 'public', relativePath), 'utf8');
|
|
|
|
test('minimal non-enriched live projection payload maps and renders without an exception', () => {
|
|
const mapped = projection.mapResponse({
|
|
projection: { stale: false },
|
|
filters: { availableLanguages: [null, {}] },
|
|
channels: [
|
|
{
|
|
id: 'logical-news:und', logicalChannelId: 'logical-news', displayName: 'News',
|
|
standard: null, language: {}, epg: null, preferredVariant: null,
|
|
variants: [{
|
|
id: 'news-raw', sourceId: 1, name: 'News Raw', tvgId: 'news.raw',
|
|
categoryName: 'News', language: null, isHidden: false
|
|
}]
|
|
},
|
|
{
|
|
id: 'logical-sports:und', logicalChannelId: 'logical-sports', displayName: 'Sports',
|
|
language: null, variants: null,
|
|
preferredVariant: {
|
|
id: 'sports-raw', sourceId: 1, name: 'Sports Raw', tvgId: 'sports.raw',
|
|
categoryName: 'Sports', isHidden: false
|
|
}
|
|
},
|
|
{
|
|
id: 'empty', logicalChannelId: 'empty', displayName: 'Empty',
|
|
language: null, variants: null, preferredVariant: null
|
|
}
|
|
]
|
|
}, [{ id: 1, type: 'm3u', name: 'Provider' }]);
|
|
|
|
assert.equal(mapped.logicalChannels.length, 2);
|
|
assert.ok(mapped.logicalChannels.every(channel => channel.language.code === 'und'));
|
|
assert.equal(mapped.logicalChannels[0].tvgId, 'news.raw');
|
|
assert.deepEqual(mapped.availableLanguages.map(language => language.code), ['und']);
|
|
|
|
const dom = new JSDOM('<div id="result"></div>', { url: 'http://nodecast.test/', runScripts: 'outside-only' });
|
|
const { window } = dom;
|
|
window.eval(script('js/icons.js'));
|
|
window.eval(script('js/services/channelLanguageDetection.js'));
|
|
window.eval(script('js/services/channelLanguageFusion.js'));
|
|
window.eval(script('js/services/channelVariantGrouping.js'));
|
|
window.eval(`${script('js/components/ChannelList.js')}\nwindow.ChannelList = ChannelList;`);
|
|
window.app = { epgGuide: { getCurrentProgram: () => null, loadError: null } };
|
|
const channelList = Object.create(window.ChannelList.prototype);
|
|
channelList.expandedVariants = new Set();
|
|
channelList.sources = [{ id: 1, type: 'm3u', name: 'Provider' }];
|
|
channelList.showHidden = false;
|
|
channelList.isFavorite = () => false;
|
|
channelList.isHidden = () => false;
|
|
|
|
const result = window.document.getElementById('result');
|
|
assert.doesNotThrow(() => {
|
|
result.innerHTML = mapped.logicalChannels.map(channel =>
|
|
channelList.renderLogicalChannel(channel, 'render-id', 'News')
|
|
).join('');
|
|
});
|
|
assert.equal(result.querySelectorAll('.logical-channel').length, 2);
|
|
assert.equal(result.querySelector('.channel-language-badge').getAttribute('aria-label'), 'Unknown language');
|
|
assert.match(result.textContent, /No program information/);
|
|
dom.window.close();
|
|
});
|