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 = `
`;
const VOID_ELEMENT_HTML = `
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