Download packages/app/src/pages/session/timeline/rows-current.test.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 7.16 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/pages/session/timeline/rows-current.test.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/app/src/pages/session/timeline/rows-current.test.ts
-
curl -L -o rows-current.test.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/pages/session/timeline/rows-current.test.ts
7.16 kB
| import { describe, expect, mock, test } from "bun:test" | |
| import type { SessionMessageInfo } from "@opencode-ai/client/promise" | |
| import { normalizeSessionMessages } from "@/utils/session-message" | |
| mock.module("@opencode-ai/session-ui/message-part", () => ({ | |
| renderable: () => true, | |
| groupParts: (refs: Array<{ messageID: string; part: { id: string } }>) => | |
| refs.map((ref) => ({ | |
| type: "part" as const, | |
| key: ref.part.id, | |
| ref: { messageID: ref.messageID, partID: ref.part.id }, | |
| })), | |
| })) | |
| const { Timeline, TimelineRow } = await import("./rows") | |
| describe("current session timeline rows", () => { | |
| test("derives turns and tagged rows from chronological current messages", () => { | |
| const source = [ | |
| { id: "msg_1", type: "user", text: "first", time: { created: 1 } }, | |
| { | |
| id: "msg_2", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [{ type: "text", text: "answer" }], | |
| time: { created: 2, completed: 3 }, | |
| }, | |
| { id: "msg_3", type: "user", text: "second", time: { created: 4 } }, | |
| { | |
| id: "msg_4", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [{ type: "reasoning", text: "working" }], | |
| time: { created: 5 }, | |
| }, | |
| ] satisfies SessionMessageInfo[] | |
| const normalized = normalizeSessionMessages("ses_1", source) | |
| const messages = new Map(normalized.messages.map((message) => [message.id, message])) | |
| const result = Timeline.constructSessionMessageRows( | |
| source, | |
| (messageID) => messages.get(messageID), | |
| (messageID) => normalized.parts.get(messageID) ?? [], | |
| true, | |
| "busy", | |
| true, | |
| normalized.messages.filter((message) => message.role === "user"), | |
| ) | |
| expect(result.activeMessageID).toBe("msg_3") | |
| expect(result.rows.map(TimelineRow.key)).toEqual([ | |
| "user-message:msg_1", | |
| "assistant-part:msg_1:msg_2:text:0", | |
| "turn-gap:msg_3", | |
| "user-message:msg_3", | |
| "assistant-part:msg_3:msg_4:reasoning:0", | |
| ]) | |
| }) | |
| test("renders a current shell message as a standalone turn", () => { | |
| const source = [ | |
| { | |
| id: "msg_shell", | |
| type: "shell", | |
| shellID: "shell_1", | |
| command: "pwd", | |
| status: "exited", | |
| exit: 0, | |
| output: { output: "/repo", cursor: 5, size: 5, truncated: false }, | |
| time: { created: 1, completed: 2 }, | |
| }, | |
| ] satisfies SessionMessageInfo[] | |
| const normalized = normalizeSessionMessages("ses_1", source) | |
| const messages = new Map(normalized.messages.map((message) => [message.id, message])) | |
| const result = Timeline.constructSessionMessageRows( | |
| source, | |
| (messageID) => messages.get(messageID), | |
| (messageID) => normalized.parts.get(messageID) ?? [], | |
| true, | |
| "idle", | |
| true, | |
| normalized.messages.filter((message) => message.role === "user"), | |
| ) | |
| expect(result.activeMessageID).toBe("msg_shell") | |
| expect(result.rows.map(TimelineRow.key)).toEqual([ | |
| "user-message:msg_shell", | |
| "assistant-part:msg_shell:msg_shell:tool", | |
| ]) | |
| }) | |
| test("keeps a projected parent missing from the source page before newer turns", () => { | |
| const source = [ | |
| { id: "msg_user_1", type: "user", text: "first question", time: { created: 1 } }, | |
| { | |
| id: "msg_assistant_1", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [{ type: "text", text: "first answer" }], | |
| time: { created: 2, completed: 3 }, | |
| }, | |
| { id: "msg_user_2", type: "user", text: "second question", time: { created: 4 } }, | |
| { | |
| id: "msg_assistant_2", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [{ type: "text", text: "second answer" }], | |
| time: { created: 5, completed: 6 }, | |
| }, | |
| ] satisfies SessionMessageInfo[] | |
| const normalized = normalizeSessionMessages("ses_1", source) | |
| const messages = new Map(normalized.messages.map((message) => [message.id, message])) | |
| const result = Timeline.constructSessionMessageRows( | |
| source.slice(1), | |
| (messageID) => messages.get(messageID), | |
| (messageID) => normalized.parts.get(messageID) ?? [], | |
| true, | |
| "idle", | |
| true, | |
| normalized.messages.filter((message) => message.role === "user"), | |
| ) | |
| expect(result.rows.map(TimelineRow.key)).toEqual([ | |
| "user-message:msg_user_1", | |
| "assistant-part:msg_user_1:msg_assistant_1:text:0", | |
| "turn-gap:msg_user_2", | |
| "user-message:msg_user_2", | |
| "assistant-part:msg_user_2:msg_assistant_2:text:0", | |
| ]) | |
| }) | |
| test("renders an optimistic user turn and thinking before the protocol message arrives", () => { | |
| const source = [ | |
| { id: "msg_z", type: "user", text: "existing", time: { created: 1 } }, | |
| ] satisfies SessionMessageInfo[] | |
| const normalized = normalizeSessionMessages("ses_1", source) | |
| const optimistic = { | |
| id: "msg_a", | |
| sessionID: "ses_1", | |
| role: "user" as const, | |
| time: { created: 2 }, | |
| agent: "build", | |
| model: { modelID: "model", providerID: "provider" }, | |
| } | |
| const result = Timeline.constructSessionMessageRows( | |
| source, | |
| (messageID) => | |
| messageID === optimistic.id ? optimistic : normalized.messages.find((message) => message.id === messageID), | |
| () => [], | |
| true, | |
| "busy", | |
| true, | |
| [...normalized.messages.filter((message) => message.role === "user"), optimistic], | |
| ) | |
| expect(result.activeMessageID).toBe(optimistic.id) | |
| expect(result.rows.map(TimelineRow.key)).toEqual([ | |
| "user-message:msg_z", | |
| "turn-gap:msg_a", | |
| "user-message:msg_a", | |
| "thinking:msg_a", | |
| ]) | |
| }) | |
| test("removes a failed assistant error when the turn continues streaming", () => { | |
| const source = [ | |
| { id: "msg_user", type: "user", text: "recover", time: { created: 1 } }, | |
| { | |
| id: "msg_failed", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [], | |
| error: { type: "ProviderError", message: "temporary failure" }, | |
| time: { created: 2, completed: 3 }, | |
| }, | |
| { | |
| id: "msg_recovery", | |
| type: "assistant", | |
| agent: "build", | |
| model: { id: "model", providerID: "provider" }, | |
| content: [{ type: "text", text: "streaming again" }], | |
| time: { created: 4 }, | |
| }, | |
| ] satisfies SessionMessageInfo[] | |
| const normalized = normalizeSessionMessages("ses_1", source) | |
| const messages = new Map(normalized.messages.map((message) => [message.id, message])) | |
| const result = Timeline.constructSessionMessageRows( | |
| source, | |
| (messageID) => messages.get(messageID), | |
| (messageID) => normalized.parts.get(messageID) ?? [], | |
| true, | |
| "busy", | |
| true, | |
| normalized.messages.filter((message) => message.role === "user"), | |
| ) | |
| expect(result.rows.map((row) => row._tag)).toEqual(["UserMessage", "AssistantPart"]) | |
| }) | |
| }) | |