Files
backnotprop__plannotator/packages/guide-viewer/renderInlineMarkdown.test.tsx
Michael Ramos 64062af9a1 feat: Portable Guided Reviews — export, share links, agent-authored guides, guides.show (#1324)
A Guided Review can now leave Plannotator: as a single self-contained HTML file that renders exactly like the in-app guide, as an encrypted-by-default share link on guides.show, or authored by any agent through the new guide CLI.

Highlights: packages/guide-viewer extracted from review-editor at the injection seam (read-only host, no third renderer); guides.show Worker with R2-backed share storage, per-IP rate limiting on creation, delete tokens hashed at rest, and 128-bit ids; portable exports pin the viewer by SRI hash with budget and manifest gates in PR CI and at deploy; two-runtime parity across Bun and Pi verified; v0.27.x saved guides load unchanged. Retention is indefinite by explicit decision, to revisit with the lean sharing refactor.

Decision record: adr/decisions/007-portable-guided-reviews-20260815.md
2026-08-16 12:17:13 -07:00

46 lines
2.2 KiB
TypeScript

import { describe, it, expect } from "bun:test";
import { renderInlineMarkdown } from "./renderInlineMarkdown";
const isElement = (node: unknown): node is { type: string; props: { children?: unknown } } =>
typeof node === "object" && node !== null && "type" in node && "props" in node;
const types = (nodes: unknown[]) => nodes.map((node) => (isElement(node) ? node.type : typeof node));
describe("renderInlineMarkdown", () => {
it("renders underscore emphasis", () => {
const nodes = renderInlineMarkdown("_text_");
expect(nodes).toHaveLength(1);
expect(isElement(nodes[0])).toBe(true);
expect((nodes[0] as { type: string; props: { children?: unknown } }).type).toBe("em");
expect((nodes[0] as { type: string; props: { children?: unknown } }).props.children).toBe("text");
});
it("renders underscore emphasis in context", () => {
const nodes = renderInlineMarkdown("foo _bar_ baz");
expect(nodes.filter(isElement)).toHaveLength(1);
expect(types(nodes)).toContain("em");
expect((nodes.find(isElement) as { type: string; props: { children?: unknown } }).props.children).toBe("bar");
expect(nodes.filter((node) => typeof node === "string").join("")).toBe("foo baz");
});
it("keeps intraword underscores literal", () => {
expect(renderInlineMarkdown("snake_case")).toEqual(["snake_case"]);
expect(renderInlineMarkdown("foo_bar_baz")).toEqual(["foo_bar_baz"]);
expect(renderInlineMarkdown("__init__")).toEqual(["__init__"]);
});
it("renders underscore emphasis after other inline tokens", () => {
const boldNodes = renderInlineMarkdown("**bold**_italic_");
expect(types(boldNodes)).toEqual(["strong", "em"]);
expect((boldNodes[1] as { type: string; props: { children?: unknown } }).props.children).toBe("italic");
const codeNodes = renderInlineMarkdown("`code`_italic_");
expect(types(codeNodes)).toEqual(["code", "em"]);
expect((codeNodes[1] as { type: string; props: { children?: unknown } }).props.children).toBe("italic");
const linkNodes = renderInlineMarkdown("[link](https://example.com)_italic_");
expect(types(linkNodes)).toEqual(["a", "em"]);
expect((linkNodes[1] as { type: string; props: { children?: unknown } }).props.children).toBe("italic");
});
});