const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const test = require('node:test'); const assert = require('node:assert/strict'); function loadAnnouncementTemplateHelpers() { const scriptPath = path.join(__dirname, '..', 'src', 'player', 'public', 'js', 'player-announcement-templates.js'); const script = fs.readFileSync(scriptPath, 'utf8'); const sandbox = { window: {}, module: { exports: {} }, exports: {}, console }; sandbox.window = sandbox; vm.runInNewContext(script, sandbox, { filename: scriptPath }); return sandbox; } function createTextNode(width) { return { width: width, attributes: {}, cloneNode() { return createTextNode(width); }, setAttribute(name, value) { this.attributes[name] = String(value); }, getBoundingClientRect() { return { width: this.width }; } }; } test('announcement marquee helper repeats enough content and measures a real cycle width', () => { const sandbox = loadAnnouncementTemplateHelpers(); assert.equal(typeof sandbox.initPlayerAnnouncementMarquee, 'function'); const textNodes = [createTextNode(100), createTextNode(100)]; const track = { dataset: {}, style: { values: {}, setProperty(name, value) { this.values[name] = String(value); } }, attributes: { 'data-announcement-marquee-rate': '50' }, parentElement: null, appended: [], getAttribute(name) { return this.attributes[name]; }, querySelectorAll() { return textNodes.slice(); }, appendChild(node) { this.appended.push(node); textNodes.push(node); return node; }, getBoundingClientRect() { const gap = 20; const width = textNodes.length * 100 + Math.max(0, textNodes.length - 1) * gap; return { width: width }; } }; const messageContainer = { getBoundingClientRect() { return { width: 420 }; } }; sandbox.window.getComputedStyle = function () { return { columnGap: '20px', gap: '20px' }; }; sandbox.initPlayerAnnouncementMarquee(track, { container: messageContainer }); assert.equal(track.dataset.marqueeMeasured, 'true'); assert.equal(track.style.values['--announcement-scroll-distance'], '120.00px'); assert.equal(track.style.values['--announcement-scroll-duration'], '8.00s'); assert.equal(track.style.animationDuration, '8.00s'); assert.ok(track.appended.length >= 3); }); test('announcement markup strips line breaks from the message', () => { const sandbox = loadAnnouncementTemplateHelpers(); sandbox.playerAnnouncementTemplates = { topBanner: '
' }; const markup = sandbox.renderPlayerAnnouncementMarkup({ announcement_type: 'top-banner', message: 'First line\nSecond line\r\nThird line', short_label: 'Breaking News', color_key: 'primary', icon_key: 'info-circle' }); assert.match(markup, /First line Second line Third line/); assert.doesNotMatch(markup, /