81 lines
3.2 KiB
JavaScript
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);
|
|
});
|