Skip to content

Commit 4ceaf2b

Browse files
committed
test: add tests for useUIActions
1 parent 1ded598 commit 4ceaf2b

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

tests/unit/ui/useUIActions.test.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { describe, it, expect, beforeEach, afterEach, vi, type Mock } from "vitest";
2+
import { createElement, type FunctionComponent } from "react";
3+
import { renderToString } from "react-dom/server";
4+
import { useUIActions } from "../../../src/ui/hooks/useUIActions.js";
5+
6+
type UseUIActionsResult = ReturnType<typeof useUIActions>;
7+
8+
interface HookOptions {
9+
targetOrigin?: string;
10+
}
11+
12+
/**
13+
* Simple hook testing utility that renders a component using the hook
14+
* and captures the result for assertions.
15+
*/
16+
function testHook(options?: HookOptions): UseUIActionsResult {
17+
let hookResult: UseUIActionsResult | undefined;
18+
19+
const TestComponent: FunctionComponent = () => {
20+
hookResult = useUIActions(options);
21+
return null;
22+
};
23+
24+
renderToString(createElement(TestComponent));
25+
26+
if (!hookResult) {
27+
throw new Error("Hook did not return a result");
28+
}
29+
30+
return hookResult;
31+
}
32+
33+
describe("useUIActions", () => {
34+
let postMessageMock: Mock;
35+
let originalWindow: typeof globalThis.window;
36+
37+
beforeEach(() => {
38+
originalWindow = globalThis.window;
39+
postMessageMock = vi.fn();
40+
41+
// Create a minimal window mock with parent.postMessage
42+
globalThis.window = {
43+
parent: {
44+
postMessage: postMessageMock,
45+
},
46+
} as unknown as typeof globalThis.window;
47+
});
48+
49+
afterEach(() => {
50+
globalThis.window = originalWindow;
51+
vi.restoreAllMocks();
52+
});
53+
54+
it("intent() sends a message with name and params", () => {
55+
const actions = testHook();
56+
57+
actions.intent("create-task", { title: "Test Task" });
58+
59+
expect(postMessageMock).toHaveBeenCalledWith(
60+
{
61+
type: "intent",
62+
payload: {
63+
intent: "create-task",
64+
params: { title: "Test Task" },
65+
},
66+
},
67+
"*"
68+
);
69+
});
70+
71+
it("intent() sends a message without params when not provided", () => {
72+
const actions = testHook();
73+
74+
actions.intent("cancel");
75+
76+
expect(postMessageMock).toHaveBeenCalledWith(
77+
{
78+
type: "intent",
79+
payload: {
80+
intent: "cancel",
81+
params: undefined,
82+
},
83+
},
84+
"*"
85+
);
86+
});
87+
88+
it("notify() sends a notification message", () => {
89+
const actions = testHook();
90+
91+
actions.notify("Operation completed successfully");
92+
93+
expect(postMessageMock).toHaveBeenCalledWith(
94+
{
95+
type: "notify",
96+
payload: {
97+
message: "Operation completed successfully",
98+
},
99+
},
100+
"*"
101+
);
102+
});
103+
104+
it("prompt() sends a prompt message", () => {
105+
const actions = testHook();
106+
107+
actions.prompt("What is the status of my database?");
108+
109+
expect(postMessageMock).toHaveBeenCalledWith(
110+
{
111+
type: "prompt",
112+
payload: {
113+
prompt: "What is the status of my database?",
114+
},
115+
},
116+
"*"
117+
);
118+
});
119+
120+
it("tool() sends a tool message with name and params", () => {
121+
const actions = testHook();
122+
123+
actions.tool("listDatabases", { connectionString: "mongodb://localhost" });
124+
125+
expect(postMessageMock).toHaveBeenCalledWith(
126+
{
127+
type: "tool",
128+
payload: {
129+
toolName: "listDatabases",
130+
params: { connectionString: "mongodb://localhost" },
131+
},
132+
},
133+
"*"
134+
);
135+
});
136+
137+
it("tool() sends a tool message without params when not provided", () => {
138+
const actions = testHook();
139+
140+
actions.tool("getServerInfo");
141+
142+
expect(postMessageMock).toHaveBeenCalledWith(
143+
{
144+
type: "tool",
145+
payload: {
146+
toolName: "getServerInfo",
147+
params: undefined,
148+
},
149+
},
150+
"*"
151+
);
152+
});
153+
154+
it("link() sends a link message with a URL", () => {
155+
const actions = testHook();
156+
157+
actions.link("https://mongodb.com/docs");
158+
159+
expect(postMessageMock).toHaveBeenCalledWith(
160+
{
161+
type: "link",
162+
payload: {
163+
url: "https://mongodb.com/docs",
164+
},
165+
},
166+
"*"
167+
);
168+
});
169+
170+
it("reportSizeChange() sends size change with both dimensions", () => {
171+
const actions = testHook();
172+
173+
actions.reportSizeChange({ width: 400, height: 300 });
174+
175+
expect(postMessageMock).toHaveBeenCalledWith(
176+
{
177+
type: "ui-size-change",
178+
payload: { width: 400, height: 300 },
179+
},
180+
"*"
181+
);
182+
});
183+
184+
it("reportSizeChange() sends size change with only width", () => {
185+
const actions = testHook();
186+
187+
actions.reportSizeChange({ width: 500 });
188+
189+
expect(postMessageMock).toHaveBeenCalledWith(
190+
{
191+
type: "ui-size-change",
192+
payload: { width: 500 },
193+
},
194+
"*"
195+
);
196+
});
197+
198+
it("reportSizeChange() sends size change with only height", () => {
199+
const actions = testHook();
200+
201+
actions.reportSizeChange({ height: 250 });
202+
203+
expect(postMessageMock).toHaveBeenCalledWith(
204+
{
205+
type: "ui-size-change",
206+
payload: { height: 250 },
207+
},
208+
"*"
209+
);
210+
});
211+
212+
it("uses custom targetOrigin when provided in options", () => {
213+
const actions = testHook({ targetOrigin: "https://example.com" });
214+
215+
actions.notify("test message");
216+
217+
expect(postMessageMock).toHaveBeenCalledWith(
218+
{
219+
type: "notify",
220+
payload: {
221+
message: "test message",
222+
},
223+
},
224+
"https://example.com"
225+
);
226+
});
227+
228+
it("defaults targetOrigin to '*' when not provided", () => {
229+
const actions = testHook();
230+
231+
actions.notify("test message");
232+
233+
expect(postMessageMock).toHaveBeenCalledWith(expect.any(Object), "*");
234+
});
235+
});

0 commit comments

Comments
 (0)