diff --git a/packages/core/src/lint/hyperframeLinter.test.ts b/packages/core/src/lint/hyperframeLinter.test.ts index 1e4b6b61..201c455b 100644 --- a/packages/core/src/lint/hyperframeLinter.test.ts +++ b/packages/core/src/lint/hyperframeLinter.test.ts @@ -1,5 +1,5 @@ -import { describe, it, expect, vi } from "vitest"; -import { lintHyperframeHtml, lintScriptUrls } from "./hyperframeLinter.js"; +import { describe, it, expect } from "vitest"; +import { lintHyperframeHtml } from "./hyperframeLinter.js"; describe("lintHyperframeHtml", () => { const validComposition = ` @@ -111,281 +111,23 @@ describe("lintHyperframeHtml", () => { expect(codes.length).toBe(uniqueCodes.length); }); - it("detects timeline ID mismatch", () => { - const html = ` -
-
-
- -`; - const result = lintHyperframeHtml(html); - const mismatch = result.findings.find((f) => f.code === "timeline_id_mismatch"); - expect(mismatch).toBeDefined(); - expect(mismatch?.message).toContain("intro-anim"); - }); - - it("does not flag matching timeline IDs", () => { - const result = lintHyperframeHtml(validComposition); - const mismatch = result.findings.find((f) => f.code === "timeline_id_mismatch"); - expect(mismatch).toBeUndefined(); - }); - - it("reports error when timeline assignment has no init guard", () => { - const html = ` -
- -`; - const result = lintHyperframeHtml(html); - const finding = result.findings.find((f) => f.code === "timeline_registry_missing_init"); - expect(finding).toBeDefined(); - expect(finding?.severity).toBe("error"); - expect(finding?.message).toContain("without initializing"); - }); - - it("does not flag timeline assignment when init guard is present", () => { - const result = lintHyperframeHtml(validComposition); - const finding = result.findings.find((f) => f.code === "timeline_registry_missing_init"); - expect(finding).toBeUndefined(); - }); -}); - -describe("lintScriptUrls", () => { - it("reports error for script URL returning non-2xx", async () => { - const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 404 }); - vi.stubGlobal("fetch", mockFetch); - - const html = ` -
- -`; - const findings = await lintScriptUrls(html); - const finding = findings.find((f) => f.code === "inaccessible_script_url"); - expect(finding).toBeDefined(); - expect(finding?.severity).toBe("error"); - expect(finding?.message).toContain("404"); - - vi.unstubAllGlobals(); - }); - - it("reports error for unreachable script URL", async () => { - const mockFetch = vi.fn().mockRejectedValue(new Error("AbortError")); - vi.stubGlobal("fetch", mockFetch); - - const html = ` -
- -`; - const findings = await lintScriptUrls(html); - const finding = findings.find((f) => f.code === "inaccessible_script_url"); - expect(finding).toBeDefined(); - - vi.unstubAllGlobals(); - }); - - it("does not flag accessible script URLs", async () => { - const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200 }); - vi.stubGlobal("fetch", mockFetch); - - const html = ` -
- -`; - const findings = await lintScriptUrls(html); - expect(findings.length).toBe(0); - - vi.unstubAllGlobals(); - }); - - it("skips inline scripts without src", async () => { - const mockFetch = vi.fn(); - vi.stubGlobal("fetch", mockFetch); - - const html = ` -
- -`; - const findings = await lintScriptUrls(html); - expect(findings.length).toBe(0); - expect(mockFetch).not.toHaveBeenCalled(); - - vi.unstubAllGlobals(); - }); - - // ── gsap_css_transform_conflict ────────────────────────────────────────── - - it("warns when tl.to animates x on an element with CSS translateX", () => { - const html = ` - -
-
-
- - -`; - const result = lintHyperframeHtml(html); - const finding = result.findings.find((f) => f.code === "gsap_css_transform_conflict"); - expect(finding).toBeDefined(); - expect(finding?.severity).toBe("warning"); - expect(finding?.selector).toBe("#title"); - expect(finding?.fixHint).toMatch(/fromTo/); - expect(finding?.fixHint).toMatch(/xPercent/); - }); - - it("warns when tl.to animates scale on an element with CSS scale transform", () => { - const html = ` - -
-
-
- - -`; - const result = lintHyperframeHtml(html); - const finding = result.findings.find((f) => f.code === "gsap_css_transform_conflict"); - expect(finding).toBeDefined(); - expect(finding?.severity).toBe("warning"); - expect(finding?.selector).toBe("#hero"); - }); - - it("does NOT warn when tl.to targets element without CSS transform", () => { - const html = ` - -
-
-
- - -`; - const result = lintHyperframeHtml(html); - const conflict = result.findings.find((f) => f.code === "gsap_css_transform_conflict"); - expect(conflict).toBeUndefined(); - }); - - it("does NOT warn when tl.fromTo targets element WITH CSS transform (author owns both ends)", () => { - const html = ` - -
-
-
- - -`; - const result = lintHyperframeHtml(html); - const conflict = result.findings.find((f) => f.code === "gsap_css_transform_conflict"); - expect(conflict).toBeUndefined(); - }); - - it("emits one warning when a combined CSS transform conflicts with multiple GSAP properties", () => { - const html = ` - -
-
-
- - -`; - const result = lintHyperframeHtml(html); - const conflicts = result.findings.filter((f) => f.code === "gsap_css_transform_conflict"); - expect(conflicts).toHaveLength(1); - expect(conflicts[0]?.message).toMatch(/x\/scale|scale\/x/); - }); -}); - -describe("template_literal_selector rule", () => { - it("reports error when querySelector uses template literal variable", () => { - const html = ` - -
-
-
- -`; - const result = lintHyperframeHtml(html); - const finding = result.findings.find((f) => f.code === "template_literal_selector"); - expect(finding).toBeDefined(); - expect(finding?.severity).toBe("error"); - }); - - it("reports error for querySelectorAll with template literal variable", () => { - const html = ` - -
- -`; - const result = lintHyperframeHtml(html); - const finding = result.findings.find((f) => f.code === "template_literal_selector"); - expect(finding).toBeDefined(); - }); - - it("does not report error for hardcoded querySelector strings", () => { - const html = ` - -
-
+ it("strips