import { describe, it, expect } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { z } from "zod"; import { JSONGenerativeUI as ClientGenUI } from "./JSONGenerativeUI.client"; import { JSONGenerativeUI as ServerGenUI } from "./JSONGenerativeUI.server"; import { defineGenerativeComponents } from "./defineGenerativeComponents"; import type { GenerativeUILibrary } from "./types"; const library: GenerativeUILibrary = { Card: { description: "A card.", properties: z.object({ title: z.string() }), render: ({ title, children }: any) => (
{children}
), }, Button: { description: "A button.", properties: z.object({ label: z.string() }), render: ({ label }: any) => , }, }; const renderTool = (tool: any, args: unknown) => renderToStaticMarkup( <>{tool.render({ args, status: { type: "complete" } })}, ); describe("JSONGenerativeUI — client build", () => { const ui = new ClientGenUI({ library }); it("present is a frontend tool with matching parameters, render, and execute", () => { const tool = ui.present(); expect(tool.type).toBe("frontend"); expect(typeof tool.execute).toBe("function"); expect(typeof tool.render).toBe("function"); expect(tool.unstable_backendDefault).toEqual({ parameters: true }); expect((tool.parameters as any).properties.$type.enum).toEqual([ "Card", "Button", ]); }); it("present renders the model's tree against the library", () => { const html = renderTool(ui.present(), { $type: "Card", title: "Hi" }); expect(html).toContain('
'); }); it("wraps the tree so top-level siblings are spaced by the surface", () => { const html = renderTool(ui.present(), [ { $type: "Card", title: "One" }, { $type: "Card", title: "Two" }, ]); expect(html.match(/data-aui="root"/g)).toHaveLength(1); expect(html.indexOf('data-title="One"')).toBeLessThan( html.indexOf('data-title="Two"'), ); }); it("leaves the surface childless until the stream produces a node", () => { // `:empty` is what hides it, so the contract is that it has no children. expect(renderTool(ui.present(), undefined)).toBe( '
', ); expect(renderTool(ui.present(), [])).toBe('
'); }); it("prompt_user is a human tool that renders the tree (no execute)", () => { const tool = ui.promptUser(); expect(tool.type).toBe("human"); expect(tool.unstable_backendDefault).toEqual({ parameters: true }); expect((tool as any).execute).toBeUndefined(); const html = renderTool(tool, { $type: "Button", label: "ok" }); expect(html).toContain(""); }); }); describe("JSONGenerativeUI — server build", () => { const ui = new ServerGenUI({ library }); it("present carries only schema (no render/execute) and matches the client schema", () => { const tool = ui.present() as any; expect(tool.type).toBe("frontend"); expect(tool.render).toBeUndefined(); expect(tool.execute).toBeUndefined(); expect(tool.unstable_backendDefault).toBeUndefined(); expect(tool.parameters.properties.$type.enum).toEqual(["Card", "Button"]); expect(tool.parameters).toEqual( new ClientGenUI({ library }).present().parameters, ); }); it("prompt_user carries only schema (no render)", () => { const tool = ui.promptUser() as any; expect(tool.type).toBe("human"); expect(tool.render).toBeUndefined(); expect(tool.unstable_backendDefault).toBeUndefined(); expect(tool.parameters.properties.$type.enum).toEqual(["Card", "Button"]); }); }); describe("defineGenerativeComponents", () => { it("throws at runtime — it must be stripped by the compiler, never called", () => { expect(() => defineGenerativeComponents({})).toThrow( /no runtime implementation/, ); }); });