File size: 4,392 Bytes
e18726e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | // 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("preserves node-panel presentation metadata", () => {
expect(
extractCanvasFromText(
JSON.stringify({
kind: "canvas",
presentation: { target: "node_panel", title: "Status" },
view: { id: "cv_status", url: "/__openclaw__/canvas/documents/cv_status/index.html" },
}),
),
).toMatchObject({ surface: "node_panel", viewId: "cv_status", title: "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");
});
});
it("removes a shortcode without rewriting surrounding literal whitespace", () => {
const source = ' a\n\n\n b\n\n[embed ref="doc1" /]';
expect(extractCanvasShortcodes(source)).toMatchObject({
text: " a\n\n\n b\n\n",
previews: [{ viewId: "doc1" }],
});
});
|