feat(studio): studio web UI (course list, manifest, lessons with live preview, decks, attachments, exercises, conflicts, sync) and dev entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
497ce91216
commit
0992df7257
18 files changed
+1403
-5
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { renderMarkdown } from "../src/ui/markdown";
|
||||
|
||||
test("headings, paragraphs, emphasis, inline code", () => {
|
||||
expect(renderMarkdown("# Title\n\nSome **bold** and *em* and `x < y`.\nSecond line.")).toBe(
|
||||
'<h1 id="title">Title</h1>\n<p>Some <strong>bold</strong> and <em>em</em> and <code>x < y</code>.\nSecond line.</p>',
|
||||
);
|
||||
});
|
||||
|
||||
test("raw HTML is escaped, except the details/summary hint blocks", () => {
|
||||
expect(renderMarkdown("<script>alert(1)</script>")).toBe("<p><script>alert(1)</script></p>");
|
||||
const h = renderMarkdown("<details>\n<summary>Exercise 1 — nudge</summary>\n\nTry **this**.\n\n</details>");
|
||||
expect(h).toMatch(/<details>\s*<summary>Exercise 1 — nudge<\/summary>/);
|
||||
expect(h).toContain("<p>Try <strong>this</strong>.</p>");
|
||||
expect(h).toContain("</details>");
|
||||
expect(renderMarkdown("<summary onclick=x>a</summary>")).not.toContain("<summary onclick");
|
||||
});
|
||||
|
||||
test("fenced code keeps content verbatim and escaped", () => {
|
||||
expect(renderMarkdown("```js\nconst a = '<b>';\n# not a heading\n```")).toBe(
|
||||
'<pre class="code"><code data-lang="js">const a = \'<b>\';\n# not a heading</code></pre>',
|
||||
);
|
||||
});
|
||||
|
||||
test("lists, quotes, rules", () => {
|
||||
expect(renderMarkdown("- a\n- b\n\n1. one\n2. two")).toBe("<ul><li>a</li><li>b</li></ul>\n<ol><li>one</li><li>two</li></ol>");
|
||||
expect(renderMarkdown("> quoted\n\n---")).toBe("<blockquote><p>quoted</p></blockquote>\n<hr>");
|
||||
});
|
||||
|
||||
test("links: safe schemes only; images resolved through the given resolver", () => {
|
||||
expect(renderMarkdown("[ok](https://x.org/a?b=1&c=2)")).toBe('<p><a href="https://x.org/a?b=1&c=2">ok</a></p>');
|
||||
expect(renderMarkdown("[bad](javascript:alert(1))")).toBe("<p>bad</p>");
|
||||
expect(renderMarkdown("", { resolveUrl: (u) => `/raw/${encodeURIComponent(u)}` })).toBe('<p><img src="/raw/img%2Fa%20b.png" alt="d"></p>');
|
||||
});
|
||||
|
||||
test("front matter is not rendered", () => {
|
||||
expect(renderMarkdown("---\ntitle: x\n---\n# H")).toBe('<h1 id="h">H</h1>');
|
||||
});
|
||||
Reference in new issue
Block a user