176 lines
5.6 KiB
TypeScript
176 lines
5.6 KiB
TypeScript
import { renderToString } from "react-dom/server";
|
|
import { describe, expect, test } from "vitest";
|
|
import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
|
|
import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
|
|
import {
|
|
AppStateView,
|
|
dashboardTileMatchKey,
|
|
dashboardHydrationTiles,
|
|
restoreDashboardTileSnapshots,
|
|
} from "./App";
|
|
|
|
describe("React app dashboard state view", () => {
|
|
test("renders loading dashboard state", () => {
|
|
const html = renderToString(
|
|
<AppStateView
|
|
dashboard={{
|
|
state: "loading",
|
|
title: "Loading Dashboard",
|
|
subtitle: "Fetching active model",
|
|
message: "Waiting for the active dashboard document.",
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(html).toContain("Loading Dashboard");
|
|
expect(html).toContain("Fetching active model");
|
|
});
|
|
|
|
test("renders an accessible theme toggle with the active theme", () => {
|
|
const html = renderToString(
|
|
<AppStateView
|
|
dashboard={{
|
|
state: "loading",
|
|
title: "Loading Dashboard",
|
|
subtitle: "Fetching active model",
|
|
message: "Waiting for the active dashboard document.",
|
|
}}
|
|
theme="dark"
|
|
onThemeChange={() => undefined}
|
|
/>,
|
|
);
|
|
|
|
expect(html).toContain('data-ui-theme-toggle="true"');
|
|
expect(html).toContain('data-ui-theme-current="dark"');
|
|
expect(html).toContain('aria-label="Light theme"');
|
|
expect(html).toContain('aria-pressed="false"');
|
|
expect(html).toContain("Theme");
|
|
expect(html).toContain("Dark");
|
|
expect(html).toContain("Light");
|
|
});
|
|
|
|
test("renders the dashboard shell while individual items hydrate", () => {
|
|
const html = renderToString(
|
|
<AppStateView
|
|
dashboard={{
|
|
state: "ready",
|
|
document: genericDashboardFixture,
|
|
schemaVersion: "dashboard.v1",
|
|
currentRevisionId: "revision-1234567890",
|
|
}}
|
|
hydratingItemIds={new Set([
|
|
"telemetry:service-uptime",
|
|
"service:core-services:identity",
|
|
"module:ambient",
|
|
"status:runtime:status",
|
|
])}
|
|
/>,
|
|
);
|
|
|
|
expect(html).toContain("Operations Console");
|
|
expect(html).toContain("Service Uptime");
|
|
expect(html).toContain("Identity");
|
|
expect(html).toContain("Environment");
|
|
expect(html).not.toContain("Loading Dashboard");
|
|
expect(html).toContain(
|
|
'data-severity="loading" data-model-id="service-uptime"',
|
|
);
|
|
expect(html).toContain('data-severity="loading" data-model-id="identity"');
|
|
expect(html).toContain('data-severity="loading" data-model-id="ambient"');
|
|
expect(html).toContain('data-severity="loading" data-model-id="runtime:status"');
|
|
});
|
|
|
|
test("hydrates every status cell from the Dimension Lab shell", () => {
|
|
expect(dashboardHydrationTiles(dimensionLabDashboardFixture)).toContainEqual({
|
|
kind: "status",
|
|
stripId: "footer-status",
|
|
id: "auto-refresh",
|
|
});
|
|
});
|
|
|
|
test("prioritizes status, telemetry, modules, then services for hydration", () => {
|
|
const tiles = dashboardHydrationTiles(dimensionLabDashboardFixture);
|
|
const kinds = tiles.map((tile) => tile.kind);
|
|
const firstServiceIndex = kinds.indexOf("service");
|
|
|
|
expect(kinds.slice(0, 5)).toEqual([
|
|
"status",
|
|
"status",
|
|
"status",
|
|
"status",
|
|
"status",
|
|
]);
|
|
expect(kinds.lastIndexOf("telemetry")).toBeLessThan(kinds.indexOf("module"));
|
|
expect(kinds.lastIndexOf("module")).toBeLessThan(firstServiceIndex);
|
|
expect(
|
|
tiles.filter((tile) => tile.kind === "telemetry").map((tile) => tile.id),
|
|
).toEqual(
|
|
dimensionLabDashboardFixture.telemetry
|
|
.filter((card) => card.datasource?.type === "external")
|
|
.map((card) => card.id),
|
|
);
|
|
expect(
|
|
tiles.filter((tile) => tile.kind === "service").map((tile) => ({
|
|
groupId: tile.groupId,
|
|
id: tile.id,
|
|
})),
|
|
).toEqual(
|
|
dimensionLabDashboardFixture.serviceGroups.flatMap((group) =>
|
|
group.services
|
|
.filter((service) => service.datasource?.type === "external")
|
|
.map((service) => ({
|
|
groupId: group.id,
|
|
id: service.id,
|
|
}))
|
|
),
|
|
);
|
|
});
|
|
|
|
test("applies restored tile snapshots without marking them as loading", () => {
|
|
const restored = restoreDashboardTileSnapshots(
|
|
{
|
|
state: "ready",
|
|
document: dimensionLabDashboardFixture,
|
|
schemaVersion: "dashboard.v1",
|
|
currentRevisionId: "revision-a",
|
|
},
|
|
[
|
|
{
|
|
ageMs: 60_000,
|
|
response: {
|
|
state: "ready",
|
|
tile: { kind: "telemetry", id: "infra-ram" },
|
|
item: {
|
|
...dimensionLabDashboardFixture.telemetry[0],
|
|
detail: "cached - stale 60s",
|
|
severity: "stale",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
);
|
|
|
|
expect(restored.restoredItemIds).toEqual(new Set(["telemetry:infra-ram"]));
|
|
if (restored.dashboard.state !== "ready") {
|
|
throw new Error("Expected dashboard to be ready");
|
|
}
|
|
expect(restored.dashboard.document.telemetry[0]).toMatchObject({
|
|
id: "infra-ram",
|
|
detail: "cached - stale 60s",
|
|
severity: "stale",
|
|
});
|
|
});
|
|
|
|
test("uses structured tile match keys for delimiter-bearing ids", () => {
|
|
expect(
|
|
dashboardTileMatchKey({ kind: "service", groupId: "a:b", id: "c" }),
|
|
).not.toBe(
|
|
dashboardTileMatchKey({ kind: "service", groupId: "a", id: "b:c" }),
|
|
);
|
|
expect(
|
|
dashboardTileMatchKey({ kind: "status", stripId: "a:b", id: "c" }),
|
|
).not.toBe(
|
|
dashboardTileMatchKey({ kind: "status", stripId: "a", id: "b:c" }),
|
|
);
|
|
});
|
|
});
|