import { describe, expect, it } from "vitest"; import { sanitizeHtml, stripInvisibleUnicode } from "./web-fetch-visibility.js"; describe("sanitizeHtml", () => { it("strips display:none elements", async () => { const html = '

Visible

Hidden

'; const result = await sanitizeHtml(html); expect(result).toContain("Visible"); expect(result).not.toContain("Hidden"); }); it("strips visibility:hidden elements", async () => { const html = '

Visible

Secret'; const result = await sanitizeHtml(html); expect(result).not.toContain("Secret"); }); it("strips opacity:0 elements", async () => { const html = '

Show

Invisible
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Invisible"); }); it("strips font-size:0 elements", async () => { const html = '

Normal

Tiny'; const result = await sanitizeHtml(html); expect(result).not.toContain("Tiny"); }); it("strips text-indent far-offscreen elements", async () => { const html = '

Normal

Offscreen

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Offscreen"); }); it("strips color:transparent elements", async () => { const html = '

Visible

Ghost

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Ghost"); }); it("strips color:rgba with zero alpha elements", async () => { const html = '

Visible

Invisible

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Invisible"); }); it("strips color:rgba with zero decimal alpha elements", async () => { const html = '

Visible

Invisible

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Invisible"); }); it("strips color:hsla with zero alpha elements", async () => { const html = '

Visible

Invisible

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Invisible"); }); it("strips transform:scale(0) elements", async () => { const html = '

Show

Scaled
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Scaled"); }); it("strips transform:translateX far-offscreen elements", async () => { const html = '

Show

Translated
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Translated"); }); it("strips width:0 height:0 overflow:hidden elements", async () => { const html = '

Show

Zero
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Zero"); }); it("strips left far-offscreen positioned elements", async () => { const html = '

Show

Offscreen
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Offscreen"); }); it("strips clip-path:inset(100%) elements", async () => { const html = '

Show

Clipped
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Clipped"); }); it("strips clip-path:inset(50%) elements", async () => { const html = '

Show

Clipped
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Clipped"); }); it("does not strip clip-path:inset(0%) elements", async () => { const html = '

Show

Visible
'; const result = await sanitizeHtml(html); expect(result).toContain("Visible"); }); it("strips sr-only class elements", async () => { const html = '

Main

Screen reader only'; const result = await sanitizeHtml(html); expect(result).not.toContain("Screen reader only"); }); it("strips visually-hidden class elements", async () => { const html = '

Main

Hidden visually'; const result = await sanitizeHtml(html); expect(result).not.toContain("Hidden visually"); }); it("strips d-none class elements", async () => { const html = '

Main

Bootstrap hidden
'; const result = await sanitizeHtml(html); expect(result).not.toContain("Bootstrap hidden"); }); it("strips hidden class elements", async () => { const html = '

Main

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Class hidden"); }); it("does not strip elements with hidden as substring of class name", async () => { const html = '

Main

Should be visible
'; const result = await sanitizeHtml(html); expect(result).toContain("Should be visible"); }); it("strips aria-hidden=true elements", async () => { const html = '

Visible

'; const result = await sanitizeHtml(html); expect(result).not.toContain("Aria hidden"); }); it("strips elements with hidden attribute", async () => { const html = "

Visible

"; const result = await sanitizeHtml(html); expect(result).not.toContain("HTML hidden"); }); it("strips input type=hidden", async () => { const html = '
'; const result = await sanitizeHtml(html); expect(result).not.toContain("csrf-token-secret"); }); it("strips HTML comments", async () => { const html = "

Visible

"; const result = await sanitizeHtml(html); expect(result).not.toContain("inject"); expect(result).not.toContain("ignore previous instructions"); }); it("strips meta tags", async () => { const html = '

Body

'; const result = await sanitizeHtml(html); expect(result).not.toContain("prompt payload"); }); it("strips template tags", async () => { const html = "

Visible

"; const result = await sanitizeHtml(html); expect(result).not.toContain("Hidden template content"); }); it("strips iframe tags", async () => { const html = "

Visible

"; const result = await sanitizeHtml(html); expect(result).not.toContain("Iframe content"); }); it("preserves visible content", async () => { const html = "

Hello world

Title

Link"; const result = await sanitizeHtml(html); expect(result).toContain("Hello world"); expect(result).toContain("Title"); }); it("handles nested hidden elements without removing visible siblings", async () => { const html = '

Visible

Hidden

Also visible

'; const result = await sanitizeHtml(html); expect(result).toContain("Visible"); expect(result).toContain("Also visible"); expect(result).not.toContain("Hidden"); }); it("handles malformed HTML gracefully", async () => { const html = "

Unclosed

Nested"; await expect(sanitizeHtml(html)).resolves.toBeDefined(); }); }); describe("stripInvisibleUnicode", () => { it("strips zero-width space", () => { const text = "Hello\u200BWorld"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("strips zero-width non-joiner", () => { const text = "Hello\u200CWorld"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("strips zero-width joiner", () => { const text = "Hello\u200DWorld"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("strips left-to-right mark", () => { const text = "Hello\u200EWorld"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("strips right-to-left mark", () => { const text = "Hello\u200FWorld"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("strips directional overrides (LRO, RLO, PDF, etc.)", () => { const text = "\u202AHello\u202E"; expect(stripInvisibleUnicode(text)).toBe("Hello"); }); it("strips word joiner and other formatting chars", () => { const text = "Hello\u2060World\uFEFF"; expect(stripInvisibleUnicode(text)).toBe("HelloWorld"); }); it("preserves normal text unchanged", () => { const text = "Hello, World! 123 \u00e9\u4e2d\u6587"; expect(stripInvisibleUnicode(text)).toBe(text); }); it("strips multiple invisible chars in a row", () => { const text = "A\u200B\u200C\u200D\u200E\u200FB"; expect(stripInvisibleUnicode(text)).toBe("AB"); }); it("handles empty string", () => { expect(stripInvisibleUnicode("")).toBe(""); }); });