fix(live): prevent analysis crash on large catalogs (#26)
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>
This commit is contained in:
69
test/t26Frontend.test.js
Normal file
69
test/t26Frontend.test.js
Normal file
@ -0,0 +1,69 @@
|
||||
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();
|
||||
});
|
||||
Reference in New Issue
Block a user