diff --git a/src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx b/src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx
index b835b231..cbb7cd48 100644
--- a/src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx
+++ b/src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx
@@ -40,7 +40,8 @@ describe("HousekeepingPageState", () => {
);
expect(loading).toContain('aria-live="polite"');
- expect(partial).toContain('data-state-tone="warning"');
+ expect(partial).toContain("--admin-warning-border");
+ expect(partial).toContain("--admin-warning-subtle");
expect(partial).toContain(">Partial title<");
});
diff --git a/src/features/housekeeping/foundation/page/housekeeping-page-state.tsx b/src/features/housekeeping/foundation/page/housekeeping-page-state.tsx
index dc2e2a56..c980e45e 100644
--- a/src/features/housekeeping/foundation/page/housekeeping-page-state.tsx
+++ b/src/features/housekeeping/foundation/page/housekeeping-page-state.tsx
@@ -24,7 +24,7 @@ export function HousekeepingPageState({
isError
? "border-[var(--admin-error)] bg-[var(--admin-surface)]"
: isPartial
- ? "border-[var(--admin-warning)] bg-[var(--admin-surface)]"
+ ? "border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]"
: "border-[var(--admin-border)] bg-[var(--admin-surface)]"
}`}
>
diff --git a/src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx b/src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx
index 394a4043..4277f531 100644
--- a/src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx
+++ b/src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx
@@ -67,14 +67,73 @@ const domains: readonly HousekeepingNavigationDomain[] = [
},
];
+const taskFiles = [
+ "src/features/housekeeping/foundation/shell/housekeeping-shell.tsx",
+ "src/features/housekeeping/foundation/shell/domain-rail.tsx",
+ "src/features/housekeeping/foundation/shell/context-nav.tsx",
+ "src/features/housekeeping/foundation/shell/command-trigger.tsx",
+ "src/features/housekeeping/foundation/shell/operator-summary.tsx",
+ "src/features/housekeeping/foundation/page/housekeeping-page-shell.tsx",
+ "src/features/housekeeping/foundation/page/housekeeping-page-state.tsx",
+ "src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx",
+ "src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx",
+] as const;
+
+const componentTaskFiles = taskFiles.slice(0, 7);
+
+const structuralBoundaryRules = [
+ { label: "client directive", pattern: new RegExp("use " + "client") },
+ {
+ label: "palette color",
+ pattern:
+ /(?:bg|text|border|from|to)-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d{2,3}/,
+ },
+ { label: "hex color", pattern: new RegExp("#" + "[0-9a-fA-F]{3,8}") },
+ {
+ label: "rgb or hsl color",
+ pattern: new RegExp("(?:r" + "gb|h" + "sl)\\("),
+ },
+];
+
+const componentBoundaryRules = [
+ {
+ label: "React hook",
+ pattern:
+ /\buse(?:State|Effect|Memo|Callback|Ref|Reducer|LayoutEffect|Transition)(?:\s*\(|\s*(?:,|}))/,
+ },
+ { label: "click handler", pattern: /\bonClick\s*=/ },
+ { label: "keyboard handler", pattern: /\bonKey(?:Down|Up|Press)\s*=/ },
+ { label: "event listener", pattern: new RegExp("addEvent" + "Listener") },
+ {
+ label: "database or action import",
+ pattern: new RegExp(
+ "(?:from\\s*|import\\s*\\()\\s*['\"][^'\"]*(?:@/lib/" +
+ "db|drizzle(?:-orm)?|/" +
+ "actions?)[^'\"]*['\"]",
+ ),
+ },
+ { label: "command package", pattern: new RegExp("cmd" + "k", "i") },
+ { label: "localization import", pattern: new RegExp("next" + "-intl") },
+ {
+ label: "search or mutation runtime",
+ pattern: /\b(?:fetch|useQuery|useMutation)\s*\(/,
+ },
+ {
+ label: "persistence storage",
+ pattern:
+ /\b(?:localStorage|sessionStorage|indexedDB)\b|\.(?:setItem|getItem|removeItem)\s*\(/,
+ },
+];
+
function renderShell(
activeDomainId: HousekeepingNavigationDomain["id"] = "operations",
+ availableDomains: readonly HousekeepingNavigationDomain[] = domains,
): string {
return renderToStaticMarkup(
Deck body
@@ -82,10 +141,30 @@ function renderShell(
);
}
+function anchors(html: string): readonly string[] {
+ return html.match(/]*>/g) ?? [];
+}
+
+function anchorForHref(html: string, href: string): string {
+ const anchor = anchors(html).find((candidate) =>
+ candidate.includes(`href="${href}"`),
+ );
+ if (!anchor) throw new Error(`missing anchor for ${href}`);
+ return anchor;
+}
+
+function boundaryViolations(
+ source: string,
+ rules: readonly { label: string; pattern: RegExp }[],
+): readonly string[] {
+ return rules
+ .filter((rule) => rule.pattern.test(source))
+ .map((rule) => rule.label);
+}
+
describe("HousekeepingShell", () => {
it("renders the accessible command deck landmarks and supplied shell content", () => {
const html = renderShell();
-
expect(html).toContain('href="#housekeeping-content"');
expect(html).toContain(">Skip to housekeeping content<");
expect(html).toContain("
");
- expect(html).toMatch(
- /]*href="\/admin-next\/operations")(?=[^>]*aria-current="page")/,
- );
expect(html).toContain('href="/admin-next/operations/queue"');
expect(html).toContain(">Queue<");
expect(html).toContain(">Nora<");
@@ -109,66 +185,88 @@ describe("HousekeepingShell", () => {
);
});
- it("maps every authorized domain icon and only marks the active domain", () => {
+ it("maps all six authorized icons and exactly one active domain", () => {
const html = renderShell("people");
-
- for (const iconId of [
- "inbox",
- "users",
- "file-text",
- "gem",
- "hotel",
- "settings",
+ const allAnchors = anchors(html);
+ const activeAnchors = allAnchors.filter((anchor) =>
+ anchor.includes('aria-current="page"'),
+ );
+ const peopleAnchor = anchorForHref(html, "/admin-next/people");
+ for (const iconClass of [
+ "lucide-inbox",
+ "lucide-users",
+ "lucide-file-text",
+ "lucide-gem",
+ "lucide-hotel",
+ "lucide-settings",
]) {
- expect(html).toContain(`data-domain-icon="${iconId}"`);
+ expect(html).toContain(`class="lucide ${iconClass}"`);
+ }
+ expect(activeAnchors).toHaveLength(1);
+ expect(peopleAnchor).toContain('aria-current="page"');
+ for (const anchor of allAnchors.filter(
+ (anchor) => anchor !== peopleAnchor,
+ )) {
+ expect(anchor).not.toContain('aria-current="page"');
}
- expect(html).toMatch(
- /]*href="\/admin-next\/people")(?=[^>]*aria-current="page")/,
- );
- expect(html).not.toContain(
- 'href="/admin-next/operations" aria-current="page"',
- );
});
- it("renders no contextual items when the active domain is absent", () => {
- const html = renderShell("system");
-
+ it("does not throw or render contextual items when a valid active domain is absent", () => {
+ const domainsWithoutSystem = domains.filter(
+ (domain) => domain.id !== "system",
+ );
+ expect(() => renderShell("system", domainsWithoutSystem)).not.toThrow();
+ const html = renderShell("system", domainsWithoutSystem);
expect(html).not.toContain('href="/admin-next/operations/queue"');
expect(html).not.toContain(">Queue<");
});
it("keeps Task 10 modules within the server-rendered source boundary", () => {
- const taskFiles = [
- "src/features/housekeeping/foundation/shell/housekeeping-shell.tsx",
- "src/features/housekeeping/foundation/shell/domain-rail.tsx",
- "src/features/housekeeping/foundation/shell/context-nav.tsx",
- "src/features/housekeeping/foundation/shell/command-trigger.tsx",
- "src/features/housekeeping/foundation/shell/operator-summary.tsx",
- "src/features/housekeeping/foundation/page/housekeeping-page-shell.tsx",
- "src/features/housekeeping/foundation/page/housekeeping-page-state.tsx",
- "src/features/housekeeping/foundation/shell/housekeeping-shell.test.tsx",
- "src/features/housekeeping/foundation/page/housekeeping-page-state.test.tsx",
- ];
- const prohibited = [
- new RegExp("use " + "client"),
- new RegExp("cmd" + "k", "i"),
- new RegExp("addEvent" + "Listener"),
- new RegExp(
- "from\\s+['\"][^'\"]*(?:@/lib/" +
- "db|drizzle|/" +
- "actions)[^'\"]*['\"]",
- ),
- new RegExp("next" + "-intl"),
- /(?:bg|text|border|from|to)-(?:red|blue|green|yellow|orange|purple|pink|gray|slate|zinc|neutral)-\d{2,3}/,
- new RegExp("#" + "[0-9a-fA-F]{3,8}"),
- new RegExp("(?:r" + "gb|h" + "sl)\\("),
- ];
-
for (const path of taskFiles) {
const source = readFileSync(resolve(process.cwd(), path), "utf8");
- for (const pattern of prohibited) {
- expect(source).not.toMatch(pattern);
- }
+ expect(boundaryViolations(source, structuralBoundaryRules)).toEqual([]);
+ }
+ for (const path of componentTaskFiles) {
+ const source = readFileSync(resolve(process.cwd(), path), "utf8");
+ expect(boundaryViolations(source, componentBoundaryRules)).toEqual([]);
}
});
+
+ it("detects representative prohibited source mutations", () => {
+ const componentMutations = [
+ ["React hook", 'import { useState } from "react";'],
+ ["click handler", "