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([]);
});
});