chore: upgrade marked 15→17 (ReDoS fix, list rendering improvements)
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 17m28s

This commit is contained in:
Hoid 2026-03-11 08:07:05 +01:00
parent af3391d05a
commit 75c6a6ce58
3 changed files with 117 additions and 6 deletions

View file

@ -0,0 +1,111 @@
import { describe, it, expect } from "vitest";
import { marked } from "marked";
/** Tests for marked list rendering — covering v17 breaking changes */
describe("Markdown list rendering", () => {
const parse = (md: string) => marked.parse(md, { async: false }) as string;
describe("loose lists (paragraphs inside list items)", () => {
it("renders loose list items with <p> tags", () => {
const md = `- Item one\n\n- Item two\n\n- Item three\n`;
const html = parse(md);
expect(html).toContain("<ul>");
expect(html).toContain("<li>");
// Loose lists wrap content in <p>
expect(html).toContain("<p>Item one</p>");
expect(html).toContain("<p>Item two</p>");
expect(html).toContain("<p>Item three</p>");
});
it("renders tight list items without <p> tags", () => {
const md = `- Item one\n- Item two\n- Item three\n`;
const html = parse(md);
expect(html).toContain("<ul>");
expect(html).not.toContain("<p>");
expect(html).toContain("Item one");
});
});
describe("checkbox/task lists", () => {
it("renders unchecked checkboxes", () => {
const md = `- [ ] Todo item\n`;
const html = parse(md);
expect(html).toContain('<input');
expect(html).toContain('type="checkbox"');
expect(html).not.toContain("checked");
expect(html).toContain("Todo item");
});
it("renders checked checkboxes", () => {
const md = `- [x] Done item\n`;
const html = parse(md);
expect(html).toContain('checked');
expect(html).toContain("Done item");
});
it("renders mixed task list", () => {
const md = `- [x] Done\n- [ ] Pending\n- Regular item\n`;
const html = parse(md);
expect(html).toContain("Done");
expect(html).toContain("Pending");
expect(html).toContain("Regular item");
// Should have exactly 2 checkboxes
const checkboxCount = (html.match(/type="checkbox"/g) || []).length;
expect(checkboxCount).toBe(2);
});
});
describe("nested lists", () => {
it("renders nested unordered lists", () => {
const md = `- Parent\n - Child\n - Grandchild\n`;
const html = parse(md);
expect(html).toContain("Parent");
expect(html).toContain("Child");
expect(html).toContain("Grandchild");
// Should have nested <ul> elements
const ulCount = (html.match(/<ul>/g) || []).length;
expect(ulCount).toBeGreaterThanOrEqual(2);
});
it("renders nested ordered lists", () => {
const md = `1. First\n 1. Sub-first\n 2. Sub-second\n2. Second\n`;
const html = parse(md);
expect(html).toContain("<ol>");
expect(html).toContain("First");
expect(html).toContain("Sub-first");
expect(html).toContain("Second");
});
});
describe("mixed list content", () => {
it("renders code blocks inside list items", () => {
const md = [
"- Item with code:",
"",
" ```js",
" console.log(\"hi\");",
" ```",
"",
"- Normal item",
"",
].join("\n");
const html = parse(md);
expect(html).toContain("console.log(&quot;hi&quot;);");
expect(html).toContain("Normal item");
});
it("renders inline code in list items", () => {
const md = `- Use \`npm install\`\n- Run \`npm test\`\n`;
const html = parse(md);
expect(html).toContain("<code>npm install</code>");
expect(html).toContain("<code>npm test</code>");
});
it("renders bold and italic in list items", () => {
const md = `- **Bold item**\n- *Italic item*\n`;
const html = parse(md);
expect(html).toContain("<strong>Bold item</strong>");
expect(html).toContain("<em>Italic item</em>");
});
});
});