import { describe, it, expect } from "vitest"; import { lintHyperframeHtml } from "../hyperframeLinter.js"; describe("media rules", () => { it("reports error for duplicate media ids", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "duplicate_media_id"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.elementId).toBe("v1"); }); it("reports error for audio with data-start but no id", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_missing_id"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.message).toContain("SILENT"); }); it("reports error for video with data-start but no id", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_missing_id"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.message).toContain("FROZEN"); }); it("flags media that has data-hf-id but no real id", async () => { // Regression: readAttr(tag, "id") used a \b boundary that matched the // trailing `id="…"` inside `data-hf-id="…"`, so media carrying only a // Studio-stamped data-hf-id passed the check and then rendered as a blank // wash (video) / silent (audio). data-hf-id is NOT a render id. const html = `
`; const result = await lintHyperframeHtml(html); const findings = result.findings.filter((f) => f.code === "media_missing_id"); expect(findings).toHaveLength(2); expect(findings.every((f) => f.severity === "error")).toBe(true); }); it("does not flag media elements that have id", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_missing_id"); expect(finding).toBeUndefined(); }); it("reports warning for media with preload=none", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_preload_none"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("warning"); }); it("reports error for media with id but no src", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_missing_src"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); }); it("reports error for media with src but no data-start", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "media_missing_data_start"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.elementId).toBe("demo-video"); }); it("allows audible video clips to omit muted when data-has-audio is true", async () => { const html = `
`; const result = await lintHyperframeHtml(html); expect(result.findings.find((f) => f.code === "video_missing_muted")).toBeUndefined(); expect( result.findings.find((f) => f.code === "video_muted_with_declared_audio"), ).toBeUndefined(); }); it("reports error for videos that declare audio while muted", async () => { const html = `
`; const result = await lintHyperframeHtml(html); const finding = result.findings.find((f) => f.code === "video_muted_with_declared_audio"); expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.elementId).toBe("demo-video"); }); it("does NOT flag