const test = require('node:test'); const assert = require('node:assert/strict'); const grouping = require('../public/js/services/channelVariantGrouping.js'); const channel = (id, name, extra = {}) => ({ id, streamId: id, sourceId: 1, name, groupTitle: 'Sports', ...extra }); test('normalizes regional prefixes and quality markers into one logical channel', () => { const names = ['|FR| BEIN SPORTS uhd 1', '|FR| BEIN SPORTS 1 4K', 'FR-CAR| bein sports 1']; assert.deepEqual(names.map(grouping.normalizeChannelName), ['bein sports 1', 'bein sports 1', 'bein sports 1']); const logical = grouping.groupChannels(names.map((name, index) => channel(String(index + 1), name))); assert.equal(logical.length, 1); assert.equal(logical[0].displayName, 'beIN Sports 1'); assert.equal(logical[0].variants.length, 3); }); test('does not merge distinct numbered channels', () => { const logical = grouping.groupChannels([ channel('1', 'beIN Sports 1 HD'), channel('2', 'beIN Sports 2 HD') ]); assert.deepEqual(logical.map(item => item.fingerprint), ['bein sports 1', 'bein sports 2']); }); test('normalizes captured beIN Sports aliases like the server projection', () => { const aliases = [ 'C Car beIN Sports 1', 'C Reu beIN Sports 1', 'Thai beIN Sports 1 Thai', 'beIN Sports 1', 'beIN Sports 1 12h', 'beIN Sports 1 2h', 'beIN Sports 1 4h', 'beIN Sports 1 8h', 'beIN Sports 1 Appel', 'beIN Sports 1 En', 'beIN Sports 1 Fr', 'beIN Sports 1 Tod' ]; assert.deepEqual(aliases.map(grouping.normalizeChannelName), Array(aliases.length).fill('bein sports 1')); const logical = grouping.groupChannels(aliases.map((name, index) => channel(String(index), name))); assert.equal(logical.length, 1); assert.equal(logical[0].variants.length, aliases.length); }); test('keeps distinct beIN numbers and unknown suffixes separate', () => { assert.equal(grouping.normalizeChannelName('beIN Sports 1'), 'bein sports 1'); assert.equal(grouping.normalizeChannelName('beIN Sports 2'), 'bein sports 2'); assert.equal(grouping.normalizeChannelName('beIN Sports Max 10'), 'bein sports max 10'); assert.equal(grouping.normalizeChannelName('Partner beIN Sports 1 Event'), 'bein sports 1 event'); assert.notEqual( grouping.normalizeChannelName('Partner beIN Sports 1 Event'), grouping.normalizeChannelName('beIN Sports 1') ); }); test('preserves precomputed projection boundaries and display names', () => { const logical = grouping.groupChannels([ channel('1', 'Shared Name HD', { logicalId: 'logical:a', logicalFingerprint: 'a', logicalDisplayName: 'Backend A' }), channel('2', 'Shared Name 4K', { logicalId: 'logical:b', logicalFingerprint: 'b', logicalDisplayName: 'Backend B' }) ]); assert.deepEqual(logical.map(item => [item.id, item.displayName]), [ ['logical:a', 'Backend A'], ['logical:b', 'Backend B'] ]); }); test('logical favorite and hidden state are derived from individual streams', () => { const variants = [channel('1', 'BEIN SPORTS 1 HD'), channel('2', 'BEIN SPORTS 1 4K')]; const logical = grouping.groupChannels(variants, { isFavorite: item => item.id === '1', isHidden: item => item.id === '2' })[0]; assert.equal(logical.isFavorite, true); assert.equal(logical.isHidden, false); assert.equal(logical.preferredVariant.id, '1'); assert.equal(grouping.groupChannels(variants, { isHidden: () => true }).length, 0); assert.equal(grouping.groupChannels(variants, { isHidden: () => true, showHidden: true })[0].isHidden, true); }); test('an explicit visible selection overrides the automatic quality preference', () => { const hd = channel('1', 'BEIN SPORTS 1 HD'); const uhd = channel('2', 'BEIN SPORTS 1 UHD'); const selectedVariants = new Map([['bein sports 1', grouping.getVariantKey(hd)]]); const logical = grouping.groupChannels([hd, uhd], { selectedVariants })[0]; assert.equal(logical.preferredVariant.id, '1'); }); test('flat list ordering promotes favorites without duplicating channels', () => { const channels = [ { id: 'one', isFavorite: false }, { id: 'two', isFavorite: true }, { id: 'three', isFavorite: false }, { id: 'four', isFavorite: true } ]; const ordered = grouping.orderFlatChannels(channels); assert.deepEqual(ordered.map(item => item.id), ['two', 'four', 'one', 'three']); assert.equal(new Set(ordered.map(item => item.id)).size, channels.length); }); test('favorites group updates are disabled for flat lists', () => { assert.equal(grouping.shouldUseFavoritesGroup('flat'), false); assert.equal(grouping.shouldUseFavoritesGroup('grouped'), true); }); test('Laya language results participate in variant selection', () => { const lowQualityFrench = channel('1', 'Sports One SD'); const highQualityEnglish = channel('2', 'Sports One 4K'); const languageResults = new Map([ [grouping.getVariantKey(lowQualityFrench), { language: 'fr', confidence: 0.92 }], [grouping.getVariantKey(highQualityEnglish), { language: 'en', confidence: 0.94 }] ]); const logical = grouping.groupChannels([lowQualityFrench, highQualityEnglish], { languageMode: 'laya', languageResults, preferredLanguage: 'fr' })[0]; assert.equal(logical.preferredVariant.id, '1'); assert.deepEqual(logical.availableLanguages.map(item => item.code), ['fr', 'en']); }); test('language selection immediately resolves the best visible matching variant', () => { const frenchSd = channel('fr-sd', 'Sports One SD'); const frenchHd = channel('fr-hd', 'Sports One HD'); const english4k = channel('en-4k', 'Sports One 4K'); const languageResults = new Map([ [grouping.getVariantKey(frenchSd), { language: 'fr', confidence: 0.9 }], [grouping.getVariantKey(frenchHd), { language: 'fr', confidence: 0.9 }], [grouping.getVariantKey(english4k), { language: 'en', confidence: 0.95 }] ]); const logical = grouping.groupChannels([frenchSd, frenchHd, english4k], { languageMode: 'laya', languageResults })[0]; const selected = grouping.selectVariantByLanguage(logical, 'fr', { isHidden: variant => variant.id === 'fr-hd' }); assert.equal(selected.id, 'fr-sd'); });