Files
QZMusic_PC/amll-local/packages/lyric/test/yrc.test.ts
lqtmcstudio 72f4510dc8 fork(fix): Clone AMLL 并修复 BUG
- 将AMLL Clone到本以地进行修复和优化(emm虽然这很不优雅但是暂时无时间做子模块和Fork)
- 修复在当前播放歌词行不可见的视口Seek会出现滚动偏移的问题
2026-06-07 00:02:14 +08:00

135 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import { parseYrc, stringifyYrc } from "../src/formats/yrc";
describe("yrc", () => {
it("parses basic word-timestamped line", () => {
const lines = parseYrc(
"[1000,1000](1000,500,0)Hello(1500,500,0)World\n[3000,1000](3000,1000,0)Again",
);
expect(lines).toHaveLength(2);
expect(lines[0].startTime).toBe(1000);
expect(lines[0].endTime).toBe(2000);
expect(lines[0].words[0].word).toBe("Hello");
expect(lines[0].words[0].startTime).toBe(1000);
expect(lines[0].words[0].endTime).toBe(1500);
expect(lines[0].words[1].word).toBe("World");
expect(lines[0].words[1].startTime).toBe(1500);
expect(lines[0].words[1].endTime).toBe(2000);
expect(lines[1].words[0].word).toBe("Again");
});
it("handles CRLF and ignores lines without valid line timestamp", () => {
const lines = parseYrc(
"no timestamp\r\n[invalid,100](0,100,0)Bad\r\n[-1,100](0,100,0)Bad\r\n[1000,1000](1000,1000,0)Hello",
);
expect(lines).toHaveLength(1);
expect(lines[0].startTime).toBe(1000);
expect(lines[0].endTime).toBe(2000);
expect(lines[0].words[0].word).toBe("Hello");
});
it("ignores empty lines and lines with only whitespace", () => {
const lines = parseYrc(
"[0,20] \n[0,20]\n[0,20](10,10,0)Test\n \n\n[0,20] \n",
);
expect(lines).toHaveLength(1);
expect(lines[0].words[0].word).toBe("Test");
});
it("stringifies words and preserves spaces", () => {
const result = stringifyYrc([
{
startTime: 1000,
endTime: 3000,
words: [
{ startTime: 1000, endTime: 2000, word: "Hello", romanWord: "" },
{ startTime: 0, endTime: 0, word: " ", romanWord: "" },
{ startTime: 2000, endTime: 3000, word: "World", romanWord: "" },
],
translatedLyric: "",
romanLyric: "",
isBG: false,
isDuet: false,
},
]);
expect(result).toBe("[1000,2000](1000,1000,0)Hello (2000,1000,0)World");
});
it("stringifies bg lines with full-width wrapping parentheses", () => {
const result = stringifyYrc([
{
startTime: 1000,
endTime: 2000,
words: [
{ startTime: 1000, endTime: 2000, word: "Hello", romanWord: "" },
],
translatedLyric: "",
romanLyric: "",
isBG: true,
isDuet: false,
},
]);
expect(result).toBe("[1000,1000](1000,1000,0)Hello");
});
it("parses and keeps bg wrapper lines stable", () => {
const input = "[1000,1000](1000,1000,0)Hello, (2000,2000,0)world";
const lines = parseYrc(input);
expect(lines).toHaveLength(1);
expect(lines[0].isBG).toBe(true);
expect(lines[0].words).toHaveLength(2);
expect(lines[0].words[0].word).toBe("Hello, ");
expect(lines[0].words[1].word).toBe("world");
expect(stringifyYrc(lines)).toBe(input);
});
it("keeps parenthesized fragments in plain word text", () => {
const lines = parseYrc("[0,20](0,10,0)A(x)(10,10,0) B");
expect(lines).toHaveLength(1);
expect(lines[0].words.map((w) => w.word)).toEqual(["A(x)", " B"]);
expect(lines[0].words[0].startTime).toBe(0);
expect(lines[0].words[0].endTime).toBe(10);
expect(lines[0].words[1].startTime).toBe(10);
expect(lines[0].words[1].endTime).toBe(20);
});
it("normalizes invalid timestamps when stringifying", () => {
const result = stringifyYrc([
{
startTime: Number.NaN,
endTime: Number.POSITIVE_INFINITY,
words: [
{
startTime: -1,
endTime: Number.POSITIVE_INFINITY,
word: "Hello",
romanWord: "",
},
],
translatedLyric: "",
romanLyric: "",
isBG: false,
isDuet: false,
},
]);
expect(result).toBe("[0,0](0,0,0)Hello");
});
it("keeps parse -> stringify -> parse stable for content and timing", () => {
const input =
"[1000,1000](1000,500,0)Hello (1500,500,0)World\n[3000,1000](3000,1000,0)Again";
const first = parseYrc(input);
const text = stringifyYrc(first);
const second = parseYrc(text);
expect(second).toEqual(first);
});
});