1import { describe, expect, it } from "vite-plus/test";
2import { filePreviewDelimiter, parseDelimitedPreview } from "./delimitedPreview.ts";
3
4describe("delimited file previews", () => {
5 it("respects a specific MIME type before the extension", () => {
6 expect(filePreviewDelimiter({ name: "data.CSV", mimeType: "text/plain" })).toBe(",");
7 expect(filePreviewDelimiter({ name: "data.csv", mimeType: "application/pdf" })).toBeNull();
8 });
9 it("preserves quoted separators, escaped quotes, blank cells and line breaks", () => {
10 expect(
11 parseDelimitedPreview('\ufeffname,notes,empty\r\n"A, B","say ""hi""\nagain",\r\n', ","),
12 ).toEqual({
13 rows: [
14 ["name", "notes", "empty"],
15 ["A, B", 'say "hi"\nagain', ""],
16 ],
17 truncated: false,
18 });
19 expect(parseDelimitedPreview("one\ttwo\n\tthree", "\t").rows).toEqual([
20 ["one", "two"],
21 ["", "three"],
22 ]);
23 });
24 describe.each([",", "\t"] as const)("with delimiter %j", (delimiter) => {
25 it("preserves a final quoted empty record without requiring a line ending", () => {
26 for (const ending of ["", "\n", "\r\n"]) {
27 expect(parseDelimitedPreview(`name\r\nAlice\r\n""${ending}`, delimiter)).toEqual({
28 rows: [["name"], ["Alice"], [""]],
29 truncated: false,
30 });
31 }
32 expect(parseDelimitedPreview('""', delimiter)).toEqual({
33 rows: [[""]],
34 truncated: false,
35 });
36 });
37 it("does not invent records for empty input or a trailing line ending", () => {
38 for (const prefix of ["", "\ufeff"]) {
39 expect(parseDelimitedPreview(prefix, delimiter).rows).toEqual([]);
40 expect(parseDelimitedPreview(`${prefix}name\r\n`, delimiter).rows).toEqual([["name"]]);
41 expect(parseDelimitedPreview(`${prefix}""`, delimiter).rows).toEqual([[""]]);
42 }
43 });
44 it("keeps a quoted empty record at the row limit", () => {
45 const text = `${"name\n".repeat(99)}""`;
46 const preview = parseDelimitedPreview(text, delimiter);
47 expect(preview.rows).toHaveLength(100);
48 expect(preview.rows.at(-1)).toEqual([""]);
49 expect(preview.truncated).toBe(false);
50 expect(parseDelimitedPreview(`${text}\nextra`, delimiter).truncated).toBe(true);
51 });
52 });
53 it("bounds rows, columns and cell length and reports partial content", () => {
54 const table = parseDelimitedPreview(
55 Array.from({ length: 101 }, () =>
56 Array.from({ length: 31 }, () => "x".repeat(2001)).join(","),
57 ).join("\n"),
58 ",",
59 );
60 expect(table.truncated).toBe(true);
61 expect(table.rows).toHaveLength(100);
62 expect(table.rows[0]).toHaveLength(30);
63 expect(table.rows[0]?.[0]).toHaveLength(2000);
64 expect(parseDelimitedPreview('a,"unfinished', ",").truncated).toBe(true);
65 });
66});