159 lines
4.5 KiB
TypeScript
159 lines
4.5 KiB
TypeScript
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { PERMS } from "@/lib/permission-slugs";
|
|
import {
|
|
anyCapability,
|
|
type HousekeepingRouteDefinition,
|
|
ok,
|
|
} from "../../../foundation/contracts";
|
|
import { defaultHousekeepingPreferences } from "../../../foundation/preferences/schema";
|
|
import type { HousekeepingWidgetOption } from "../../../foundation/preferences/widget-options";
|
|
import type { OperationsWorkspaceLoadResult } from "../queries";
|
|
import { OperationsWorkspacePage } from "./workspace";
|
|
|
|
const capability = anyCapability(PERMS.USERS_VIEW);
|
|
const routes: readonly HousekeepingRouteDefinition[] = [
|
|
{
|
|
id: "people.users",
|
|
labelKey: "Users",
|
|
href: "/ase/people/users",
|
|
capability,
|
|
},
|
|
];
|
|
const widgets: readonly HousekeepingWidgetOption[] = [
|
|
{
|
|
id: "people.queue",
|
|
kind: "mandatory",
|
|
},
|
|
];
|
|
const preferences = {
|
|
...defaultHousekeepingPreferences(),
|
|
pinnedRouteIds: ["people.users"],
|
|
shortcutOrder: ["people.users"],
|
|
widgetOrder: ["people.queue"],
|
|
};
|
|
const result: OperationsWorkspaceLoadResult = {
|
|
model: {
|
|
inbox: {
|
|
items: [
|
|
{
|
|
sourceId: "people.queue",
|
|
itemId: "ticket-1",
|
|
deduplicationKey: "people.queue:ticket-1",
|
|
domain: "people",
|
|
capability,
|
|
severity: "warning",
|
|
priority: "high",
|
|
ageMs: 1_000,
|
|
state: "open",
|
|
occurredAt: "2026-08-30T10:00:00.000Z",
|
|
titleKey: "Ticket",
|
|
href: "/ase/people/users",
|
|
freshness: "fresh",
|
|
actions: [],
|
|
},
|
|
],
|
|
errors: [{ sourceId: "system.alerts", code: "TIMEOUT" }],
|
|
correlationId: "inbox",
|
|
},
|
|
recent: [
|
|
{
|
|
routeId: "people.users",
|
|
canonicalHref: "/ase/people/users",
|
|
labelKey: "Users",
|
|
occurredAt: "2026-08-30T09:00:00.000Z",
|
|
source: "route-visit",
|
|
},
|
|
],
|
|
favorites: preferences,
|
|
widgets: {
|
|
widgets: [{ id: "people.queue", data: { count: 1 } }],
|
|
errors: [{ widgetId: "system.health", code: "TIMEOUT" }],
|
|
},
|
|
},
|
|
errors: [{ section: "recent", code: "DEPENDENCY_UNAVAILABLE" }],
|
|
correlationId: "workspace",
|
|
};
|
|
|
|
const savePreferences = vi.fn(async (value) => ok(value, "saved"));
|
|
const labels = {
|
|
title: "Operations workspace",
|
|
description: "Capability-derived operational home",
|
|
noDomainsTitle: "No operational domains are available",
|
|
noDomainsDescription: "No authorized domain",
|
|
partial: "Some workspace sections are unavailable",
|
|
inbox: {
|
|
title: "Operational inbox",
|
|
domainFilter: "Filter by domain",
|
|
stateFilter: "Filter by state",
|
|
priorityFilter: "Filter by priority",
|
|
all: "All",
|
|
loading: "Loading",
|
|
empty: "Empty",
|
|
partial: "Some sources are unavailable",
|
|
open: "Open",
|
|
},
|
|
recent: { title: "Recent work", empty: "Nothing recent" },
|
|
favorites: {
|
|
title: "Favorites",
|
|
pin: "Pin",
|
|
unpin: "Unpin",
|
|
error: "Preferences could not be saved",
|
|
reorderInstructions: "Press Space, then use arrow keys.",
|
|
},
|
|
widgetSettings: {
|
|
title: "Widget settings",
|
|
mandatory: "Mandatory",
|
|
error: "Preferences could not be saved",
|
|
},
|
|
widgets: {
|
|
title: "Widgets",
|
|
partial: "Some widgets are unavailable",
|
|
error: "Widget order could not be saved",
|
|
reorderInstructions: "Press Space, then use arrow keys.",
|
|
},
|
|
} as const;
|
|
const routeTitles = { Users: "Users" };
|
|
const widgetTitles = { "people.queue": "People queue" };
|
|
|
|
describe("OperationsWorkspacePage", () => {
|
|
it("renders capability-filtered sections, partial failures, and preview links", () => {
|
|
const html = renderToStaticMarkup(
|
|
<OperationsWorkspacePage
|
|
result={result}
|
|
routes={routes}
|
|
widgetDefinitions={widgets}
|
|
labels={labels}
|
|
routeTitles={routeTitles}
|
|
widgetTitles={widgetTitles}
|
|
savePreferences={savePreferences}
|
|
/>,
|
|
);
|
|
expect(html).toContain("Operations workspace");
|
|
expect(html).toContain("Operational inbox");
|
|
expect(html).toContain("Recent work");
|
|
expect(html).toContain("Favorites");
|
|
expect(html).toContain("Widget settings");
|
|
expect(html).toContain("Some workspace sections are unavailable");
|
|
expect(html).not.toContain("system.alerts");
|
|
expect(html).not.toContain("system.health");
|
|
expect(html).toContain('href="/ase/people/users"');
|
|
});
|
|
|
|
it("renders an explicit state when no operational domain is accessible", () => {
|
|
const html = renderToStaticMarkup(
|
|
<OperationsWorkspacePage
|
|
result={result}
|
|
routes={[]}
|
|
widgetDefinitions={[]}
|
|
labels={labels}
|
|
routeTitles={{}}
|
|
widgetTitles={{}}
|
|
savePreferences={savePreferences}
|
|
/>,
|
|
);
|
|
expect(html).toContain("No operational domains are available");
|
|
expect(html).not.toContain("Operational inbox");
|
|
});
|
|
});
|