/* Unit tests for htmlpbt — html.pbt covering its documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the html.pbt suite's contract for test-file scaffold coverage. */ import { test, expect } from "vitest"; import fc from "fast-check"; import { hashHtml } from "../normalizers/html.ts"; const safeText = fc.string({ minLength: 1, maxLength: 20 }).map((s) => s.replace(/[<>&"']/g, "x")); const safeAttrName = fc .string({ minLength: 1, maxLength: 10 }) .map((s) => s.replace(/[^a-zA-Z-]/g, "x")); test("PBT: hashHtml is deterministic — same input always produces same hash", () => { fc.assert( fc.property(safeText, (html) => { expect(hashHtml(html)).toBe(hashHtml(html)); }), ); }); test("PBT: hashHtml always returns sha256 format", () => { fc.assert( fc.property(safeText, (html) => { expect(hashHtml(html)).toMatch(/^sha256:[0-9a-f]{64}$/); }), ); }); test("PBT: whitespace-only differences between tags produce the same hash", () => { fc.assert( fc.property( fc.array( fc.record({ tag: fc.constantFrom("div", "span", "p", "h1", "h2", "ul", "li"), text: safeText, }), { minLength: 2, maxLength: 5 }, ), (elements) => { const compact = elements.map((e) => `<${e.tag}>${e.text}`).join(""); const spaced = elements.map((e) => `<${e.tag}>${e.text}`).join("\n \n "); expect(hashHtml(compact)).toBe(hashHtml(spaced)); }, ), ); }); test("PBT: data-* attributes (except data-testid) don't affect the hash", () => { fc.assert( fc.property( fc.record({ tag: fc.constantFrom("div", "span", "p"), attr: safeAttrName.map((s) => `data-${s}`), value: safeText, text: safeText, }), ({ tag, attr, value, text }) => { const withAttr = `<${tag} ${attr}="${value}">${text}`; const withoutAttr = `<${tag}>${text}`; expect(hashHtml(withAttr)).toBe(hashHtml(withoutAttr)); }, ), ); }); test("PBT: comments don't affect the hash", () => { fc.assert( fc.property( fc.record({ html: safeText, comment: safeText, }), ({ html, comment }) => { const withComment = `${html}`; expect(hashHtml(withComment)).toBe(hashHtml(html)); }, ), ); });