132 lines
4.2 KiB
TypeScript
132 lines
4.2 KiB
TypeScript
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<Record<string, string | null>>) {
|
|
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" });
|
|
});
|
|
});
|