@tangle-network/ui 11.51.1 → 11.51.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,189 +0,0 @@
1
- import { render, screen } from "@testing-library/react"
2
- import { beforeAll, describe, expect, it, vi } from "vitest"
3
- import type { SessionMessage } from "../types/message"
4
- import type { SessionPart, ToolPart } from "../types/parts"
5
- import { ChatContainer } from "./chat-container"
6
-
7
- // jsdom has no layout engine; the auto-scroll hook calls scrollIntoView.
8
- beforeAll(() => {
9
- Element.prototype.scrollIntoView = vi.fn()
10
- })
11
-
12
- const messages: SessionMessage[] = [
13
- { id: "m1", role: "assistant" },
14
- ]
15
-
16
- const editPart: ToolPart = {
17
- type: "tool",
18
- id: "tool-1",
19
- tool: "edit",
20
- state: {
21
- status: "completed",
22
- input: { path: "src/batch-writer.ts" },
23
- output: "Applied 1 edit to src/batch-writer.ts",
24
- },
25
- }
26
-
27
- const partMap: Record<string, SessionPart[]> = { m1: [editPart] }
28
-
29
- describe("ChatContainer timeline tool rendering", () => {
30
- it("shows thinking as soon as a submitted user message is streaming", () => {
31
- render(
32
- <ChatContainer
33
- messages={[{ id: "user-1", role: "user" }]}
34
- partMap={{
35
- "user-1": [{ type: "text", text: "hello" }],
36
- }}
37
- isStreaming
38
- presentation="timeline"
39
- />,
40
- )
41
-
42
- expect(
43
- screen.getByRole("status", { name: "Agent is thinking" }),
44
- ).toBeInTheDocument()
45
- })
46
-
47
- it("does not let an earlier assistant reply hide the next turn's thinking state", () => {
48
- render(
49
- <ChatContainer
50
- messages={[
51
- { id: "user-1", role: "user" },
52
- { id: "assistant-1", role: "assistant" },
53
- { id: "user-2", role: "user" },
54
- ]}
55
- partMap={{
56
- "user-1": [{ type: "text", text: "first" }],
57
- "assistant-1": [{ type: "text", text: "done" }],
58
- "user-2": [{ type: "text", text: "second" }],
59
- }}
60
- isStreaming
61
- presentation="timeline"
62
- />,
63
- )
64
-
65
- expect(
66
- screen.getByRole("status", { name: "Agent is thinking" }),
67
- ).toBeInTheDocument()
68
- })
69
-
70
- it("replaces the generic thinking row when the current reply has text", () => {
71
- render(
72
- <ChatContainer
73
- messages={[
74
- { id: "user-1", role: "user" },
75
- { id: "assistant-1", role: "assistant" },
76
- ]}
77
- partMap={{
78
- "user-1": [{ type: "text", text: "hello" }],
79
- "assistant-1": [{ type: "text", text: "working on it" }],
80
- }}
81
- isStreaming
82
- presentation="timeline"
83
- />,
84
- )
85
-
86
- expect(
87
- screen.queryByRole("status", { name: "Agent is thinking" }),
88
- ).not.toBeInTheDocument()
89
- expect(screen.getByText("working on it")).toBeInTheDocument()
90
- })
91
-
92
- it("shows a clean tool detail (the file path), not the raw input JSON", () => {
93
- render(
94
- <ChatContainer
95
- messages={messages}
96
- partMap={partMap}
97
- isStreaming={false}
98
- presentation="timeline"
99
- />,
100
- )
101
-
102
- expect(screen.getByText("src/batch-writer.ts")).toBeInTheDocument()
103
- // The raw `{ "path": ... }` input JSON must not leak into the summary row.
104
- expect(screen.queryByText(/"path"/)).not.toBeInTheDocument()
105
- })
106
-
107
- it("titles the tool row with the bare verb and prints the path once", () => {
108
- const { container } = render(
109
- <ChatContainer
110
- messages={messages}
111
- partMap={partMap}
112
- isStreaming={false}
113
- presentation="timeline"
114
- />,
115
- )
116
-
117
- expect(screen.getByText("Edit")).toBeInTheDocument()
118
- expect(screen.getAllByText("src/batch-writer.ts")).toHaveLength(1)
119
- expect(container.textContent).not.toContain("Edit src/batch-writer.ts")
120
- })
121
-
122
- it("stacks consecutive tool calls with no visible group header", () => {
123
- const readPart: ToolPart = {
124
- type: "tool",
125
- id: "tool-2",
126
- tool: "read",
127
- state: { status: "completed", input: { path: "src/lib/jitter.ts" }, output: "ok" },
128
- }
129
- render(
130
- <ChatContainer
131
- messages={messages}
132
- partMap={{ m1: [editPart, readPart] }}
133
- isStreaming={false}
134
- presentation="timeline"
135
- />,
136
- )
137
-
138
- expect(screen.queryByText(/tool activity/i)).not.toBeInTheDocument()
139
- expect(screen.getByRole("group", { name: "Tool activity" })).toBeInTheDocument()
140
- expect(screen.getByText("Edit")).toBeInTheDocument()
141
- expect(screen.getByText("Read")).toBeInTheDocument()
142
- })
143
-
144
- it("shimmers a running tool's verb and shows no spinner", () => {
145
- const running: ToolPart = {
146
- type: "tool",
147
- id: "tool-3",
148
- tool: "bash",
149
- state: { status: "running", input: { command: "pnpm build" }, time: { start: 1 } },
150
- }
151
- const { container } = render(
152
- <ChatContainer
153
- messages={[{ id: "user-1", role: "user" }, { id: "m1", role: "assistant" }]}
154
- partMap={{ "user-1": [{ type: "text", text: "build" }], m1: [running] }}
155
- isStreaming
156
- presentation="timeline"
157
- />,
158
- )
159
-
160
- const shimmer = container.querySelector(".tangle-text-shimmer")
161
- expect(shimmer).not.toBeNull()
162
- expect(shimmer!.textContent).toBe("Shell")
163
- expect(container.querySelector(".animate-spin")).toBeNull()
164
- })
165
-
166
- it("threads renderTimelineToolActions to the timeline tool item with its source part", () => {
167
- const renderTimelineToolActions = vi.fn((part: ToolPart) => (
168
- <button type="button">Open {part.id}</button>
169
- ))
170
-
171
- render(
172
- <ChatContainer
173
- messages={messages}
174
- partMap={partMap}
175
- isStreaming={false}
176
- presentation="timeline"
177
- renderTimelineToolActions={renderTimelineToolActions}
178
- />,
179
- )
180
-
181
- expect(renderTimelineToolActions).toHaveBeenCalled()
182
- expect(renderTimelineToolActions.mock.calls[0][0]).toMatchObject({
183
- id: "tool-1",
184
- })
185
- expect(
186
- screen.getByRole("button", { name: /open tool-1/i }),
187
- ).toBeInTheDocument()
188
- })
189
- })