import { describe, expect, it } from "vitest"; import { cellId, columnLabel, columnLabelsForCount, evaluateCell, expandRange, formatCellValueForDisplay, mergedCellInfo, normalizeCellRange, parseCellId, summarizeColumn, usedSheetBounds } from "./spreadsheet"; describe("spreadsheet utilities", () => { it("expands rectangular ranges", () => { expect(expandRange("A1:B2")).toEqual(["A1", "B1", "A2", "B2"]); }); it("supports Excel-style columns beyond H", () => { expect(columnLabel(0)).toBe("A"); expect(columnLabel(25)).toBe("Z"); expect(columnLabel(26)).toBe("AA"); expect(columnLabelsForCount(28).slice(-2)).toEqual(["AA", "AB"]); expect(cellId(27, 19)).toBe("AB20"); expect(parseCellId("ab20")).toEqual({ column: 27, row: 19 }); expect(expandRange("Z1:AA2")).toEqual(["Z1", "AA1", "Z2", "AA2"]); }); it("evaluates aggregate formulas", () => { const cells = { A1: "2", A2: "3", A3: "=SUM(A1:A2)", A4: "=AVG(A1:A2)" }; expect(evaluateCell("A3", cells)).toBe("5"); expect(evaluateCell("A4", cells)).toBe("2.50"); }); it("evaluates arithmetic formulas with cell references", () => { const cells = { A1: "10", B1: "5", C1: "=A1*B1+2" }; expect(evaluateCell("C1", cells)).toBe("52"); }); it("evaluates formulas outside the default visible grid", () => { const cells = { AA20: "7", AB20: "8", AC20: "=SUM(AA20:AB20)", AD20: "=AC20*2" }; expect(evaluateCell("AC20", cells)).toBe("15"); expect(evaluateCell("AD20", cells)).toBe("30"); }); it("reports cycles instead of silently treating them as zero", () => { const cells = { A1: "=SUM(A1:A2)", A2: "2", B1: "=B1+1" }; expect(evaluateCell("A1", cells)).toBe("#CYCLE"); expect(evaluateCell("B1", cells)).toBe("#CYCLE"); }); it("calculates used sheet bounds from populated cells", () => { expect(usedSheetBounds({ C3: "x", AA20: "y" })).toEqual({ columnCount: 27, rowCount: 20 }); expect(usedSheetBounds({}, [{ start: "A1", end: "J24" }])).toEqual({ columnCount: 10, rowCount: 24 }); expect(usedSheetBounds({})).toEqual({ columnCount: 8, rowCount: 16 }); }); it("normalizes and locates merged cell ranges", () => { expect(normalizeCellRange({ start: "c2", end: "A1" })).toEqual({ start: "A1", end: "C2" }); expect(mergedCellInfo("B1", [{ start: "A1", end: "C2" }])).toEqual({ range: { start: "A1", end: "C2" }, isOrigin: false, columnSpan: 3, rowSpan: 2 }); expect(mergedCellInfo("A1", [{ start: "A1", end: "C2" }])?.isOrigin).toBe(true); }); it("uses the final aggregate formula as the column summary when present", () => { const cells = { E2: "=SUM(B2:D2)", E3: "=SUM(B3:D3)", E4: "=SUM(B4:D4)", E6: "=SUM(E2:E4)", B2: "12500", C2: "14300", D2: "16800", B3: "8200", C3: "9100", D3: "9900", B4: "19600", C4: "21400", D4: "23800" }; expect(summarizeColumn(cells, 4)).toBe(135600); }); it("formats numeric cell display values from XLSX number formats", () => { expect(formatCellValueForDisplay("44927", { numberFormat: "dd.mm.yyyy" })).toBe("01.01.2023"); expect(formatCellValueForDisplay("44927.5", { numberFormat: "dd.mm.yyyy hh:mm" })).toBe("01.01.2023 12:00"); expect(formatCellValueForDisplay("0.25", { numberFormat: "0%" })).toBe("25%"); expect(formatCellValueForDisplay("1250.5", { numberFormat: "# ##0.00 ₽" })).toBe("1 250,50 ₽"); expect(formatCellValueForDisplay("Текст", { numberFormat: "0.00" })).toBe("Текст"); }); });