Skip to content

Testing

Patterns for testing both the agent-side MCP Client and frontend React SDK.

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);
}
}
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!" });
});
});
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);
});

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" });
});
const result = validateComponentCode(compiledCode);
expect(result.valid).toBe(true);
expect(result.checks.hasDefaultExport).toBe(true);
expect(result.checks.noEval).toBe(true);
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);
});