129 lines
3.9 KiB
TypeScript
129 lines
3.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
exportFilename,
|
|
formatSrtTimestamp,
|
|
transcriptToSrt,
|
|
transcriptToTxt,
|
|
} from "../src/lib/export";
|
|
import type { TranscriptChunk } from "../src/worker/protocol";
|
|
|
|
describe("transcriptToTxt", () => {
|
|
test("trims and appends a single trailing newline", () => {
|
|
expect(transcriptToTxt({ text: " Hello world. " })).toBe("Hello world.\n");
|
|
});
|
|
|
|
test("empty transcript becomes a bare newline", () => {
|
|
expect(transcriptToTxt({ text: "" })).toBe("\n");
|
|
});
|
|
});
|
|
|
|
describe("formatSrtTimestamp", () => {
|
|
test("zero", () => {
|
|
expect(formatSrtTimestamp(0)).toBe("00:00:00,000");
|
|
});
|
|
|
|
test("pads milliseconds to three digits", () => {
|
|
expect(formatSrtTimestamp(1.5)).toBe("00:00:01,500");
|
|
expect(formatSrtTimestamp(1.05)).toBe("00:00:01,050");
|
|
expect(formatSrtTimestamp(1.005)).toBe("00:00:01,005");
|
|
});
|
|
|
|
test("rolls over past one hour", () => {
|
|
expect(formatSrtTimestamp(3661.25)).toBe("01:01:01,250");
|
|
});
|
|
|
|
test("rounds sub-millisecond values", () => {
|
|
expect(formatSrtTimestamp(0.9996)).toBe("00:00:01,000");
|
|
});
|
|
|
|
test("clamps negatives to zero", () => {
|
|
expect(formatSrtTimestamp(-1)).toBe("00:00:00,000");
|
|
});
|
|
});
|
|
|
|
describe("transcriptToSrt", () => {
|
|
const chunk = (
|
|
text: string,
|
|
start: number,
|
|
end: number | null,
|
|
): TranscriptChunk => ({ text, timestamp: [start, end] });
|
|
|
|
test("formats numbered cues separated by blank lines", () => {
|
|
const srt = transcriptToSrt([
|
|
chunk(" Hello.", 0, 2.5),
|
|
chunk(" World.", 2.5, 4),
|
|
]);
|
|
expect(srt).toBe(
|
|
"1\n00:00:00,000 --> 00:00:02,500\nHello.\n" +
|
|
"\n" +
|
|
"2\n00:00:02,500 --> 00:00:04,000\nWorld.\n",
|
|
);
|
|
});
|
|
|
|
test("null end borrows the next chunk's start", () => {
|
|
const srt = transcriptToSrt([
|
|
chunk("One", 0, null),
|
|
chunk("Two", 3, 5),
|
|
]);
|
|
expect(srt).toContain("1\n00:00:00,000 --> 00:00:03,000\nOne");
|
|
});
|
|
|
|
test("null end on the final chunk falls back to start + 2s", () => {
|
|
const srt = transcriptToSrt([chunk("Tail", 10, null)]);
|
|
expect(srt).toBe("1\n00:00:10,000 --> 00:00:12,000\nTail\n");
|
|
});
|
|
|
|
test("skips empty and whitespace-only chunks (numbering stays contiguous)", () => {
|
|
const srt = transcriptToSrt([
|
|
chunk(" ", 0, 1),
|
|
chunk("Kept", 1, null),
|
|
chunk("", 2, 3),
|
|
chunk("Also kept", 4, 5),
|
|
]);
|
|
// The null end skips over the empty chunk to the next *captioned* start.
|
|
expect(srt).toBe(
|
|
"1\n00:00:01,000 --> 00:00:04,000\nKept\n" +
|
|
"\n" +
|
|
"2\n00:00:04,000 --> 00:00:05,000\nAlso kept\n",
|
|
);
|
|
});
|
|
|
|
test("cue never ends before it starts", () => {
|
|
// Out-of-order borrow: next captioned chunk starts before this one.
|
|
const srt = transcriptToSrt([chunk("A", 5, null), chunk("B", 4, 6)]);
|
|
expect(srt).toContain("00:00:05,000 --> 00:00:05,000");
|
|
});
|
|
|
|
test("no captioned chunks yields an empty string", () => {
|
|
expect(transcriptToSrt([])).toBe("");
|
|
expect(transcriptToSrt([chunk(" ", 0, 1)])).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("exportFilename", () => {
|
|
test("swaps the extension", () => {
|
|
expect(exportFilename("talk.m4a", "srt")).toBe("talk.srt");
|
|
expect(exportFilename("talk.m4a", "txt")).toBe("talk.txt");
|
|
});
|
|
|
|
test("only the last dot counts", () => {
|
|
expect(exportFilename("2026.07.14 meeting.wav", "srt")).toBe(
|
|
"2026.07.14 meeting.srt",
|
|
);
|
|
});
|
|
|
|
test("no extension keeps the whole name", () => {
|
|
expect(exportFilename("recording", "txt")).toBe("recording.txt");
|
|
});
|
|
|
|
test("leading dot is a hidden file, not an extension", () => {
|
|
expect(exportFilename(".bashrc", "txt")).toBe(".bashrc.txt");
|
|
});
|
|
|
|
test("null, undefined, and blank fall back to transcript.*", () => {
|
|
expect(exportFilename(null, "srt")).toBe("transcript.srt");
|
|
expect(exportFilename(undefined, "txt")).toBe("transcript.txt");
|
|
expect(exportFilename(" ", "srt")).toBe("transcript.srt");
|
|
});
|
|
});
|