147 lines
4.7 KiB
TypeScript
147 lines
4.7 KiB
TypeScript
import { renderToString } from "react-dom/server";
|
|
import { describe, expect, test } from "vitest";
|
|
import { Button } from "./Button";
|
|
import { DashboardFrame } from "./DashboardFrame";
|
|
import { FooterCell } from "./FooterCell";
|
|
import { IconButton } from "./IconButton";
|
|
import { ServiceRow } from "./ServiceRow";
|
|
import { TelemetryCard } from "./TelemetryCard";
|
|
import { dashboardPreviewFixtures } from "../fixtures";
|
|
|
|
describe("dashboard UI components", () => {
|
|
test("renders the primary generic dashboard fixture", () => {
|
|
const body = renderToString(
|
|
<DashboardFrame dashboard={dashboardPreviewFixtures.primary} />,
|
|
);
|
|
|
|
expect(body).toContain("Operations Console");
|
|
expect(body).toContain("Core Throughput");
|
|
expect(body).toContain("Queue Workers");
|
|
expect(body).toContain("data-icon-name=\"mdi:server-network\"");
|
|
expect(body).toContain("data-severity=\"warning\"");
|
|
expect(body).not.toContain("dashboard-title");
|
|
});
|
|
|
|
test("renders another generic dashboard fixture through the same component", () => {
|
|
const body = renderToString(
|
|
<DashboardFrame dashboard={dashboardPreviewFixtures.secondary} />,
|
|
);
|
|
|
|
expect(body).toContain("Support Desk");
|
|
expect(body).toContain("Response Window");
|
|
expect(body).toContain("Regional Nodes");
|
|
expect(body).toContain("data-icon-name=\"mdi:headset\"");
|
|
expect(body).toContain("data-severity=\"loading\"");
|
|
});
|
|
|
|
test("renders optional service and status links as focusable anchors", () => {
|
|
const service = renderToString(
|
|
<ServiceRow
|
|
service={{
|
|
id: "linked-service",
|
|
label: "Linked Service",
|
|
description: "Generic linked destination",
|
|
severity: "ok",
|
|
detail: "ready",
|
|
link: { href: "https://example.test/service", external: true },
|
|
}}
|
|
/>,
|
|
);
|
|
const footer = renderToString(
|
|
<FooterCell
|
|
item={{
|
|
id: "linked-status",
|
|
label: "Linked Status",
|
|
value: "open",
|
|
severity: "neutral",
|
|
link: { href: "https://example.test/status" },
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(service).toContain("<a ");
|
|
expect(service).toContain("href=\"https://example.test/service\"");
|
|
expect(service).toContain("target=\"_blank\"");
|
|
expect(footer).toContain("<a ");
|
|
expect(footer).toContain("href=\"https://example.test/status\"");
|
|
});
|
|
|
|
test("renders stable model IDs on group and status containers", () => {
|
|
const body = renderToString(
|
|
<DashboardFrame dashboard={dashboardPreviewFixtures.primary} />,
|
|
);
|
|
|
|
expect(body).toContain("data-model-id=\"queue-workers\"");
|
|
expect(body).toContain("data-model-id=\"dashboard-status\"");
|
|
});
|
|
|
|
test("does not render progress bars for non-percent metrics without explicit progress", () => {
|
|
const withoutProgress = renderToString(
|
|
<TelemetryCard
|
|
card={{
|
|
id: "bytes",
|
|
label: "Bytes Metric",
|
|
value: { kind: "bytes", value: 2048 },
|
|
severity: "neutral",
|
|
}}
|
|
/>,
|
|
);
|
|
const withProgress = renderToString(
|
|
<TelemetryCard
|
|
card={{
|
|
id: "bytes-with-progress",
|
|
label: "Bytes With Progress",
|
|
value: { kind: "bytes", value: 2048 },
|
|
progress: 42,
|
|
severity: "neutral",
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(withoutProgress).not.toContain("telemetry-card__bar");
|
|
expect(withProgress).toContain("--metric-progress:42%");
|
|
});
|
|
|
|
test("renders telemetry trends through the uPlot chart surface", () => {
|
|
const body = renderToString(
|
|
<TelemetryCard
|
|
card={{
|
|
id: "trend-card",
|
|
label: "Trend Card",
|
|
value: { kind: "percent", value: 64 },
|
|
sparkline: [18, 24, 64],
|
|
severity: "ok",
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(body).toContain('data-chart-library="uplot"');
|
|
});
|
|
|
|
test("base button controls forward native attributes", () => {
|
|
const button = renderToString(
|
|
<Button
|
|
label="Refresh"
|
|
id="refresh-action"
|
|
className="custom-action"
|
|
aria-controls="refresh-target"
|
|
/>,
|
|
);
|
|
const iconButton = renderToString(
|
|
<IconButton
|
|
icon="mdi:refresh"
|
|
label="Refresh status"
|
|
id="refresh-icon-action"
|
|
className="custom-icon-action"
|
|
aria-expanded="false"
|
|
/>,
|
|
);
|
|
|
|
expect(button).toContain("id=\"refresh-action\"");
|
|
expect(button).toContain("class=\"ui-button custom-action ");
|
|
expect(button).toContain("aria-controls=\"refresh-target\"");
|
|
expect(iconButton).toContain("id=\"refresh-icon-action\"");
|
|
expect(iconButton).toContain("class=\"icon-button custom-icon-action ");
|
|
expect(iconButton).toContain("aria-expanded=\"false\"");
|
|
});
|
|
});
|