Testing
Patterns for testing both the agent-side MCP Client and frontend React SDK.
Testing Agent-Side Code
Section titled “Testing Agent-Side Code”Mocking GguiClient
Section titled “Mocking GguiClient”import type { GguiPushOutput, GguiConsumeOutput, ActionEnvelope } from "@ggui-ai/mcp-client";
class MockGguiClient { private sessions = new Map<string, ActionEnvelope[]>(); public pushCalls: Array<{ prompt: string; context?: unknown }> = [];
async push(input: { prompt: string; context?: unknown; sessionId?: string }) { this.pushCalls.push(input); const sessionId = input.sessionId || `session_${Date.now()}`; this.sessions.set(sessionId, []); return { sessionId, url: `https://render.guuey.com/mock_${sessionId}`, shortCode: "mock123", stackItemId: `card_${Date.now()}`, renderUrl: `https://render.guuey.com/mock123`, } satisfies GguiPushOutput; }
async consume(sessionId: string) { const events = this.sessions.get(sessionId) || []; this.sessions.set(sessionId, []); return { events, status: events.length > 0 ? "completed" : "active" } as GguiConsumeOutput; }
async close(sessionId: string) { this.sessions.delete(sessionId); return { success: true }; }
simulateSubmit(sessionId: string, data: unknown) { const events = this.sessions.get(sessionId) || []; events.push({ sessionId, type: "data:submit", payload: data, stackIndex: 0, } as unknown as ActionEnvelope); this.sessions.set(sessionId, events); }}Using the mock in tests
Section titled “Using the mock in tests”import { describe, it, expect, beforeEach } from "vitest";
describe("feedback agent", () => { let mockGgui: MockGguiClient;
beforeEach(() => { mockGgui = new MockGguiClient(); });
it("collects user feedback", async () => { const { sessionId, url } = await mockGgui.push({ story: { intent: "Collect feedback", prompt: "Show a feedback form" }, });
expect(url).toContain("render.guuey.com"); mockGgui.simulateSubmit(sessionId, { rating: 5, comments: "Great!" });
const { events, status } = await mockGgui.consume(sessionId); expect(status).toBe("completed"); expect(events[0].payload).toEqual({ rating: 5, comments: "Great!" }); });});Testing error handling
Section titled “Testing error handling”import { GguiAuthError, GguiTimeoutError } from "@ggui-ai/mcp-client";
it("handles auth errors", async () => { const failingClient = { push: async () => { throw new GguiAuthError(); }, }; await expect(failingClient.push({ story: { intent: "test" } })).rejects.toThrow(GguiAuthError);});Testing Frontend Code (React SDK)
Section titled “Testing Frontend Code (React SDK)”Using test utilities
Section titled “Using test utilities”import { setupMockTools, resolveBindingsForTest, validateComponentCode,} from "@ggui-ai/react/testing";
beforeEach(() => { setupMockTools({ auth: { id: "user-123", name: "Test User" }, fetch: { "/api/users": [{ id: 1, name: "Alice" }] }, storage: { theme: "dark" }, });});
it("resolves dependent bindings", async () => { const result = await resolveBindingsForTest({ user: { tool: "auth", config: { field: "currentUser" } }, profile: { tool: "fetch", config: { endpoint: "/api/users/{user.id}/profile" }, dependsOn: ["user"], }, });
expect(result.success).toBe(true); expect(result.data.user).toEqual({ id: "user-123", name: "Test User" });});Validating generated code
Section titled “Validating generated code”const result = validateComponentCode(compiledCode);expect(result.valid).toBe(true);expect(result.checks.hasDefaultExport).toBe(true);expect(result.checks.noEval).toBe(true);Testing data bindings
Section titled “Testing data bindings”import { validateBindings } from "@ggui-ai/react";
it("rejects circular dependencies", () => { expect(() => validateBindings({ a: { tool: "fetch", config: { endpoint: "/a" }, dependsOn: ["b"] }, b: { tool: "fetch", config: { endpoint: "/b" }, dependsOn: ["a"] }, }) ).toThrow(/circular dependency/i);});