Files
opencode/services/site/test/docs-rehype.test.ts

99 lines
4.4 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import type { Element, ElementContent, Root } from "hast"
import { toText } from "hast-util-to-text"
import { VFile } from "vfile"
import rehypeHeadings from "~/docs/rehype-headings"
import rehypeShiki from "~/docs/rehype-shiki"
// The MDX components rehype-shiki.ts and rehype-headings.ts put in the tree, as MDX compiles them.
type Jsx = { type: string; name: string; attributes: { name: string; value: string }[]; children: ElementContent[] }
const attributes = (node: Jsx) => Object.fromEntries(node.attributes.map((item) => [item.name, item.value]))
async function fence(lang: string, text: string, meta?: string) {
const code: Element = {
type: "element",
tagName: "code",
properties: { className: [`language-${lang}`] },
children: [{ type: "text", value: `${text}\n` }],
data: meta ? ({ meta } as Element["data"]) : undefined,
}
const tree: Root = { type: "root", children: [{ type: "element", tagName: "pre", properties: {}, children: [code] }] }
await rehypeShiki()(tree)
return tree.children[0] as unknown as Jsx
}
describe("docs code frames", () => {
test("a shell session copies its commands, without the prompts or the output", async () => {
const frame = await fence("bash", "$ opencode pair\nPairing code: 123456\n$ opencode service status")
expect(frame.name).toBe("CodeFrame")
expect(attributes(frame)).toMatchObject({
variant: "terminal",
kind: "terminal",
label: "Terminal",
copy: "$ opencode pair\n$ opencode service status",
})
const pre = frame.children[0] as Element
expect(toText(pre, { whitespace: "pre" })).toBe("$ opencode pair\nPairing code: 123456\n$ opencode service status")
const tokens = (pre.children[0] as Element).children.flatMap((node) =>
node.type === "element" ? [node.properties.dataToken] : [],
)
expect(tokens).toEqual(["prompt", "output", "prompt"])
})
test("a titled block names its file, with the file's icon, and other titles stay plain labels", async () => {
expect(attributes(await fence("jsonc", "{}", 'title="opencode.jsonc"'))).toMatchObject({
variant: "codeTitle",
kind: "file",
label: "opencode.jsonc",
copy: "{}",
})
expect(attributes(await fence("json", "{}", 'title="theme.json (excerpt)"'))).toMatchObject({
kind: "file",
label: "theme.json (excerpt)",
file: "theme.json",
})
expect(attributes(await fence("jsonc", "{}", 'title="Inside base"'))).toMatchObject({
variant: "codeTitle",
kind: "code",
label: "Inside base",
})
expect(attributes(await fence("ts", "const a = 1"))).toMatchObject({ variant: "code", label: "TypeScript" })
})
test("a box-drawn tree in a text block is the site's file tree, with nothing to copy", async () => {
const frame = await fence("text", ".opencode/\n└── plugins/\n └── concise.ts")
expect(attributes(frame)).toEqual({ variant: "fileTree", kind: "tree", label: "Files" })
const tree = frame.children[0] as unknown as Jsx
expect(tree.name).toBe("FileTree")
expect(attributes(tree).tree).toBe(".opencode/\n└── plugins/\n └── concise.ts")
})
test("a diff copies the result, without the removed lines", async () => {
const frame = await fence("diff", ' {\n- "bash": "allow"\n+ "bash": "ask"\n }')
expect(attributes(frame)).toMatchObject({ variant: "codeDiff", copy: '{\n "bash": "ask"\n}' })
})
})
describe("docs headings", () => {
test("keep their ids and wrap h2 and h3 in the link that copies their address", () => {
const heading = (tagName: string, text: string): Element => ({
type: "element",
tagName,
properties: {},
children: [{ type: "text", value: text }],
})
const tree: Root = { type: "root", children: [heading("h2", "Local models"), heading("h4", "Ollama")] }
const file = new VFile()
rehypeHeadings()(tree, file)
const [h2, h4] = tree.children as Element[]
expect(h2.properties.id).toBe("local-models")
expect((h2.children[0] as unknown as Jsx).name).toBe("HeadingLink")
expect(attributes(h2.children[0] as unknown as Jsx)).toEqual({ id: "local-models", title: "Local models" })
expect(h4.children[0]).toEqual({ type: "text", value: "Ollama" })
expect(file.data.headings).toEqual([
{ depth: 2, slug: "local-models", text: "Local models" },
{ depth: 4, slug: "ollama", text: "Ollama" },
])
})
})