import { describe, expect, it } from "vitest"; import { prepareComparisonChart } from "./comparison"; const sourceId = "1100db26-5efc-459b-b208-3b4a33cc0d16"; const config = { dataSourceId: sourceId, categoryField: "name", series: [ { field: "damage", label: "ดาเมจ", color: "chart-1" }, { field: "support", label: "สนับสนุน", color: "chart-2" }, ], presentation: "grouped-bar", normalization: "raw", sort: "authored", showValues: true, showLegend: true, tableFallback: true, } as const; function source(rows: Array>) { return { id: sourceId, name: "เปรียบเทียบ", version: 1, fields: [ { key: "name", label: "ชื่อ", type: "text" as const }, { key: "damage", label: "ดาเมจ", type: "number" as const }, { key: "support", label: "สนับสนุน", type: "number" as const }, ], rows: rows.map((values, index) => ({ id: `00000000-0000-4000-8000-${String(index + 1).padStart(12, "0")}`, values, })), }; } describe("comparison chart preparation", () => { it("preserves exact raw values and authored order", () => { const result = prepareComparisonChart( config, source([ { name: "A", damage: "9007199254740991.25", support: "2" }, { name: "B", damage: "-3", support: "4" }, ]), ); expect(result.status).toBe("ready"); if (result.status === "ready") { expect(result.rows.map((row) => row.category)).toEqual(["A", "B"]); expect(result.rows[0].exactValues.damage).toBe("9007199254740991.25"); expect(result.rows[1].values.damage).toBe(-3); } }); it("sorts by the first authored series", () => { const result = prepareComparisonChart( { ...config, sort: "descending" }, source([ { name: "ต่ำ", damage: "1", support: "20" }, { name: "สูง", damage: "10", support: "2" }, ]), ); expect(result.status === "ready" && result.rows.map((row) => row.category)).toEqual([ "สูง", "ต่ำ", ]); }); it("normalizes percent totals and baseline 100 deterministically", () => { const percent = prepareComparisonChart( { ...config, normalization: "percent-of-total" }, source([{ name: "A", damage: "1", support: "3" }]), ); expect(percent.status === "ready" && percent.rows[0].normalizedValues).toEqual({ damage: "25", support: "75", }); const baseline = prepareComparisonChart( { ...config, normalization: "baseline-100" }, source([ { name: "ฐาน", damage: "4", support: "10" }, { name: "ใหม่", damage: "6", support: "5" }, ]), ); expect(baseline.status === "ready" && baseline.rows[1].normalizedValues).toEqual({ damage: "150", support: "50", }); }); it("returns typed errors for negative percentages, formula failures, and crowded data", () => { expect( prepareComparisonChart( { ...config, normalization: "percent-of-total" }, source([{ name: "A", damage: "-1", support: "3" }]), ), ).toMatchObject({ status: "error", code: "incompatible-normalization" }); expect( prepareComparisonChart( config, source([{ name: "A", damage: "", support: "3" }]), ), ).toMatchObject({ status: "error", code: "invalid-value", field: "damage" }); expect( prepareComparisonChart( config, source( Array.from({ length: 21 }, (_, index) => ({ name: String(index), damage: "1", support: "1", })), ), ), ).toMatchObject({ status: "error", code: "too-many-categories" }); }); it("returns useful empty and missing-field states", () => { expect(prepareComparisonChart(config, undefined)).toMatchObject({ status: "empty" }); expect(prepareComparisonChart(config, source([]))).toMatchObject({ status: "empty" }); expect( prepareComparisonChart( { ...config, categoryField: "missing" }, source([{ name: "A", damage: "1", support: "2" }]), ), ).toMatchObject({ status: "error", code: "missing-field", field: "missing" }); }); });