import { beforeEach, describe, expect, it } from "vitest"; import { act, fireEvent, render, screen } from "@testing-library/react"; import { App } from "./App"; import { dispatch, resetStore } from "./store"; import type { OriSocket } from "./ws"; import type { ClientMessage } from "./protocol"; function fakeSocket() { const sent: ClientMessage[] = []; const socket: OriSocket = { send: (message) => sent.push(message), close: () => {}, }; return { socket, sent }; } describe("App", () => { beforeEach(() => { resetStore(); }); it("sends a prompt typed in the composer", () => { const { socket, sent } = fakeSocket(); render(); fireEvent.change(screen.getByLabelText("prompt"), { target: { value: "hello agent" }, }); fireEvent.click(screen.getByRole("button", { name: "Send" })); expect(sent).toEqual([{ type: "prompt", text: "hello agent" }]); }); it("shows the streamed conversation from the store", () => { const { socket } = fakeSocket(); render(); act(() => { dispatch({ type: "server", message: { type: "user_message", text: "my question" }, }); dispatch({ type: "server", message: { type: "session_update", update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "my **answer**" }, }, }, }); }); expect(screen.getByText("my question")).toBeDefined(); expect(screen.getByText("answer")).toBeDefined(); // bold inner text }); it("offers Stop instead of Send during a turn, and Stop cancels", () => { const { socket, sent } = fakeSocket(); render(); act(() => { dispatch({ type: "server", message: { type: "turn_started" } }); }); expect(screen.queryByRole("button", { name: "Send" })).toBeNull(); fireEvent.click(screen.getByRole("button", { name: "Stop" })); expect(sent).toEqual([{ type: "cancel" }]); }); it("shows the connection status and session id from hello", () => { const { socket } = fakeSocket(); render(); act(() => { dispatch({ type: "server", message: { type: "hello", sessionId: "sess-42" }, }); }); expect(screen.getByText(/online/)).toBeDefined(); expect(screen.getByText(/sess-42/)).toBeDefined(); }); it("renders tool calls, plan and thoughts from updates", () => { const { socket } = fakeSocket(); render(); act(() => { dispatch({ type: "server", message: { type: "session_update", update: { sessionUpdate: "tool_call", toolCallId: "c1", title: "Running tests", kind: "execute", status: "in_progress", }, }, }); dispatch({ type: "server", message: { type: "session_update", update: { sessionUpdate: "plan", entries: [ { content: "write the fix", priority: "high", status: "in_progress", }, ], }, }, }); dispatch({ type: "server", message: { type: "session_update", update: { sessionUpdate: "agent_thought_chunk", content: { type: "text", text: "let me think" }, }, }, }); }); expect(screen.getByText("Running tests")).toBeDefined(); expect(screen.getByText(/write the fix/)).toBeDefined(); expect(screen.getByText("let me think")).toBeDefined(); }); it("answers a permission request through the socket", () => { const { socket, sent } = fakeSocket(); render(); act(() => { dispatch({ type: "server", message: { type: "permission_request", requestId: "perm-9", request: { toolCall: { title: "Write main.go" }, options: [ { optionId: "allow-once", name: "Allow once", kind: "allow_once", }, { optionId: "reject-once", name: "Reject", kind: "reject_once" }, ], }, }, }); }); expect(screen.getByText(/Write main\.go/)).toBeDefined(); fireEvent.click(screen.getByRole("button", { name: "Allow once" })); expect(sent).toEqual([ { type: "permission_response", requestId: "perm-9", optionId: "allow-once", }, ]); }); it("does not send empty prompts", () => { const { socket, sent } = fakeSocket(); render(); fireEvent.change(screen.getByLabelText("prompt"), { target: { value: " " }, }); fireEvent.keyDown(screen.getByLabelText("prompt"), { key: "Enter" }); expect(sent).toEqual([]); }); });