import { expect, test } from "bun:test";
import { renderHintsHtml, renderLessonHtml, sanitizeHtml } from "../src/web/markdown";
const opts = { courseId: "c", baseFile: "course/l.md", tracks: [], lessonFiles: {} };
const decode = (s: string) => s.replace(/([0-9a-f]+);?/gi, (_, h) => String.fromCodePoint(parseInt(h, 16)))
.replace(/(\d+);?/g, (_, d) => String.fromCodePoint(Number(d))).replace(/:/gi, ":").replace(/&tab;/gi, "\t").replace(/&newline;/gi, "\n").replace(/&/g, "&");
/** Fails when real tags in `html` carry event handlers, style, a dangerous URL, or are script-capable elements. */
function expectSafe(html: string) {
const tags = html.match(/<[a-zA-Z][^>]*>/g) ?? [];
for (const t of tags) {
expect(t).not.toMatch(/\son[a-z]+\s*=/i);
expect(t).not.toMatch(/\sstyle\s*=/i);
expect(t).not.toMatch(/^<(script|style|svg|math|iframe|object|embed|form|input|button|textarea|select|link|meta|base|frame|frameset)\b/i);
for (const m of t.matchAll(/\s(?:href|src|action|formaction|xlink:href)\s*=\s*"([^"]*)"/gi)) {
const url = decode(m[1]!).replace(/[\u0000- \u007f-\u009f]/g, "").toLowerCase();
expect(url).not.toMatch(/^(javascript|vbscript|data|file):/);
}
}
// An unterminated tag must never swallow following markup into its attributes.
expect(html).not.toMatch(/]*
or