Files
ytplayer/frontend/lyrics-core.test.js

81 lines
3.2 KiB
JavaScript

'use strict';
const { test } = require('node:test');
const assert = require('node:assert');
const LC = require('./lyrics-core');
test('fromText: timed, untimed, sections, cues, tags', () => {
const doc = LC.fromText([
'@ Key G, Capo 2 (Play F) | 70 BPM',
'[0:18] # Verse 1',
'[0:18.00] Worthy of every song',
'an untimed line',
'! Drums in',
'',
].join('\n'));
assert.deepStrictEqual(doc.tags, ['Key G', 'Capo 2 (Play F)', '70 BPM']);
assert.deepStrictEqual(doc.lines, [
{ t: 18, text: 'Verse 1', kind: 'section' },
{ t: 18, text: 'Worthy of every song', kind: 'line' },
{ t: null, text: 'an untimed line', kind: 'line' },
{ t: null, text: 'Drums in', kind: 'cue' },
]);
assert.strictEqual(doc.offset, null);
});
test('fromText: LRC multi-stamps expand and a fully timed sheet is time-sorted', () => {
const doc = LC.fromText('[ti:Song]\n[ar:Band]\n[offset:+500]\n[00:30.00]b\n[00:10.00][00:50.00]chorus');
assert.deepStrictEqual(doc.lines.map((l) => [l.t, l.text]), [[10, 'chorus'], [30, 'b'], [50, 'chorus']]);
assert.strictEqual(doc.offset, -0.5); // LRC +500 ms = sooner = our -0.5 s
});
test('toText round-trips through fromText', () => {
const doc = {
tags: ['Key G'],
lines: [
{ t: 83.456, text: 'line one', kind: 'line' },
{ t: null, text: 'Bridge', kind: 'section' },
{ t: 90, text: 'swell', kind: 'cue' },
],
};
const text = LC.toText(doc);
assert.strictEqual(text, '@ Key G\n[1:23.46] line one\n# Bridge\n[1:30.00] ! swell');
const back = LC.fromText(text);
assert.deepStrictEqual(back.lines.map((l) => l.kind), ['line', 'section', 'cue']);
assert.strictEqual(back.lines[0].t, 83.46);
});
test('activeIndex follows sung lines only and honours the offset', () => {
const lines = [
{ t: 10, text: 'Verse', kind: 'section' },
{ t: 10, text: 'a', kind: 'line' },
{ t: null, text: 'untimed', kind: 'line' },
{ t: 20, text: 'b', kind: 'line' },
{ t: 25, text: 'cue', kind: 'cue' },
];
assert.strictEqual(LC.activeIndex(lines, 5, 0), -1);
assert.strictEqual(LC.activeIndex(lines, 12, 0), 1);
assert.strictEqual(LC.activeIndex(lines, 26, 0), 3);
// +2 s offset: the "b" line shows at 22 s of playback, not 20.
assert.strictEqual(LC.activeIndex(lines, 21, 2), 1);
assert.strictEqual(LC.activeIndex(lines, 22, 2), 3);
assert.strictEqual(LC.activeSection(lines, 12, 0).text, 'Verse');
assert.strictEqual(LC.timedCount(lines), 2);
});
test('clock and stamp formatting', () => {
assert.strictEqual(LC.fmtClock(83.9), '1:23');
assert.strictEqual(LC.fmtClock(3723), '1:02:03');
assert.strictEqual(LC.fmtStamp(5.5), '0:05.50');
assert.strictEqual(LC.parseStamp('1:23.5'), 83.5);
assert.strictEqual(LC.parseStamp('1:02:03'), 3723);
assert.strictEqual(LC.parseStamp('nope'), null);
});
test('YouTube export adds a 0:00 entry only when needed', () => {
const items = [{ t: 130, title: 'Key change → D' }, { t: 42, title: 'Intro' }];
assert.strictEqual(LC.toYouTubeTimestamps(items), '0:00 Start\n0:42 Intro\n2:10 Key change → D');
assert.strictEqual(LC.toYouTubeTimestamps([{ t: 0, title: 'Top' }]), '0:00 Top');
assert.strictEqual(LC.activeMarker(items, 60).title, 'Intro');
assert.strictEqual(LC.activeMarker(items, 10), null);
});