diff options
Diffstat (limited to 'packages/cli/src/render.test.ts')
| -rw-r--r-- | packages/cli/src/render.test.ts | 105 |
1 files changed, 103 insertions, 2 deletions
diff --git a/packages/cli/src/render.test.ts b/packages/cli/src/render.test.ts index c638584..849d33a 100644 --- a/packages/cli/src/render.test.ts +++ b/packages/cli/src/render.test.ts @@ -1,7 +1,12 @@ -import type { AgentEvent } from "@dispatch/transport-contract"; +import type { AgentEvent, ConversationMeta } from "@dispatch/transport-contract"; import type { StepId } from "@dispatch/wire"; import { describe, expect, it } from "vitest"; -import { renderEvent } from "./render.js"; +import { + extractLastText, + formatConversationList, + formatRelativeTime, + renderEvent, +} from "./render.js"; describe("renderEvent", () => { const opts = { showReasoning: false }; @@ -149,3 +154,99 @@ describe("renderEvent", () => { expect(renderEvent(e, opts)).toBeUndefined(); }); }); + +describe("extractLastText", () => { + it("accumulates text deltas", () => { + const events: AgentEvent[] = [ + { type: "text-delta", conversationId: "c", turnId: "t", delta: "Hello" }, + { type: "text-delta", conversationId: "c", turnId: "t", delta: ", " }, + { type: "text-delta", conversationId: "c", turnId: "t", delta: "world" }, + { type: "done", conversationId: "c", turnId: "t", reason: "completed" }, + ]; + expect(extractLastText(events)).toBe("Hello, world"); + }); + + it("returns empty string when no text-delta events", () => { + const events: AgentEvent[] = [ + { type: "turn-start", conversationId: "c", turnId: "t" }, + { type: "done", conversationId: "c", turnId: "t", reason: "completed" }, + ]; + expect(extractLastText(events)).toBe(""); + }); + + it("ignores non-text-delta events but keeps deltas interleaved among them", () => { + const events: AgentEvent[] = [ + { type: "text-delta", conversationId: "c", turnId: "t", delta: "a" }, + { + type: "tool-call", + conversationId: "c", + turnId: "t", + stepId: "t1#0" as StepId, + toolCallId: "tc1", + toolName: "read_file", + input: {}, + }, + { type: "text-delta", conversationId: "c", turnId: "t", delta: "b" }, + ]; + expect(extractLastText(events)).toBe("ab"); + }); + + it("returns empty string for an empty event list", () => { + expect(extractLastText([])).toBe(""); + }); +}); + +describe("formatRelativeTime", () => { + const now = 1_000_000_000_000; // fixed clock + + it("returns 'just now' for under a minute", () => { + expect(formatRelativeTime(now - 30_000, now)).toBe("just now"); + }); + + it("returns minutes ago", () => { + expect(formatRelativeTime(now - 5 * 60_000, now)).toBe("5m ago"); + }); + + it("returns hours ago", () => { + expect(formatRelativeTime(now - 3 * 3_600_000, now)).toBe("3h ago"); + }); + + it("returns days ago", () => { + expect(formatRelativeTime(now - 2 * 86_400_000, now)).toBe("2d ago"); + }); + + it("returns a date past a week", () => { + // 2001-09-09T01:46:40.000Z + expect(formatRelativeTime(now - 8 * 86_400_000, now)).toBe("2001-09-01"); + }); +}); + +describe("formatConversationList", () => { + const now = 1_000_000_000_000; + + const conv = (id: string, title: string, ageMs: number): ConversationMeta => ({ + id, + title, + createdAt: now - ageMs - 1000, + lastActivityAt: now - ageMs, + }); + + it("returns empty string for an empty list", () => { + expect(formatConversationList([], now)).toBe(""); + }); + + it("formats one row with short id, title, relative time", () => { + const list = [conv("abcdef1234567890", "hello world", 5 * 60_000)]; + expect(formatConversationList(list, now)).toBe("abcdef12 | hello world | 5m ago"); + }); + + it("formats multiple rows one per line", () => { + const list = [ + conv("abcdef1234567890", "first", 5 * 60_000), + conv("0123456789abcdef", "second", 3 * 3_600_000), + ]; + expect(formatConversationList(list, now)).toBe( + "abcdef12 | first | 5m ago\n01234567 | second | 3h ago", + ); + }); +}); |
