openclaw/test/scripts/docs-markdown.test.ts
Peter Steinberger 4b17ae27c2
Some checks are pending
ClawSweeper Dispatch / dispatch (push) Waiting to run
CodeQL / scope (push) Waiting to run
CodeQL / Security High () (push) Blocked by required conditions
Control UI Locale Refresh / resolve-base (push) Waiting to run
Control UI Locale Refresh / Verify generated PR App permissions (push) Blocked by required conditions
Control UI Locale Refresh / Refresh (push) Blocked by required conditions
Control UI Locale Refresh / Commit control UI locale refresh (push) Blocked by required conditions
Docs Sync Publish Repo / sync-publish-repo (push) Waiting to run
Docs / docs (push) Waiting to run
Native App Locale Refresh / Refresh native hi (push) Blocked by required conditions
Native App Locale Refresh / Refresh native id (push) Blocked by required conditions
Native App Locale Refresh / Refresh native it (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ja-JP (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ko (push) Blocked by required conditions
Native App Locale Refresh / Refresh native nl (push) Blocked by required conditions
Native App Locale Refresh / Refresh native pl (push) Blocked by required conditions
Native App Locale Refresh / Refresh native pt-BR (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ru (push) Blocked by required conditions
Native App Locale Refresh / Refresh native sv (push) Blocked by required conditions
Native App Locale Refresh / Refresh native th (push) Blocked by required conditions
Native App Locale Refresh / Refresh native tr (push) Blocked by required conditions
Native App Locale Refresh / Refresh native uk (push) Blocked by required conditions
Native App Locale Refresh / Refresh native vi (push) Blocked by required conditions
Native App Locale Refresh / Refresh native zh-CN (push) Blocked by required conditions
Native App Locale Refresh / Refresh native zh-TW (push) Blocked by required conditions
Native App Locale Refresh / Commit native locale refresh (push) Blocked by required conditions
Native App Locale Refresh / resolve-base (push) Waiting to run
Native App Locale Refresh / Verify generated PR App permissions (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ar (push) Blocked by required conditions
Native App Locale Refresh / Refresh native de (push) Blocked by required conditions
Native App Locale Refresh / Refresh native es (push) Blocked by required conditions
Native App Locale Refresh / Refresh native fa (push) Blocked by required conditions
Native App Locale Refresh / Refresh native fr (push) Blocked by required conditions
Node Runtime Conformance / scope (push) Waiting to run
Node Runtime Conformance / TypeScript contracts (push) Blocked by required conditions
Node Runtime Conformance / Rust workspace (push) Blocked by required conditions
Plugin Init Scaffold Validation / scope (push) Waiting to run
Plugin Init Scaffold Validation / Validate provider scaffold (push) Blocked by required conditions
Plugin NPM Release / preview_plugins_npm (push) Waiting to run
Plugin NPM Release / Validate release publish approval (push) Blocked by required conditions
Plugin NPM Release / preview_plugin_pack (push) Blocked by required conditions
Plugin NPM Release / Preflight plugin npm package () (push) Blocked by required conditions
Plugin NPM Release / Seal prepared plugin npm release (push) Blocked by required conditions
Plugin NPM Release / Trusted publisher OIDC exchange (push) Blocked by required conditions
Plugin NPM Release / approve_plugins_npm_release (push) Blocked by required conditions
Plugin NPM Release / Publish plugin npm package () (push) Blocked by required conditions
Plugin NPM Release / verify_plugins_npm (push) Blocked by required conditions
Vitest Cache Warm / dependencies (push) Waiting to run
Vitest Cache Warm / warm (push) Waiting to run
Website Installer Sync / macos-installer (push) Blocked by required conditions
Website Installer Sync / windows-installer (push) Blocked by required conditions
Website Installer Sync / Select installer platforms (push) Waiting to run
Website Installer Sync / static (push) Blocked by required conditions
Website Installer Sync / Docker installers (bookworm) (push) Blocked by required conditions
Website Installer Sync / Docker installers (trixie) (push) Blocked by required conditions
Website Installer Sync / Debian installer (bookworm) (push) Blocked by required conditions
Website Installer Sync / Debian installer (trixie) (push) Blocked by required conditions
Website Installer Sync / Build tools failure (bookworm) (push) Blocked by required conditions
Website Installer Sync / Build tools failure (trixie) (push) Blocked by required conditions
Website Installer Sync / Non-root installer (bookworm) (push) Blocked by required conditions
Website Installer Sync / Non-root installer (trixie) (push) Blocked by required conditions
Website Installer Sync / Fedora installer (non-root) (push) Blocked by required conditions
Website Installer Sync / Fedora installer (root) (push) Blocked by required conditions
Website Installer Sync / sync-website (push) Blocked by required conditions
Workflow Sanity / actionlint (push) Waiting to run
Workflow Sanity / generated-doc-baselines (push) Waiting to run
test(gateway,cli,feishu): remove low-value tests (batch d409) (#179934)
* test(gateway): deslop y0174 tests

* test(gateway): consolidate y0174 test families

Consolidate resident resolution and plugin drain siblings into existing family owners. Remove duplicate owner-layer replays while retaining authority, publication, and lifecycle cases. Keep the existing Vitest project routing and prune deleted-file inventory entries.

CI-only validation: oxfmt, plain oxlint, and git diff --check. Shard LOC is 11626 to 10778 cumulatively; the 35 percent target remains unmet.

* test(gateway): share y0174 lifecycle fixtures

Factor repeated reload snapshots, plugin requests, environment setup, and callback counts. Share typed SSE reading and disposal across history tests, and reuse model and resolution fixture shapes without removing distinct cases.

CI-only static validation: oxfmt, plain oxlint, and diff-check pass. Shard LOC is 10529, down from 11626 originally; the 35 percent target remains unmet.

* test(gateway): deslop y0178 tests

* test(gateway): consolidate y0178 test family

* test(gateway): share y0178 recovery fixtures

* test(feishu): deslop y0176 tests

* test(feishu): consolidate y0176 monitor families

* test(feishu): share remaining y0176 lifecycle fixtures

* test(integration): deslop y0179 tests

* test(integration): consolidate y0179 authority and catalog proof

* test(integration): remove y0179 duplicate owner proof

* test(cli): consolidate y0177 auth and command fixtures

* test(scripts): deslop y0180 tests

* test(scripts): consolidate y0180 duplicate owner proof

* test(scripts): consolidate y0180 fixture ownership

* test(scripts): deslop y0182 tests

* test(scripts): consolidate y0182 fixture and owner proof

* test(scripts): keep lockfile framing proof at its owner

* test(scripts): deslop y0183 tests

* test(scripts): consolidate y0183 fixtures and duplicate proof

* test(scripts): share y0183 locale and entry fixtures

* test(node-host): deslop y0186 tests

* test(node-host): consolidate y0186 family coverage

* test(node-host): share y0186 setup and capacity assertions

* test(system-agent): deslop y0184 tests

* test(system-agent): consolidate y0184 test families

* test(system-agent): fix merged activation snapshot reads

* test(system-agent): share y0184 family fixtures

* test(gateway): deslop y0185 tests

* test: preserve fixture contracts after campaign replay

* test: prune stale mock export baseline rows
2026-10-21 15:39:00 -07:00

160 lines
6.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
import { createDocsMarkdown, parseDocsDocument } from "../../scripts/lib/docs-markdown.mjs";
const mapLink = (href: string, line: number | undefined) => ({ href, line });
function render(source: string, mapLines = false) {
const md = createDocsMarkdown();
const document = parseDocsDocument(source, md, mapLines ? { mapLink } : undefined);
return {
document,
html: md.renderer.render(document.tokens, md.options, document.env),
fences: document.tokens.filter((token) => token.type === "fence").map((token) => token.content),
};
}
const visibleParam = '<ParamField path="live">\n[Visible](/visible)\n</ParamField>';
const indent = (text: string, prefix: string) =>
text
.split("\n")
.map((line) => prefix + line)
.join("\n");
const fence = (body: string, info: string, prefix = "") =>
indent(`\`\`\`${info}\n${body}\n\`\`\``, prefix);
function expectVisible(document: ReturnType<typeof parseDocsDocument>, line?: number) {
expect(document.ids).toContain("param-live");
expect(document.links).toEqual(line ? [{ href: "/visible", line }] : ["/visible"]);
}
describe("docs Markdown rendering", () => {
it("preserves nested fence indentation with CRLF", () => {
const body = '{\n "features": {\n "example": "<Tab title=\\"literal\\">"\n }\n}';
const yaml = "app:\n scopes:\n - chat:write\n description: |\n Nested content";
const source = [
'<Tabs>\n <Tab title="Manifest">\n <Steps>\n <Step title="Create app">\n <CodeGroup>',
fence(body, "json Recommended"),
fence(body, "json Minimal", " "),
fence(yaml, "yaml", " "),
" </CodeGroup>\n </Step>\n </Steps>\n </Tab>\n</Tabs>",
'<ParamField path="live">[Visible](/visible)</ParamField>',
fence(body, "json"),
]
.join("\n")
.replaceAll("\n", "\r\n");
const { document, fences } = render(source);
expect(fences).toEqual([`${body}\n`, `${body}\n`, `${yaml}\n`, `${body}\n`]);
expectVisible(document);
});
it.each([
["APIUsage", "سلوك إعادة المحاولة", ["param-apiusage", "slwk-ieadt-almhawlt"]],
[
"APISection fooBar APIs DNS2API",
"ու ՈՒ Ու aŒb aœb aƏb aəb ẞ",
["param-api-section-foo-bar-apis-dns-2-api", "vo-vo-vo-a-b-a-b-a-b-a-b-ss"],
],
["a𝓀b a𝕆b aⓒb aⓓb", "Conway’s Law — DON’T", ["param-ahb-a-nb-a-b-b-a-c-b", "conways-law-dont"]],
["ŌōfooBar", "before−after before⁓after", ["param-oofoo-bar", "before-after-before-after"]],
])("preserves published component anchors for %s", (name, title, ids) => {
const document = parseDocsDocument(
`<ParamField body="${name}">Usage</ParamField>\n\n` +
`<Accordion title="${title}">Details</Accordion>`,
);
expect(document.ids).toEqual(ids);
});
it.each(["html", "jsx"])("keeps quoted list fences after multiline %s", (kind) => {
const source = [
`> 10. item\n>\n> ${kind === "html" ? "<pre>" : "{/*"}`,
'> <Card href="/hidden">literal</Card>\n>\n> continued literal',
`> ${kind === "html" ? "</pre>" : "*/}"}\n>`,
"> ~~~html\n> <code>\n> ~~~\n",
'<ParamField path="live" type="string">\n[Visible](/visible)\n</ParamField>\n',
"~~~html\n</code>\n~~~",
].join("\n");
const { document, html, fences } = render(source, true);
expect(fences).toEqual(["<code>\n", "</code>\n"]);
expectVisible(document, 14);
if (kind === "html") {
expect(html).toContain('<Card href="/hidden">literal</Card>');
} else {
expect(html).not.toContain("/hidden");
expect(html).not.toContain("continued literal");
}
expect(html).not.toContain("<ParamField");
});
it("removes comment-owned quote and indentation prefixes after raw HTML", () => {
const prefix = "<pre>\n> raw literal quote\n</pre>\n";
const source =
prefix +
'{/*\n hidden indented comment\n\n> hidden quoted comment\n <Card href="/hidden">hidden card</Card>\n*/}\n\n' +
visibleParam;
const { document, html } = render(source);
expect(html).not.toContain("hidden");
expect(html).not.toContain("<blockquote>");
expect(html).toContain(prefix.trimEnd());
expectVisible(document);
});
it("retains only the containing quote when removing JSX comments", () => {
const { document, html } = render(
"> {/*\n> > hidden nested quote\n> */}\n>\n> [Visible](/visible)",
true,
);
expect(html.match(/<blockquote>/g)).toHaveLength(1);
expect(html).not.toContain("hidden");
expect(document.links).toEqual([{ href: "/visible", line: 5 }]);
});
it("preserves JSX comment bytes inside indented code", () => {
const literal = "{/*\n> literal quote\n\n literal indentation\n*/}\n";
const source = `${indent(literal.trimEnd(), " ")}\n\n[Visible](/visible)`;
const document = parseDocsDocument(source);
expect(document.tokens.find((token) => token.type === "code_block")?.content).toBe(literal);
expect(document.links).toEqual(["/visible"]);
});
it("keeps apparent fences inside raw HTML opaque after a list fence", () => {
const prefix = "- Example\n\n ~~~html\n example\n\n";
const literal = '<pre>\n~~~text\n<Card href="/hidden">literal</Card>\n</pre>';
const { document, html } = render(`${prefix}${literal}\n\n${visibleParam}`);
expect(html).toContain(literal);
expect(html).not.toContain("<ParamField");
expectVisible(document);
});
it("keeps inline pre examples literal before a later HTML example", () => {
const source = [
"Intro with `<pre>`.",
'<ParamField path="source" type="string">\n' +
" Pass `<pre>` or ``<pre> with a ` backtick``.\n" +
" Or `<pre>\n attributes`.\n</ParamField>",
fence("<pre>example</pre>", "html"),
"## After the example",
"[Related](/related)",
].join("\n\n");
const { document, html } = render(source);
expect(html).toContain("<code>&lt;pre&gt;</code>");
expect(html).toContain("<code>&lt;pre&gt; with a ` backtick</code>");
expect(html).toContain("<code>&lt;pre&gt; attributes</code>");
expect(html).toContain("&lt;pre&gt;example&lt;/pre&gt;");
expect(html).not.toContain("<ParamField");
expect(document.ids).toContain("param-source");
expect(document.ids).toContain("after-the-example");
expect(document.links).toEqual(["/related"]);
});
it("preserves raw HTML containing backticks after an unmatched backtick", () => {
const prefix = "Unmatched `\n\n";
const literal = '<script>const example = `<Card href="/hidden" />`;</script>';
const { document, html } = render(`${prefix}${literal}\n\n[Visible](/visible)`);
expect(html).toContain(literal);
expect(html).not.toContain("OPENCLAW_DOCS_MARKER");
expect(document.links).toEqual(["/visible"]);
});
});