Files
WRNexusJS/packages/ui/test/chart.test.ts
T
Clintchiz 9520651330
Quality / quality (ubuntu-latest) (push) Failing after 10m12s
Quality / quality (windows-latest) (push) Canceled after 0s
fix(studio): render complete interactive gallery previews
2026-10-19 17:40:50 +05:30

113 lines
4.5 KiB
TypeScript

import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import {
chartConfiguration,
chartTypes,
chartTickLabel,
chartTooltipLines,
} from "../src/chart-config";
import { renderComponent, mountHtml } from "../../test/src/index";
import { discoverPlugins, createPluginRunner } from "../../plugin/src/index";
import { resolve } from "node:path";
const source = readFileSync(new URL("../components/Chart.wrn", import.meta.url), "utf8");
test("compact category ticks preserve the complete tooltip text", () => {
const label = "2026-10-17T10:03:00 · completed · audit-four";
expect(chartTickLabel(label, 18)).toHaveLength(18);
expect(chartTickLabel(label, 18)).toContain("…");
expect(chartTickLabel("Short", 18)).toBe("Short");
const lines = chartTooltipLines(label, 18);
expect(lines.every((line) => line.length <= 18)).toBe(true);
expect(lines.join("")).toBe(label);
});
for (const type of chartTypes) {
test(`Chart.js ${type} preserves native datasets`, () => {
const data = {
labels: ["A", "B"],
datasets: [
{
label: "Example",
data: type === "scatter" || type === "bubble" ? [{ x: 1, y: -2, r: 5 }] : [-20, 40],
},
],
};
const config = chartConfiguration({ type, data });
expect(config.type).toBe(type);
expect(config.data).toEqual(data);
expect(config.data).not.toBe(data);
if (["pie", "doughnut", "radar", "polarArea"].includes(type))
expect(config.options?.scales).toBeUndefined();
});
}
test("legacy rows preserve negatives, gaps, zero, and small decimal values", () => {
const config = chartConfiguration({
items: [
{ label: "Loss", value: -20 },
{ label: "Gap", value: null },
{ label: "Invalid", value: "oops" },
{ label: "Zero", value: 0 },
{ label: "Tiny", value: 0.0002 },
],
});
expect(config.data.datasets[0].data).toEqual([-20, null, null, 0, 0.0002]);
expect(config.data.labels).toEqual(["Loss", "Gap", "Invalid", "Zero", "Tiny"]);
});
test("native dataset options, mixed types, area fills, and stacked horizontal axes survive", () => {
const data = {
labels: ["A"],
datasets: [
{ type: "bar" as const, data: [20] },
{ type: "line" as const, data: [10], borderColor: "red" },
],
};
const options = {
indexAxis: "y",
scales: { x: { stacked: true }, y: { stacked: true } },
plugins: { legend: { display: false } },
};
const config = chartConfiguration({ type: "bar", data, options });
expect(config.data).toEqual(data);
expect(config.options?.indexAxis).toBe("y");
expect((config.options?.scales?.x as any)?.stacked).toBe(true);
expect(config.options?.plugins?.legend?.display).toBe(false);
const area = chartConfiguration({ type: "area", data: { datasets: [{ data: [1, 2] }] } });
expect(area.type).toBe("line");
expect((area.data.datasets[0] as any).fill).toBe(true);
expect(() => chartConfiguration({ type: "unknown" })).toThrow("Unsupported chart type");
});
test("separate datasets and custom legacy keys remain supported", () => {
expect(chartConfiguration({ labels: ["A"], datasets: [{ data: [3] }] }).data).toEqual({
labels: ["A"],
datasets: [{ data: [3] }],
});
expect(
chartConfiguration({ items: [{ name: "A", amount: -2 }], labelKey: "name", valueKey: "amount" })
.data.datasets[0].data,
).toEqual([-2]);
});
test("SSR safely carries native chart config and typed public outputs", async () => {
const data = { labels: ["<script>bad</script>"], datasets: [{ label: "Sample", data: [-2] }] };
const dom = mountHtml(await renderComponent(source, { type: "radar", data }));
const root = dom.querySelector('[data-ui-component="Chart"]') as HTMLElement;
expect(root.dataset.wrnexusRuntime).toBe("chartjs");
expect(JSON.parse(root.dataset.chartConfig!).data).toEqual(data);
expect(root.querySelector("canvas")?.getAttribute("role")).toBe("img");
expect(root.querySelector("script")).toBeNull();
});
test("UI package discovers its bundled Chart.js runtime automatically", async () => {
const plugins = await discoverPlugins(resolve("examples/component-showcase"), [], {
strict: true,
});
const runner = createPluginRunner(plugins, {
root: resolve("examples/component-showcase"),
mode: "development",
command: "dev",
metadata: new Map(),
warn: () => {},
});
const contributions = await runner.contributions();
expect(
contributions.clientRuntimes.some((runtime) => runtime.id === "chartjs" && runtime.bundle),
).toBe(true);
});