99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
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("Текст");
|
|
});
|
|
});
|