test(ui): pin SectionHeader and ToolsSection behaviour before migration

This commit is contained in:
Yuneng Jiang 2026-08-13 09:37:43 -07:00
parent 9d069f21dc
commit eb23dc2e81
No known key found for this signature in database
2 changed files with 133 additions and 0 deletions

View file

@ -0,0 +1,64 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { SectionHeader } from "./SectionHeader";
describe("SectionHeader", () => {
it("renders the input label with token, cost and turn metrics", () => {
render(<SectionHeader type="input" tokens={1234} cost={0.000123} turnCount={3} onCopy={vi.fn()} />);
expect(screen.getByText("Input")).toBeInTheDocument();
expect(screen.getByText("Tokens: 1,234")).toBeInTheDocument();
expect(screen.getByText("Cost: $0.000123")).toBeInTheDocument();
expect(screen.getByText("Turns: 3")).toBeInTheDocument();
});
it("renders the output label", () => {
render(<SectionHeader type="output" onCopy={vi.fn()} />);
expect(screen.getByText("Output")).toBeInTheDocument();
});
it("omits metrics that were not provided", () => {
render(<SectionHeader type="input" onCopy={vi.fn()} />);
expect(screen.queryByText(/^Tokens:/)).not.toBeInTheDocument();
expect(screen.queryByText(/^Cost:/)).not.toBeInTheDocument();
expect(screen.queryByText(/^Turns:/)).not.toBeInTheDocument();
});
it("omits the turn count when there are no turns", () => {
render(<SectionHeader type="input" turnCount={0} onCopy={vi.fn()} />);
expect(screen.queryByText(/^Turns:/)).not.toBeInTheDocument();
});
it("copies without toggling the section", async () => {
const onCopy = vi.fn();
const onToggleCollapse = vi.fn();
render(<SectionHeader type="input" onCopy={onCopy} onToggleCollapse={onToggleCollapse} />);
await userEvent.click(screen.getByRole("button", { name: /copy/i }));
expect(onCopy).toHaveBeenCalledTimes(1);
expect(onToggleCollapse).not.toHaveBeenCalled();
});
it("toggles the section when the header is clicked", async () => {
const onToggleCollapse = vi.fn();
render(<SectionHeader type="input" onCopy={vi.fn()} onToggleCollapse={onToggleCollapse} />);
await userEvent.click(screen.getByText("Input"));
expect(onToggleCollapse).toHaveBeenCalledTimes(1);
});
it("stays inert when no toggle handler is given", async () => {
const onCopy = vi.fn();
render(<SectionHeader type="input" onCopy={onCopy} />);
await userEvent.click(screen.getByText("Input"));
expect(onCopy).not.toHaveBeenCalled();
});
});

View file

@ -2,10 +2,46 @@
* Core tests for Tools section
*/
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect } from "vitest";
import { parseToolsFromLog } from "./utils";
import { ToolsSection } from "./ToolsSection";
import { LogEntry } from "../columns";
const logWithTools = (toolNames: string[], calledName?: string): LogEntry => ({
request_id: "render-1",
api_key: "key",
team_id: "team",
model: "gpt-4",
model_id: "gpt-4",
call_type: "completion",
spend: 0.01,
total_tokens: 100,
prompt_tokens: 50,
completion_tokens: 50,
startTime: "2024-01-01T00:00:00Z",
endTime: "2024-01-01T00:00:01Z",
cache_hit: "none",
messages: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: "hi" }],
tools: toolNames.map((name) => ({
type: "function",
function: { name, description: `${name} description`, parameters: { type: "object", properties: {} } },
})),
}),
response: JSON.stringify({
choices: [
{
message: calledName
? { tool_calls: [{ id: "call_1", type: "function", function: { name: calledName, arguments: "{}" } }] }
: { content: "done" },
},
],
}),
});
describe("ToolsSection", () => {
it("should parse tools from request and match with response tool calls", () => {
const mockLog: LogEntry = {
@ -115,3 +151,36 @@ describe("ToolsSection", () => {
expect(tools).toHaveLength(0);
});
});
describe("ToolsSection rendering", () => {
it("summarises how many tools were provided and called", () => {
render(<ToolsSection log={logWithTools(["get_weather", "search_web"], "get_weather")} />);
expect(screen.getByText("Tools")).toBeInTheDocument();
expect(screen.getByText("2 provided, 1 called")).toBeInTheDocument();
});
it("previews the first two tool names", () => {
render(<ToolsSection log={logWithTools(["alpha", "beta", "gamma"])} />);
expect(screen.getByText(/alpha, beta/)).toBeInTheDocument();
});
it("renders nothing when the log has no tools", () => {
const { container } = render(<ToolsSection log={logWithTools([])} />);
expect(container).toBeEmptyDOMElement();
});
it("reveals the tool list only after the section is expanded", async () => {
render(<ToolsSection log={logWithTools(["get_weather", "search_web"], "get_weather")} />);
expect(screen.queryByText("called")).not.toBeInTheDocument();
expect(screen.queryByText("not called")).not.toBeInTheDocument();
await userEvent.click(screen.getByText("Tools"));
expect(await screen.findByText("called")).toBeInTheDocument();
expect(screen.getByText("not called")).toBeInTheDocument();
});
});