import { describe, expect, it } from "vitest"; import { isUiEnvelopeAllowed, parseUiEnvelope, parseUiRegistry } from "./validator"; import type { UIRegistry } from "./types"; const registry: UIRegistry = { schema_version: "agent-ui-registry@1", chart_grammars: ["echarts-safe-subset"], components: [ { id: "HistoryPanel", supportedSurfaces: ["side_panel", "canvas"] }, { id: "ScadaPanel", supportedSurfaces: ["side_panel", "canvas"] } ], actions: [{ id: "zoom_to_map", supportedSurfaces: ["map_overlay"] }] }; describe("UIEnvelope validation", () => { it("fails closed when the backend registry is unavailable", () => { const envelope = parseUiEnvelope({ kind: "chart", schemaVersion: "agent-ui@1", grammar: "echarts-safe-subset", surface: "chat_inline", spec: { chart_type: "line" }, data: { x_data: ["a"], series: [{ name: "s", data: [1] }] } }); expect(envelope).not.toBeNull(); expect(envelope ? isUiEnvelopeAllowed(envelope, null) : true).toBe(false); }); it("rejects registered components outside the frontend allowlist", () => { const envelope = parseUiEnvelope({ kind: "registered_component", schemaVersion: "agent-ui@1", component: "UnsafePanel", surface: "side_panel", props: {} }); expect(envelope).toBeNull(); }); it("rejects registered components on unsupported surfaces", () => { const envelope = parseUiEnvelope({ kind: "registered_component", schemaVersion: "agent-ui@1", component: "HistoryPanel", surface: "chat_inline", props: { feature_infos: [["J1", "junction"]], data_type: "realtime" } }); expect(envelope).not.toBeNull(); expect(envelope ? isUiEnvelopeAllowed(envelope, registry) : false).toBe(false); }); it("accepts only the echarts-safe-subset chart grammar", () => { expect( parseUiEnvelope({ kind: "chart", schemaVersion: "agent-ui@1", grammar: "vega-lite", surface: "chat_inline", spec: { chart_type: "line" }, data: { x_data: ["a"], series: [{ name: "s", data: [1] }] } }) ).toBeNull(); const envelope = parseUiEnvelope({ kind: "chart", schemaVersion: "agent-ui@1", grammar: "echarts-safe-subset", surface: "chat_inline", spec: { chart_type: "line" }, data: { x_data: ["a"], series: [{ name: "s", data: [1] }] } }); expect(envelope).not.toBeNull(); expect(envelope ? isUiEnvelopeAllowed(envelope, registry) : false).toBe(true); }); it("rejects unsupported chart types and mismatched data", () => { expect(parseUiEnvelope({ kind: "chart", schemaVersion: "agent-ui@1", grammar: "echarts-safe-subset", surface: "chat_inline", spec: { chart_type: "pie" }, data: { x_data: ["a"], series: [{ name: "s", data: [1] }] } })).toBeNull(); expect(parseUiEnvelope({ kind: "chart", schemaVersion: "agent-ui@1", grammar: "echarts-safe-subset", surface: "chat_inline", spec: { chart_type: "line" }, data: { x_data: ["a", "b"], series: [{ name: "s", data: [1] }] } })).toBeNull(); }); it("filters invalid registry surfaces before allowlist checks", () => { const parsed = parseUiRegistry({ schema_version: "agent-ui-registry@1", chart_grammars: ["echarts-safe-subset"], components: [{ id: "HistoryPanel", supportedSurfaces: ["side_panel", "unsafe_surface"] }], actions: [] }); expect(parsed?.components[0]?.supportedSurfaces).toEqual(["side_panel"]); }); });