import { describe, expect, it } from "vitest"; import { MAX_BROWSER_SPREADSHEET_BYTES, assertSpreadsheetFile, parseSpreadsheet, } from "./excel.js"; async function createWorkbookFile( rows: unknown[][], fileName = "spreadsheet.xlsx", ): Promise { const ExcelJS = await import("exceljs"); const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet("Sheet1"); for (const row of rows) { worksheet.addRow(row); } const buffer = await workbook.xlsx.writeBuffer(); return new File([buffer], fileName, { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); } describe("excel import helpers", () => { it("parses csv files with quoted values and skips blank rows", async () => { const file = new File( ['name,role\n"Alice, A.",Engineer\n\nBob,Producer\n'], "people.csv", { type: "text/csv" }, ); await expect(parseSpreadsheet(file)).resolves.toEqual([ { name: "Alice, A.", role: "Engineer" }, { name: "Bob", role: "Producer" }, ]); }); it("parses xlsx files and normalizes date cells to ISO strings", async () => { const file = await createWorkbookFile([ ["name", "startDate", "active"], ["Alice", new Date("2026-03-30T09:15:00.000Z"), true], ]); await expect(parseSpreadsheet(file)).resolves.toEqual([ { name: "Alice", startDate: "2026-03-30T09:15:00.000Z", active: "true", }, ]); }); it("rejects duplicate headers in xlsx imports", async () => { const file = await createWorkbookFile([ ["Name", "name"], ["Alice", "Producer"], ]); await expect(parseSpreadsheet(file)).rejects.toThrow('duplicate header "name"'); }); it("rejects legacy .xls uploads before parsing", () => { const file = new File(["legacy"], "legacy.xls", { type: "application/vnd.ms-excel", }); expect(() => assertSpreadsheetFile(file)).toThrow( "Legacy .xls files are not supported.", ); }); it("rejects oversized spreadsheet uploads before parsing", () => { const file = new File([Buffer.alloc(MAX_BROWSER_SPREADSHEET_BYTES + 1)], "oversized.xlsx", { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); expect(() => assertSpreadsheetFile(file)).toThrow( `The selected file exceeds the ${MAX_BROWSER_SPREADSHEET_BYTES} byte limit`, ); }); });