




















@@ -15,42 +15,34 @@ import {
1515/** All valid tab identifiers derived from TAB_GROUPS */
1616const ALL_TABS: Tab[] = TAB_GROUPS.flatMap((group) => group.tabs) as Tab[];
171718-const nonEmptyTabMetadataCases = [
19-{ name: "iconForTab", resolve: iconForTab },
20-{ name: "titleForTab", resolve: titleForTab },
21-];
22-2318const leadingSlashNormalizerCases = [
2419{ name: "normalizeBasePath", normalize: normalizeBasePath, input: "ui", expected: "/ui" },
2520{ name: "normalizePath", normalize: normalizePath, input: "chat", expected: "/chat" },
2621];
272228-describe("tab metadata string helpers", () => {
29-it.each(nonEmptyTabMetadataCases)(
30-"$name returns a non-empty string for every tab",
31-({ resolve }) => {
32-for (const tab of ALL_TABS) {
33-const value = resolve(tab);
34-expect(typeof value).toBe("string");
35-expect(value.length).toBeGreaterThan(0);
36-}
37-},
38-);
39-});
40-4123describe("iconForTab", () => {
42-it("returns stable icons for known tabs", () => {
43-expect(iconForTab("chat")).toBe("messageSquare");
44-expect(iconForTab("overview")).toBe("barChart");
45-expect(iconForTab("channels")).toBe("link");
46-expect(iconForTab("instances")).toBe("radio");
47-expect(iconForTab("sessions")).toBe("fileText");
48-expect(iconForTab("cron")).toBe("loader");
49-expect(iconForTab("skills")).toBe("zap");
50-expect(iconForTab("nodes")).toBe("monitor");
51-expect(iconForTab("config")).toBe("settings");
52-expect(iconForTab("debug")).toBe("bug");
53-expect(iconForTab("logs")).toBe("scrollText");
24+it("returns stable icons for every tab", () => {
25+expect(Object.fromEntries(ALL_TABS.map((tab) => [tab, iconForTab(tab)]))).toEqual({
26+chat: "messageSquare",
27+overview: "barChart",
28+channels: "link",
29+instances: "radio",
30+sessions: "fileText",
31+usage: "barChart",
32+cron: "loader",
33+agents: "folder",
34+skills: "zap",
35+nodes: "monitor",
36+dreams: "moon",
37+config: "settings",
38+communications: "send",
39+appearance: "spark",
40+automation: "terminal",
41+infrastructure: "globe",
42+aiAgents: "brain",
43+debug: "bug",
44+logs: "scrollText",
45+});
5446});
55475648it("returns a fallback icon for unknown tab", () => {
@@ -61,24 +53,54 @@ describe("iconForTab", () => {
6153});
62546355describe("titleForTab", () => {
64-it("returns expected titles", () => {
65-expect(titleForTab("chat")).toBe("Chat");
66-expect(titleForTab("overview")).toBe("Overview");
67-expect(titleForTab("cron")).toBe("Cron Jobs");
56+it("returns expected titles for every tab", () => {
57+expect(Object.fromEntries(ALL_TABS.map((tab) => [tab, titleForTab(tab)]))).toEqual({
58+chat: "Chat",
59+overview: "Overview",
60+channels: "Channels",
61+instances: "Instances",
62+sessions: "Sessions",
63+usage: "Usage",
64+cron: "Cron Jobs",
65+agents: "Agents",
66+skills: "Skills",
67+nodes: "Nodes",
68+dreams: "Dreaming",
69+config: "Config",
70+communications: "Communications",
71+appearance: "Appearance",
72+automation: "Automation",
73+infrastructure: "Infrastructure",
74+aiAgents: "AI & Agents",
75+debug: "Debug",
76+logs: "Logs",
77+});
6878});
6979});
70807181describe("subtitleForTab", () => {
72-it("returns a string for every tab", () => {
73-for (const tab of ALL_TABS) {
74-const subtitle = subtitleForTab(tab);
75-expect(typeof subtitle).toBe("string");
76-}
77-});
78-79-it("returns descriptive subtitles", () => {
80-expect(subtitleForTab("chat")).toContain("quick interventions");
81-expect(subtitleForTab("config")).toContain("openclaw.json");
82+it("returns expected subtitles for every tab", () => {
83+expect(Object.fromEntries(ALL_TABS.map((tab) => [tab, subtitleForTab(tab)]))).toEqual({
84+chat: "Gateway chat for quick interventions.",
85+overview: "Status, entry points, health.",
86+channels: "Channels and settings.",
87+instances: "Connected clients and nodes.",
88+sessions: "Active sessions and defaults.",
89+usage: "API usage and costs.",
90+cron: "Wakeups and recurring runs.",
91+agents: "Workspaces, tools, identities.",
92+skills: "Skills and API keys.",
93+nodes: "Paired devices and commands.",
94+dreams: "Memory dreaming, consolidation, and reflection.",
95+config: "Edit openclaw.json.",
96+communications: "Channels, messages, and audio settings.",
97+appearance: "Theme, UI, and setup wizard settings.",
98+automation: "Commands, hooks, cron, and plugins.",
99+infrastructure: "Gateway, web, browser, and media settings.",
100+aiAgents: "Agents, models, skills, tools, memory, session.",
101+debug: "Snapshots, events, RPC.",
102+logs: "Live gateway logs.",
103+});
82104});
83105});
84106@@ -185,11 +207,7 @@ describe("inferBasePathFromPathname", () => {
185207186208describe("TAB_GROUPS", () => {
187209it("contains all expected groups", () => {
188-const labels = TAB_GROUPS.map((g) => g.label);
189-expect(labels).toContain("chat");
190-expect(labels).toContain("control");
191-expect(labels).toContain("agent");
192-expect(labels).toContain("settings");
210+expect(TAB_GROUPS.map((g) => g.label)).toEqual(["chat", "control", "agent", "settings"]);
193211});
194212195213it("all tabs are unique", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。