import { describe, it, expect } from "vitest"; import MarkdownRenderer from "@/js/MarkdownRenderer"; describe("MarkdownRenderer.js", () => { describe("render", () => { it("renders basic text correctly", () => { expect(MarkdownRenderer.render("Hello")).toContain("Hello"); }); it("renders bold text correctly", () => { const result = MarkdownRenderer.render("**Bold**"); expect(result).toContain("Bold"); }); it("renders italic text correctly", () => { const result = MarkdownRenderer.render("*Italic*"); expect(result).toContain("Italic"); }); it("renders bold and italic text correctly", () => { const result = MarkdownRenderer.render("***Bold and Italic***"); expect(result).toContain("Bold and Italic"); }); it("renders headers correctly", () => { expect(MarkdownRenderer.render("# Header 1")).toContain(" { const result = MarkdownRenderer.render("`code`"); expect(result).toContain(" { const result = MarkdownRenderer.render("```python\nprint('hello')\n```"); expect(result).toContain(" { const result = MarkdownRenderer.render("Para 1\n\nPara 2"); expect(result).toContain(" { it("escapes script tags", () => { const malformed = ""; const result = MarkdownRenderer.render(malformed); expect(result).not.toContain("\n```"; const result = MarkdownRenderer.render(malformed); expect(result).toContain("<script>"); }); it("escapes html in inline code", () => { const malformed = "``"; const result = MarkdownRenderer.render(malformed); expect(result).toContain("<script>"); }); }); describe("nomadnet links", () => { it("detects nomadnet:// links with hash and path", () => { const text = "check this out: nomadnet://1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"; const result = MarkdownRenderer.render(text); expect(result).toContain('data-nomadnet-url="1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"'); expect(result).toContain("nomadnet://1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"); }); it("detects bare hash and path links", () => { const text = "node is at 1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"; const result = MarkdownRenderer.render(text); expect(result).toContain('data-nomadnet-url="1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"'); expect(result).toContain("1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"); }); it("detects nomadnet:// links with just hash", () => { const text = "nomadnet://1dfeb0d794963579bd21ac8f153c77a4"; const result = MarkdownRenderer.render(text); expect(result).toContain('data-nomadnet-url="1dfeb0d794963579bd21ac8f153c77a4:/page/index.mu"'); }); it("does not detect invalid hashes", () => { const text = "not-a-hash:/page/index.mu"; const result = MarkdownRenderer.render(text); expect(result).not.toContain("nomadnet-link"); }); }); describe("fuzzing: stability testing", () => { const generateRandomString = (length) => { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;':\",./<>?`~ \n\r\t"; let result = ""; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }; it("handles random inputs without crashing (100 iterations)", () => { for (let i = 0; i < 100; i++) { const randomText = generateRandomString(Math.floor(Math.random() * 1000)); expect(() => { MarkdownRenderer.render(randomText); }).not.toThrow(); } }); it("handles deeply nested or complex markdown patterns without crashing", () => { const complex = "# ".repeat(100) + "**".repeat(100) + "```".repeat(100) + "```\n".repeat(10); expect(() => { MarkdownRenderer.render(complex); }).not.toThrow(); }); it("handles large inputs correctly (1MB of random text)", () => { const largeText = generateRandomString(1024 * 1024); const start = Date.now(); const result = MarkdownRenderer.render(largeText); const end = Date.now(); expect(typeof result).toBe("string"); // performance check: should be relatively fast (less than 500ms for 1MB usually) expect(end - start).toBeLessThan(1000); }); it("handles potential ReDoS patterns (repeated separators)", () => { // Test patterns that often cause ReDoS in poorly written markdown parsers (can never be too careful, especially on public testnets) const redosPatterns = [ "*".repeat(10000), // Long string of bold markers "#".repeat(10000), // Long string of header markers "`".repeat(10000), // Long string of backticks " ".repeat(10000) + "\n", // Long string of whitespace "[](".repeat(5000), // Unclosed links (if we added them) "** ".repeat(5000), // Bold marker followed by space repeated ]; redosPatterns.forEach((pattern) => { const start = Date.now(); MarkdownRenderer.render(pattern); const end = Date.now(); expect(end - start).toBeLessThan(100); // Should be very fast }); }); it("handles unicode homoglyphs and special characters without interference", () => { const homoglyphs = [ "**bold**", "∗∗notbold∗∗", // unicode asterisks "# header", "# not header", // fullwidth hash "`code`", "`notcode`", // fullwidth backtick ]; homoglyphs.forEach((text) => { const result = MarkdownRenderer.render(text); expect(typeof result).toBe("string"); }); }); it("handles malformed or unclosed markdown tags gracefully", () => { const malformed = [ "**bold", "```python\nprint(1)", "#header", // no space "`code", "___triple", "**bold*italic**", "***bolditalic**", ]; malformed.forEach((text) => { expect(() => MarkdownRenderer.render(text)).not.toThrow(); }); }); }); describe("strip", () => { it("strips markdown correctly", () => { const md = "# Header\n**Bold** *Italic* `code` ```\nblock\n```"; const stripped = MarkdownRenderer.strip(md); expect(stripped).toContain("Header"); expect(stripped).toContain("Bold"); expect(stripped).toContain("Italic"); expect(stripped).toContain("code"); expect(stripped).toContain("[Code Block]"); expect(stripped).not.toContain("# "); expect(stripped).not.toContain("**"); expect(stripped).not.toContain("` "); }); }); });