Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import { ChatMarkdown } from "@rakazo/chat-ui/web";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
describe("ChatMarkdown", () => {
|
|
it("renders the formatting commonly emitted by assistants", () => {
|
|
const html = renderToStaticMarkup(
|
|
<ChatMarkdown>{"## Capabilities\n\n- **Write files**\n- Run `commands`"}</ChatMarkdown>,
|
|
);
|
|
|
|
expect(html).toContain("<h2>Capabilities</h2>");
|
|
expect(html).toContain("<ul>");
|
|
expect(html).toContain("<strong>Write files</strong>");
|
|
expect(html).toContain("<code>commands</code>");
|
|
});
|
|
|
|
it("does not inject raw HTML or unsafe link protocols", () => {
|
|
const html = renderToStaticMarkup(
|
|
<ChatMarkdown>{'<script>alert("xss")</script> [bad](javascript:alert(1))'}</ChatMarkdown>,
|
|
);
|
|
|
|
expect(html).not.toContain("<script");
|
|
expect(html).not.toContain("javascript:");
|
|
});
|
|
|
|
it("renders incomplete streaming code fences as code", () => {
|
|
const html = renderToStaticMarkup(
|
|
<ChatMarkdown streaming>{"```ts\nconst live = true;"}</ChatMarkdown>,
|
|
);
|
|
|
|
expect(html).toContain("<pre>");
|
|
expect(html).toContain("const live = true;");
|
|
expect(html).toContain("rk-chat-markdown-cursor");
|
|
});
|
|
|
|
it("renders a copy button alongside each code block", () => {
|
|
const html = renderToStaticMarkup(
|
|
<ChatMarkdown>{"```ts\nconst value = 1;\n```"}</ChatMarkdown>,
|
|
);
|
|
|
|
expect(html).toContain("rk-chat-markdown-pre-wrap");
|
|
expect(html).toContain('aria-label="Copy code"');
|
|
expect(html).toContain("rk-chat-markdown-copy");
|
|
});
|
|
});
|