// @vitest-environment jsdom
import { describe, expect, it } from "vitest";
import { patchDocumentRootDuration } from "./timelineEditingHelpers";
// The regression these guard against: timing edits now soft-reload instead of
// full-reloading the iframe, so the runtime recomputes the composition length
// from the ROOT's `data-duration` and posts it back — overwriting the studio's
// optimistic duration readout. Patching the live root's `data-duration` in the
// same tick keeps the runtime's post-soft-reload report in agreement, so the
// readout stays live on grow AND shrink instead of snapping back.
function parse(html: string): Document {
return new DOMParser().parseFromString(html, "text/html");
}
describe("patchDocumentRootDuration", () => {
it("writes the content end onto the TOP-LEVEL composition root (grow)", () => {
const doc = parse(`
`);
expect(patchDocumentRootDuration(doc, 20)).toBe(true);
const root = doc.querySelector('[data-composition-id="root"]');
expect(root?.getAttribute("data-duration")).toBe("20");
// Nested sub-composition is untouched — only the outermost root is the length.
expect(doc.querySelector('[data-composition-id="scene"]')?.getAttribute("data-duration")).toBe(
"8",
);
});
it("shrinks the root duration too (not grow-only)", () => {
const doc = parse(``);
expect(patchDocumentRootDuration(doc, 6.5)).toBe(true);
expect(doc.querySelector("[data-composition-id]")?.getAttribute("data-duration")).toBe("6.5");
});
it("picks the composition with no ancestor composition as the root", () => {
// Root appears AFTER a nested one in document order — selection must be by
// ancestry, not first-match, matching the runtime's own root resolver.
const doc = parse(`
`);
patchDocumentRootDuration(doc, 12);
expect(doc.querySelector('[data-composition-id="root"]')?.getAttribute("data-duration")).toBe(
"12",
);
expect(doc.querySelector('[data-composition-id="inner"]')?.getAttribute("data-duration")).toBe(
"4",
);
});
it("no-ops on a non-positive or non-finite content end (never collapses to 0)", () => {
const doc = parse(``);
expect(patchDocumentRootDuration(doc, 0)).toBe(false);
expect(patchDocumentRootDuration(doc, -3)).toBe(false);
expect(patchDocumentRootDuration(doc, Number.NaN)).toBe(false);
expect(doc.querySelector("[data-composition-id]")?.getAttribute("data-duration")).toBe("15.18");
});
it("no-ops when there is no composition root and reports false", () => {
const doc = parse(``);
expect(patchDocumentRootDuration(doc, 10)).toBe(false);
});
it("returns false for a null document", () => {
expect(patchDocumentRootDuration(null, 10)).toBe(false);
});
});