'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { DEFINITIONS, PREFIX, actionIdFromUuid, buildRequest, normalizeSettings, renderAction, wrapTitle, } = require('../src/actions'); const STATE = { activeTab: 'music', visibleTab: 'music', tabs: { music: { playing: true, title: 'Ein sehr langer Songtitel', volume: 42.4, muted: false, likeStatus: 'like', repeatMode: 'ALL', hasVideo: true, thumbnail: 'https://example.test/c=w60-h60' }, youtube: { playing: false, title: 'Video', volume: 80, muted: true, likeStatus: 'none', repeatMode: 'NONE' }, }, }; test('actionIdFromUuid erkennt nur bekannte Aktionen', () => { assert.equal(actionIdFromUuid(`${PREFIX}volumeup`), 'volumeup'); assert.equal(actionIdFromUuid(`${PREFIX}gibtesnicht`), null); assert.equal(actionIdFromUuid('com.other.volumeup'), null); assert.equal(actionIdFromUuid(undefined), null); }); test('normalizeSettings begrenzt Werte und ersetzt ungueltige durch Standardwerte', () => { const settings = normalizeSettings({ step: '999', volume: -5, seconds: 'abc', target: 'x', tab: 'y' }); assert.equal(settings.step, 25); assert.equal(settings.volume, 0); assert.equal(settings.seconds, 10); assert.equal(settings.target, 'auto'); assert.equal(settings.tab, 'toggle'); }); test('normalizeSettings: showTitle nutzt den Standard der Aktion, wenn nichts gespeichert ist', () => { assert.equal(normalizeSettings({}, true).showTitle, true); assert.equal(normalizeSettings({}, false).showTitle, false); assert.equal(normalizeSettings({ showTitle: false }, true).showTitle, false); assert.equal(normalizeSettings({ showTitle: 'true' }, false).showTitle, true); }); test('Lauter/Leiser senden volume_change mit Vorzeichen und Schrittweite', () => { assert.deepEqual(buildRequest('volumeup', { step: 7 }), { cmd: 'volume_change', fields: { target: 'auto', value: 7 } }); assert.deepEqual(buildRequest('volumedown', { step: 7, target: 'music' }), { cmd: 'volume_change', fields: { target: 'music', value: -7 } }); }); test('Lautstaerke setzen und Spulen', () => { assert.deepEqual(buildRequest('volumeset', { volume: 55 }), { cmd: 'set_volume', fields: { target: 'auto', value: 55 } }); assert.deepEqual(buildRequest('seekback', { seconds: 15 }), { cmd: 'seek', fields: { target: 'auto', value: -15 } }); assert.deepEqual(buildRequest('seekforward', {}), { cmd: 'seek', fields: { target: 'auto', value: 10 } }); }); test('Playlist-Taste ohne ID sendet nichts, mit ID play_playlist', () => { assert.equal(buildRequest('playlist', {}), null); assert.deepEqual(buildRequest('playlist', { playlistId: ' PL123 ' }), { cmd: 'play_playlist', fields: { playlist: 'PL123' } }); }); test('Tab wechseln: "toggle" wird zu auto', () => { assert.equal(buildRequest('switchtab', {}).fields.target, 'auto'); assert.equal(buildRequest('switchtab', { tab: 'youtube' }).fields.target, 'youtube'); }); test('jede Aktion hat request() und liefert gueltiges cmd', () => { for (const id of Object.keys(DEFINITIONS)) { const request = buildRequest(id, { playlistId: 'PLx' }); assert.ok(request && typeof request.cmd === 'string', id); } }); test('Play/Pause-Zustand folgt dem aktiven Tab bzw. dem gewaehlten Ziel', () => { assert.equal(renderAction('playpause', {}, STATE, true).state, 1); assert.equal(renderAction('playpause', { target: 'youtube' }, STATE, true).state, 0); }); test('Lautstaerke-Tasten zeigen die gerundete Lautstaerke', () => { assert.equal(renderAction('volumeup', {}, STATE, true).title, '42%'); assert.equal(renderAction('volumedown', { target: 'youtube' }, STATE, true).title, '80%'); }); test('offline: Titel leer bzw. Offline-Hinweis, Zustand 0', () => { assert.equal(renderAction('volumeup', {}, null, false).title, ''); const view = renderAction('playpause', { showTitle: true }, null, false); assert.equal(view.state, 0); assert.match(view.title, /offline/); }); test('Mute, Like und Repeat bilden den Playtube-Zustand auf State-Indizes ab', () => { assert.equal(renderAction('mute', { target: 'youtube' }, STATE, true).state, 1); assert.equal(renderAction('mute', {}, STATE, true).state, 0); assert.equal(renderAction('like', {}, STATE, true).state, 1); assert.equal(renderAction('repeat', {}, STATE, true).state, 1); assert.equal(renderAction('repeat', { target: 'youtube' }, STATE, true).state, 0); const one = { ...STATE, tabs: { ...STATE.tabs, music: { ...STATE.tabs.music, repeatMode: 'ONE' } } }; assert.equal(renderAction('repeat', {}, one, true).state, 2); }); test('Cover-Taste liefert die Thumbnail-URL nur bei vorhandenem Video/Titel', () => { assert.equal(renderAction('nowplaying', {}, STATE, true).coverUrl, 'https://example.test/c=w60-h60'); assert.equal(renderAction('nowplaying', { target: 'youtube' }, STATE, true).coverUrl, null); assert.equal(renderAction('nowplaying', {}, null, false).coverUrl, null); }); test('wrapTitle bricht um, trennt lange Woerter und kuerzt', () => { assert.equal(wrapTitle('Hello World'), 'Hello\nWorld'); assert.equal(wrapTitle('Supercalifragilistic'), 'Supercal\nifragili\nstic'); const lines = wrapTitle('eins zwei drei vier fuenf sechs sieben acht neun zehn').split('\n'); assert.equal(lines.length, 3); assert.ok(lines[2].endsWith('..')); assert.equal(wrapTitle(''), ''); });