import { test, describe, expect } from "vitest";
import {
render_inline_markdown,
escape_html,
INLINE_CODE_RE,
LINK_RE,
BOLD_ASTERISK_RE,
BOLD_UNDERSCORE_RE,
ITALIC_ASTERISK_RE,
ITALIC_UNDERSCORE_RE,
PROTOCOL_RE
} from "./inline-markdown";
describe("escape_html", () => {
test("escapes ampersands", () => {
expect(escape_html("a & b")).toBe("a & b");
});
test("escapes angle brackets", () => {
expect(escape_html("")).toBe(
"<script>alert(1)</script>"
);
});
test("escapes HTML inside markdown syntax", () => {
expect(render_inline_markdown("**bold**")).toBe(
"<b>bold</b>"
);
});
});
describe("safe links", () => {
test("renders http links", () => {
expect(render_inline_markdown("[click](http://example.com)")).toBe(
'click'
);
});
test("renders https links", () => {
expect(render_inline_markdown("[click](https://example.com)")).toBe(
'click'
);
});
test("renders bare domain links (no protocol)", () => {
expect(render_inline_markdown("[Google](google.com)")).toBe(
'Google'
);
});
test("renders www links (no protocol)", () => {
expect(render_inline_markdown("[Google](www.google.com)")).toBe(
'Google'
);
});
test("renders relative path links", () => {
expect(render_inline_markdown("[docs](/path/to/docs)")).toBe(
'docs'
);
});
test("trims whitespace from URLs", () => {
expect(render_inline_markdown("[click]( https://example.com )")).toBe(
'click'
);
});
});
describe("XSS prevention", () => {
test("blocks javascript: protocol links", () => {
const result = render_inline_markdown(
"[xss](javascript:alert(document.cookie))"
);
// No tag should be produced — the XSS vector is neutralised.
// Trailing ")" is left over because the link regex stops at the first ")".
expect(result).not.toContain(" {
expect(render_inline_markdown("[xss](javascript:void)")).toBe("xss");
});
test("blocks javascript: with leading spaces", () => {
const result = render_inline_markdown("[xss]( javascript:alert(1) )");
expect(result).not.toContain(" {
expect(render_inline_markdown("[xss](vbscript:msgbox)")).toBe("xss");
});
test("blocks data: protocol links", () => {
expect(
render_inline_markdown(
"[xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)"
)
).toBe("xss");
});
test("blocks data: with nested HTML", () => {
const result = render_inline_markdown(
"[xss](data:text/html,)"
);
expect(result).not.toContain(" {
const input = "[bad](javascript:void) hello [good](https://example.com)";
const result = render_inline_markdown(input);
expect(result).toContain("bad");
expect(result).not.toContain("javascript:");
expect(result).toContain(
'good'
);
});
});
describe("combined syntax", () => {
test("renders bold inside a sentence", () => {
expect(render_inline_markdown("This is **important** info")).toBe(
"This is important info"
);
});
test("renders code and bold together", () => {
expect(render_inline_markdown("Use `func()` for **speed**")).toBe(
"Use func() for speed"
);
});
test("renders link with surrounding text", () => {
expect(
render_inline_markdown("See [docs](https://docs.io) for more")
).toBe(
'See docs for more'
);
});
test("empty string returns empty string", () => {
expect(render_inline_markdown("")).toBe("");
});
test("plain text passes through unchanged", () => {
expect(render_inline_markdown("hello world")).toBe("hello world");
});
});
});