diff --git a/ui/src/ui/app-render.helpers.node.test.ts b/ui/src/ui/app-render.helpers.node.test.ts index 7bea77067ed..d3b1acf4496 100644 --- a/ui/src/ui/app-render.helpers.node.test.ts +++ b/ui/src/ui/app-render.helpers.node.test.ts @@ -13,82 +13,72 @@ function row(overrides: Partial & { key: string }): SessionRow { * ================================================================ */ describe("parseSessionKey", () => { - it("identifies main session (bare 'main')", () => { - expect(parseSessionKey("main")).toEqual({ prefix: "", fallbackName: "Main Session" }); - }); - - it("identifies main session (agent:main:main)", () => { - expect(parseSessionKey("agent:main:main")).toEqual({ - prefix: "", - fallbackName: "Main Session", - }); - }); - - it("identifies subagent sessions", () => { - expect(parseSessionKey("agent:main:subagent:18abfefe-1fa6-43cb-8ba8-ebdc9b43e253")).toEqual({ - prefix: "Subagent:", - fallbackName: "Subagent:", - }); - }); - - it("identifies cron sessions", () => { - expect(parseSessionKey("agent:main:cron:daily-briefing-uuid")).toEqual({ - prefix: "Cron:", - fallbackName: "Cron Job:", - }); - }); - - it("identifies direct chat with known channel", () => { - expect(parseSessionKey("agent:main:bluebubbles:direct:+19257864429")).toEqual({ - prefix: "", - fallbackName: "iMessage · +19257864429", - }); - }); - - it("identifies direct chat with telegram", () => { - expect(parseSessionKey("agent:main:telegram:direct:user123")).toEqual({ - prefix: "", - fallbackName: "Telegram · user123", - }); - }); - - it("identifies group chat with known channel", () => { - expect(parseSessionKey("agent:main:discord:group:guild-chan")).toEqual({ - prefix: "", - fallbackName: "Discord Group", - }); - }); - - it("capitalises unknown channels in direct/group patterns", () => { - expect(parseSessionKey("agent:main:mychannel:direct:user1")).toEqual({ - prefix: "", - fallbackName: "Mychannel · user1", - }); - }); - - it("identifies channel-prefixed legacy keys", () => { - expect(parseSessionKey("bluebubbles:g-agent-main-bluebubbles-direct-+19257864429")).toEqual({ - prefix: "", - fallbackName: "iMessage Session", - }); - expect(parseSessionKey("discord:123:456")).toEqual({ - prefix: "", - fallbackName: "Discord Session", - }); - }); - - it("handles bare channel name as key", () => { - expect(parseSessionKey("telegram")).toEqual({ - prefix: "", - fallbackName: "Telegram Session", - }); - }); - - it("returns raw key for unknown patterns", () => { - expect(parseSessionKey("something-unknown")).toEqual({ - prefix: "", - fallbackName: "something-unknown", - }); + it("maps session keys to expected prefixes and fallback names", () => { + const cases = [ + { + name: "bare main", + key: "main", + expected: { prefix: "", fallbackName: "Main Session" }, + }, + { + name: "agent main key", + key: "agent:main:main", + expected: { prefix: "", fallbackName: "Main Session" }, + }, + { + name: "subagent key", + key: "agent:main:subagent:18abfefe-1fa6-43cb-8ba8-ebdc9b43e253", + expected: { prefix: "Subagent:", fallbackName: "Subagent:" }, + }, + { + name: "cron key", + key: "agent:main:cron:daily-briefing-uuid", + expected: { prefix: "Cron:", fallbackName: "Cron Job:" }, + }, + { + name: "direct known channel", + key: "agent:main:bluebubbles:direct:+19257864429", + expected: { prefix: "", fallbackName: "iMessage · +19257864429" }, + }, + { + name: "direct telegram", + key: "agent:main:telegram:direct:user123", + expected: { prefix: "", fallbackName: "Telegram · user123" }, + }, + { + name: "group known channel", + key: "agent:main:discord:group:guild-chan", + expected: { prefix: "", fallbackName: "Discord Group" }, + }, + { + name: "unknown channel direct", + key: "agent:main:mychannel:direct:user1", + expected: { prefix: "", fallbackName: "Mychannel · user1" }, + }, + { + name: "legacy channel-prefixed key", + key: "bluebubbles:g-agent-main-bluebubbles-direct-+19257864429", + expected: { prefix: "", fallbackName: "iMessage Session" }, + }, + { + name: "legacy discord key", + key: "discord:123:456", + expected: { prefix: "", fallbackName: "Discord Session" }, + }, + { + name: "bare channel key", + key: "telegram", + expected: { prefix: "", fallbackName: "Telegram Session" }, + }, + { + name: "unknown pattern", + key: "something-unknown", + expected: { prefix: "", fallbackName: "something-unknown" }, + }, + ] as const; + for (const testCase of cases) { + expect(parseSessionKey(testCase.key), testCase.name).toEqual(testCase.expected); + } }); }); @@ -97,167 +87,130 @@ describe("parseSessionKey", () => { * ================================================================ */ describe("resolveSessionDisplayName", () => { - // ── Key-only fallbacks (no row) ────────────────── - - it("returns 'Main Session' for agent:main:main key", () => { - expect(resolveSessionDisplayName("agent:main:main")).toBe("Main Session"); + it("resolves key-only fallbacks", () => { + const cases = [ + { key: "agent:main:main", expected: "Main Session" }, + { key: "main", expected: "Main Session" }, + { key: "agent:main:subagent:abc-123", expected: "Subagent:" }, + { key: "agent:main:cron:abc-123", expected: "Cron Job:" }, + { key: "agent:main:bluebubbles:direct:+19257864429", expected: "iMessage · +19257864429" }, + { key: "discord:123:456", expected: "Discord Session" }, + { key: "something-custom", expected: "something-custom" }, + ] as const; + for (const testCase of cases) { + expect(resolveSessionDisplayName(testCase.key), testCase.key).toBe(testCase.expected); + } }); - it("returns 'Main Session' for bare 'main' key", () => { - expect(resolveSessionDisplayName("main")).toBe("Main Session"); - }); - - it("returns 'Subagent:' for subagent key without row", () => { - expect(resolveSessionDisplayName("agent:main:subagent:abc-123")).toBe("Subagent:"); - }); - - it("returns 'Cron Job:' for cron key without row", () => { - expect(resolveSessionDisplayName("agent:main:cron:abc-123")).toBe("Cron Job:"); - }); - - it("parses direct chat key with channel", () => { - expect(resolveSessionDisplayName("agent:main:bluebubbles:direct:+19257864429")).toBe( - "iMessage · +19257864429", - ); - }); - - it("parses channel-prefixed legacy key", () => { - expect(resolveSessionDisplayName("discord:123:456")).toBe("Discord Session"); - }); - - it("returns raw key for unknown patterns", () => { - expect(resolveSessionDisplayName("something-custom")).toBe("something-custom"); - }); - - // ── With row data (label / displayName) ────────── - - it("returns parsed fallback when row has no label or displayName", () => { - expect(resolveSessionDisplayName("agent:main:main", row({ key: "agent:main:main" }))).toBe( - "Main Session", - ); - }); - - it("returns parsed fallback when displayName matches key", () => { - expect(resolveSessionDisplayName("mykey", row({ key: "mykey", displayName: "mykey" }))).toBe( - "mykey", - ); - }); - - it("returns parsed fallback when label matches key", () => { - expect(resolveSessionDisplayName("mykey", row({ key: "mykey", label: "mykey" }))).toBe("mykey"); - }); - - it("uses label alone when available", () => { - expect( - resolveSessionDisplayName( - "discord:123:456", - row({ key: "discord:123:456", label: "General" }), - ), - ).toBe("General"); - }); - - it("falls back to displayName when label is absent", () => { - expect( - resolveSessionDisplayName( - "discord:123:456", - row({ key: "discord:123:456", displayName: "My Chat" }), - ), - ).toBe("My Chat"); - }); - - it("prefers label over displayName when both are present", () => { - expect( - resolveSessionDisplayName( - "discord:123:456", - row({ key: "discord:123:456", displayName: "My Chat", label: "General" }), - ), - ).toBe("General"); - }); - - it("ignores whitespace-only label and falls back to displayName", () => { - expect( - resolveSessionDisplayName( - "discord:123:456", - row({ key: "discord:123:456", displayName: "My Chat", label: " " }), - ), - ).toBe("My Chat"); - }); - - it("uses parsed fallback when whitespace-only label and no displayName", () => { - expect( - resolveSessionDisplayName("discord:123:456", row({ key: "discord:123:456", label: " " })), - ).toBe("Discord Session"); - }); - - it("trims label and displayName", () => { - expect(resolveSessionDisplayName("k", row({ key: "k", label: " General " }))).toBe("General"); - expect(resolveSessionDisplayName("k", row({ key: "k", displayName: " My Chat " }))).toBe( - "My Chat", - ); - }); - - // ── Type prefixes applied to labels / displayNames ── - - it("prefixes subagent label with Subagent:", () => { - expect( - resolveSessionDisplayName( - "agent:main:subagent:abc-123", - row({ key: "agent:main:subagent:abc-123", label: "maintainer-v2" }), - ), - ).toBe("Subagent: maintainer-v2"); - }); - - it("prefixes subagent displayName with Subagent:", () => { - expect( - resolveSessionDisplayName( - "agent:main:subagent:abc-123", - row({ key: "agent:main:subagent:abc-123", displayName: "Task Runner" }), - ), - ).toBe("Subagent: Task Runner"); - }); - - it("prefixes cron label with Cron:", () => { - expect( - resolveSessionDisplayName( - "agent:main:cron:abc-123", - row({ key: "agent:main:cron:abc-123", label: "daily-briefing" }), - ), - ).toBe("Cron: daily-briefing"); - }); - - it("prefixes cron displayName with Cron:", () => { - expect( - resolveSessionDisplayName( - "agent:main:cron:abc-123", - row({ key: "agent:main:cron:abc-123", displayName: "Nightly Sync" }), - ), - ).toBe("Cron: Nightly Sync"); - }); - - it("does not double-prefix cron labels that already include Cron:", () => { - expect( - resolveSessionDisplayName( - "agent:main:cron:abc-123", - row({ key: "agent:main:cron:abc-123", label: "Cron: Nightly Sync" }), - ), - ).toBe("Cron: Nightly Sync"); - }); - - it("does not double-prefix subagent display names that already include Subagent:", () => { - expect( - resolveSessionDisplayName( - "agent:main:subagent:abc-123", - row({ key: "agent:main:subagent:abc-123", displayName: "Subagent: Runner" }), - ), - ).toBe("Subagent: Runner"); - }); - - it("does not prefix non-typed sessions with labels", () => { - expect( - resolveSessionDisplayName( - "agent:main:bluebubbles:direct:+19257864429", - row({ key: "agent:main:bluebubbles:direct:+19257864429", label: "Tyler" }), - ), - ).toBe("Tyler"); + it("resolves row labels/display names and typed prefixes", () => { + const cases = [ + { + name: "row with no label/display", + key: "agent:main:main", + rowData: row({ key: "agent:main:main" }), + expected: "Main Session", + }, + { + name: "displayName equals key", + key: "mykey", + rowData: row({ key: "mykey", displayName: "mykey" }), + expected: "mykey", + }, + { + name: "label equals key", + key: "mykey", + rowData: row({ key: "mykey", label: "mykey" }), + expected: "mykey", + }, + { + name: "label used", + key: "discord:123:456", + rowData: row({ key: "discord:123:456", label: "General" }), + expected: "General", + }, + { + name: "displayName fallback", + key: "discord:123:456", + rowData: row({ key: "discord:123:456", displayName: "My Chat" }), + expected: "My Chat", + }, + { + name: "label preferred over displayName", + key: "discord:123:456", + rowData: row({ key: "discord:123:456", displayName: "My Chat", label: "General" }), + expected: "General", + }, + { + name: "ignore whitespace label", + key: "discord:123:456", + rowData: row({ key: "discord:123:456", displayName: "My Chat", label: " " }), + expected: "My Chat", + }, + { + name: "fallback when whitespace label and no displayName", + key: "discord:123:456", + rowData: row({ key: "discord:123:456", label: " " }), + expected: "Discord Session", + }, + { + name: "trim label", + key: "k", + rowData: row({ key: "k", label: " General " }), + expected: "General", + }, + { + name: "trim displayName", + key: "k", + rowData: row({ key: "k", displayName: " My Chat " }), + expected: "My Chat", + }, + { + name: "prefix subagent label", + key: "agent:main:subagent:abc-123", + rowData: row({ key: "agent:main:subagent:abc-123", label: "maintainer-v2" }), + expected: "Subagent: maintainer-v2", + }, + { + name: "prefix subagent displayName", + key: "agent:main:subagent:abc-123", + rowData: row({ key: "agent:main:subagent:abc-123", displayName: "Task Runner" }), + expected: "Subagent: Task Runner", + }, + { + name: "prefix cron label", + key: "agent:main:cron:abc-123", + rowData: row({ key: "agent:main:cron:abc-123", label: "daily-briefing" }), + expected: "Cron: daily-briefing", + }, + { + name: "prefix cron displayName", + key: "agent:main:cron:abc-123", + rowData: row({ key: "agent:main:cron:abc-123", displayName: "Nightly Sync" }), + expected: "Cron: Nightly Sync", + }, + { + name: "avoid double cron prefix", + key: "agent:main:cron:abc-123", + rowData: row({ key: "agent:main:cron:abc-123", label: "Cron: Nightly Sync" }), + expected: "Cron: Nightly Sync", + }, + { + name: "avoid double subagent prefix", + key: "agent:main:subagent:abc-123", + rowData: row({ key: "agent:main:subagent:abc-123", displayName: "Subagent: Runner" }), + expected: "Subagent: Runner", + }, + { + name: "non-typed label without prefix", + key: "agent:main:bluebubbles:direct:+19257864429", + rowData: row({ key: "agent:main:bluebubbles:direct:+19257864429", label: "Tyler" }), + expected: "Tyler", + }, + ] as const; + for (const testCase of cases) { + expect(resolveSessionDisplayName(testCase.key, testCase.rowData), testCase.name).toBe( + testCase.expected, + ); + } }); });