nandi/oripublic Fork 0
2434cc7fb724f02b34c0189dfd5fb30c1a8a68e3
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

App.test.tsx · 186 lines · 4.4 KBTypeScript Blame HistoryRaw
✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS 2434cc7 k33g 2d ago1import { beforeEach, describe, expect, it } from "vitest";
2import { act, fireEvent, render, screen } from "@testing-library/react";
3import { App } from "./App";
4import { dispatch, resetStore } from "./store";
5import type { OriSocket } from "./ws";
6import type { ClientMessage } from "./protocol";
7
8function fakeSocket() {
9 const sent: ClientMessage[] = [];
10 const socket: OriSocket = {
11 send: (message) => sent.push(message),
12 close: () => {},
13 };
14 return { socket, sent };
15}
16
17describe("App", () => {
18 beforeEach(() => {
19 resetStore();
20 });
21
22 it("sends a prompt typed in the composer", () => {
23 const { socket, sent } = fakeSocket();
24 render(<App socket={socket} />);
25
26 fireEvent.change(screen.getByLabelText("prompt"), {
27 target: { value: "hello agent" },
28 });
29 fireEvent.click(screen.getByRole("button", { name: "Send" }));
30
31 expect(sent).toEqual([{ type: "prompt", text: "hello agent" }]);
32 });
33
34 it("shows the streamed conversation from the store", () => {
35 const { socket } = fakeSocket();
36 render(<App socket={socket} />);
37
38 act(() => {
39 dispatch({
40 type: "server",
41 message: { type: "user_message", text: "my question" },
42 });
43 dispatch({
44 type: "server",
45 message: {
46 type: "session_update",
47 update: {
48 sessionUpdate: "agent_message_chunk",
49 content: { type: "text", text: "my **answer**" },
50 },
51 },
52 });
53 });
54
55 expect(screen.getByText("my question")).toBeDefined();
56 expect(screen.getByText("answer")).toBeDefined(); // bold inner text
57 });
58
59 it("offers Stop instead of Send during a turn, and Stop cancels", () => {
60 const { socket, sent } = fakeSocket();
61 render(<App socket={socket} />);
62
63 act(() => {
64 dispatch({ type: "server", message: { type: "turn_started" } });
65 });
66
67 expect(screen.queryByRole("button", { name: "Send" })).toBeNull();
68 fireEvent.click(screen.getByRole("button", { name: "Stop" }));
69 expect(sent).toEqual([{ type: "cancel" }]);
70 });
71
72 it("shows the connection status and session id from hello", () => {
73 const { socket } = fakeSocket();
74 render(<App socket={socket} />);
75
76 act(() => {
77 dispatch({
78 type: "server",
79 message: { type: "hello", sessionId: "sess-42" },
80 });
81 });
82
83 expect(screen.getByText(/online/)).toBeDefined();
84 expect(screen.getByText(/sess-42/)).toBeDefined();
85 });
86
87 it("renders tool calls, plan and thoughts from updates", () => {
88 const { socket } = fakeSocket();
89 render(<App socket={socket} />);
90
91 act(() => {
92 dispatch({
93 type: "server",
94 message: {
95 type: "session_update",
96 update: {
97 sessionUpdate: "tool_call",
98 toolCallId: "c1",
99 title: "Running tests",
100 kind: "execute",
101 status: "in_progress",
102 },
103 },
104 });
105 dispatch({
106 type: "server",
107 message: {
108 type: "session_update",
109 update: {
110 sessionUpdate: "plan",
111 entries: [
112 {
113 content: "write the fix",
114 priority: "high",
115 status: "in_progress",
116 },
117 ],
118 },
119 },
120 });
121 dispatch({
122 type: "server",
123 message: {
124 type: "session_update",
125 update: {
126 sessionUpdate: "agent_thought_chunk",
127 content: { type: "text", text: "let me think" },
128 },
129 },
130 });
131 });
132
133 expect(screen.getByText("Running tests")).toBeDefined();
134 expect(screen.getByText(/write the fix/)).toBeDefined();
135 expect(screen.getByText("let me think")).toBeDefined();
136 });
137
138 it("answers a permission request through the socket", () => {
139 const { socket, sent } = fakeSocket();
140 render(<App socket={socket} />);
141
142 act(() => {
143 dispatch({
144 type: "server",
145 message: {
146 type: "permission_request",
147 requestId: "perm-9",
148 request: {
149 toolCall: { title: "Write main.go" },
150 options: [
151 {
152 optionId: "allow-once",
153 name: "Allow once",
154 kind: "allow_once",
155 },
156 { optionId: "reject-once", name: "Reject", kind: "reject_once" },
157 ],
158 },
159 },
160 });
161 });
162
163 expect(screen.getByText(/Write main\.go/)).toBeDefined();
164 fireEvent.click(screen.getByRole("button", { name: "Allow once" }));
165
166 expect(sent).toEqual([
167 {
168 type: "permission_response",
169 requestId: "perm-9",
170 optionId: "allow-once",
171 },
172 ]);
173 });
174
175 it("does not send empty prompts", () => {
176 const { socket, sent } = fakeSocket();
177 render(<App socket={socket} />);
178
179 fireEvent.change(screen.getByLabelText("prompt"), {
180 target: { value: " " },
181 });
182 fireEvent.keyDown(screen.getByLabelText("prompt"), { key: "Enter" });
183
184 expect(sent).toEqual([]);
185 });
186});