import { describe, expect, it } from "vitest"; import { applyPatchByTarget, applyPatch } from "./sourcePatcher"; /** * Reproduction tests for https://github.com/heygen-com/hyperframes/issues/958 * * The bug: dragging a clip in the Studio timeline rewrites index.html with * inline style="z-index: N" on EVERY clip, overriding the author's CSS z-index. * Additionally, void elements (img, audio) get malformed self-closing tags. */ const ISSUE_HTML = `
TITLE
`; const VOID_ELEMENT_HTML = `
rotating earth gif
TITLE
`; describe("issue #958 — timeline drag must not inject inline z-index", () => { it("reproduces the old bug: z-index injection on all clips overrides CSS layering", () => { // Simulate the OLD behavior: buildTrackZIndexMap + loop over all clips function buildTrackZIndexMap(tracks: number[]): Map { const uniqueTracks = Array.from(new Set(tracks)).sort((a, b) => a - b); const maxZIndex = uniqueTracks.length; return new Map(uniqueTracks.map((track, index) => [track, maxZIndex - index])); } const elements = [ { id: "bg-video", track: 0 }, { id: "title", track: 1 }, ]; const trackZIndices = buildTrackZIndexMap(elements.map((e) => e.track)); // Apply the old z-index injection loop let broken = ISSUE_HTML; for (const el of elements) { const nextZIndex = trackZIndices.get(el.track); if (nextZIndex == null) continue; broken = applyPatch(broken, el.id, { type: "inline-style", property: "z-index", value: String(nextZIndex), }); } // Verify the bug: bg-video gets z-index: 2, title gets z-index: 1 // This INVERTS the intended layering (CSS had bg-video: 0, title: 20) expect(broken).toContain('id="bg-video"'); expect(broken).toContain('style="z-index: 2"'); expect(broken).toContain('id="title"'); expect(broken).toContain('style="z-index: 1"'); // The title (z-index: 1) is now BEHIND the video (z-index: 2) // — the opposite of the author's intent (CSS z-index: 20 vs 0) }); it("verifies the fix: moving a clip only patches data-start and data-track-index", () => { // Simulate the NEW behavior: only patch the moved clip's timing attributes const movedElement = { id: "bg-video" }; const updates = { start: "2.5", track: "0" }; let fixed = applyPatchByTarget( ISSUE_HTML, { id: movedElement.id }, { type: "attribute", property: "start", value: updates.start }, ); fixed = applyPatchByTarget( fixed, { id: movedElement.id }, { type: "attribute", property: "track-index", value: updates.track }, ); // The moved clip's timing changed expect(fixed).toContain('id="bg-video" data-start="2.5" data-track-index="0"'); // No inline z-index was injected on ANY element expect(fixed).not.toContain('style="z-index'); // The title clip is completely untouched expect(fixed).toContain( '
TITLE
', ); // CSS z-index declarations in