204 lines
6.6 KiB
TypeScript
204 lines
6.6 KiB
TypeScript
import { renderToString } from "react-dom/server";
|
|
import { describe, expect, test } from "vitest";
|
|
import {
|
|
Button,
|
|
DashboardFrame,
|
|
FooterCell,
|
|
IconButton,
|
|
ServiceRow,
|
|
StatusStrip,
|
|
TelemetryCard,
|
|
TelemetryGrid,
|
|
ThemeToggle,
|
|
} from "../index";
|
|
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("console-header");
|
|
expect(body).toContain("telemetry-section");
|
|
expect(body).toContain("6 Metrics");
|
|
expect(body).toContain('data-variant="system-bus"');
|
|
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(service).toContain('class="service-row__status"');
|
|
expect(service).toContain('class="service-row__launch"');
|
|
expect(footer).toContain("<a ");
|
|
expect(footer).toContain("href=\"https://example.test/status\"");
|
|
});
|
|
|
|
test("renders the status strip as a compact system bus", () => {
|
|
const body = renderToString(
|
|
<StatusStrip
|
|
id="dashboard-status"
|
|
items={[
|
|
{ id: "system", label: "System", value: "Nominal", severity: "ok" },
|
|
{ id: "refresh", label: "Refresh", value: "15s", severity: "neutral" },
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(body).toContain('class="status-strip"');
|
|
expect(body).toContain('data-variant="system-bus"');
|
|
expect(body).toContain("Nominal");
|
|
});
|
|
|
|
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("renders telemetry as a framed instrument section with indexed cards", () => {
|
|
const body = renderToString(
|
|
<TelemetryGrid cards={dashboardPreviewFixtures.primary.telemetry.slice(0, 2)} />,
|
|
);
|
|
|
|
expect(body).toContain('class="telemetry-section"');
|
|
expect(body).toContain("Telemetry");
|
|
expect(body).toContain("2 Metrics");
|
|
expect(body).toContain('data-metric-index="01"');
|
|
expect(body).toContain('class="telemetry-card__index"');
|
|
expect(body).toContain('class="telemetry-card__source"');
|
|
});
|
|
|
|
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\"");
|
|
});
|
|
|
|
test("renders the theme toggle as a segmented instrument control", () => {
|
|
const body = renderToString(
|
|
<ThemeToggle theme="light" onThemeChange={() => undefined} />,
|
|
);
|
|
|
|
expect(body).toContain('class="theme-toggle"');
|
|
expect(body).toContain('data-ui-theme-current="light"');
|
|
expect(body).toContain('aria-label="Light theme"');
|
|
expect(body).toContain('aria-pressed="true"');
|
|
expect(body).toContain('class="theme-toggle__label"');
|
|
expect(body).toContain('class="theme-toggle__switch"');
|
|
expect(body).toContain('data-active="true"');
|
|
expect(body).toContain("Theme");
|
|
expect(body).toContain("Dark");
|
|
expect(body).toContain("Light");
|
|
});
|
|
});
|