Files
openclaw/src/chat/canvas-render.test.ts
T
Peter Steinberger 64c82812a5 feat(dashboard): pinned MCP apps — board rendering, lease re-mint, durable tool grants (#111524)
* feat(boards): mint pinned MCP app views

* feat(ui): render pinned MCP apps on dashboards

* fix(dashboard): reconcile MCP app rebase

* fix(dashboard): align generated CI contracts

* test(gateway): record board.widget.appView release train

* fix(dashboard): revalidate pinned MCP app views
2026-07-19 19:51:26 -07:00

110 lines
3.6 KiB
TypeScript

// Canvas-render tests cover [embed] shortcode extraction and text stripping.
import { describe, expect, it } from "vitest";
import {
extractCanvasFromDetails,
extractCanvasFromText,
extractCanvasShortcodes,
} from "./canvas-render.ts";
describe("extractCanvasFromText", () => {
it("preserves a valid pinned board widget identity", () => {
expect(
extractCanvasFromText(
JSON.stringify({
kind: "canvas",
view: {
id: "cv_status",
url: "/__openclaw__/canvas/documents/cv_status/index.html",
boardWidgetName: "release-status",
},
}),
),
).toMatchObject({ viewId: "cv_status", boardWidgetName: "release-status" });
});
it("extracts safe MCP App preview metadata from tool details", () => {
expect(
extractCanvasFromDetails({
mcpAppPreview: {
kind: "canvas",
view: { id: "cv_app" },
presentation: { target: "assistant_message", sandbox: "scripts" },
mcpApp: {
viewId: "cv_app",
serverName: "demo",
toolName: "show",
uiResourceUri: "ui://demo/app",
toolCallId: "call-1",
originSessionKey: "agent:main:main",
resultMetaState: "unavailable",
},
},
}),
).toMatchObject({
viewId: "cv_app",
mcpApp: {
viewId: "cv_app",
serverName: "demo",
toolName: "show",
uiResourceUri: "ui://demo/app",
toolCallId: "call-1",
originSessionKey: "agent:main:main",
resultMetaState: "unavailable",
},
});
});
it("keeps MCP App previews opaque while preserving model-visible results", () => {
const preview = extractCanvasFromText(
JSON.stringify({
kind: "canvas",
view: { id: "cv_app" },
presentation: { target: "assistant_message", sandbox: "scripts" },
mcpApp: { viewId: "cv_app" },
result: [{ type: "text", text: "model-visible result" }],
}),
);
expect(preview).toMatchObject({
viewId: "cv_app",
sandbox: "scripts",
mcpApp: { viewId: "cv_app" },
});
});
});
describe("extractCanvasShortcodes", () => {
it("does not let a self-closing embed start a greedy block match", () => {
// Regression: the block regex used to greedily swallow the span from a
// self-closing "[embed ... /]" open tag up to a later stray "[/embed]",
// deleting the visible text in between (" keep me ") from channel delivery.
const input = '[embed url="https://a.com" /] keep me [/embed]';
const { text, previews } = extractCanvasShortcodes(input);
expect(previews).toHaveLength(1);
expect(previews[0]?.url).toBe("https://a.com");
// The visible text between the self-closing embed and the stray close
// marker must be preserved, not silently stripped.
expect(text).toContain("keep me");
expect(text).toBe("keep me [/embed]");
});
it("still extracts a normal block embed and strips only the shortcode span", () => {
const input = 'before [embed ref="doc1"] hi [/embed] after';
const { text, previews } = extractCanvasShortcodes(input);
expect(previews).toHaveLength(1);
expect(previews[0]?.viewId).toBe("doc1");
expect(text).toBe("before after");
});
it("still extracts a plain self-closing embed and keeps surrounding text", () => {
const input = 'see [embed url="https://b.com" /] end';
const { text, previews } = extractCanvasShortcodes(input);
expect(previews).toHaveLength(1);
expect(previews[0]?.url).toBe("https://b.com");
expect(text).toBe("see end");
});
});