Files
hyperframes/packages/studio/src/components/editor/compositionReliabilityFixture.integration.test.ts
T
Miguel Ángel 2b65b4efce fix(studio): harden composition timeline reliability (#2615)
* fix(studio): preserve composition playback continuity

* feat(studio): drag compositions into the timeline

* fix(studio): collapse expanded composition move aliases

* fix(studio): make timeline cuts atomic

* fix(studio): group inspector gesture history

* test(studio): cover masked text selection

* fix(studio): harden composition timeline reliability

* fix(studio): satisfy CI source gates

* fix(studio): harden composition mutation requests
2026-07-17 14:15:30 -04:00

106 lines
4.4 KiB
TypeScript

// @vitest-environment jsdom
import { existsSync, readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { patchElementInHtml } from "@hyperframes/studio-server/source-mutation";
import { describe, expect, it } from "vitest";
import { buildDomEditStylePatchOperation } from "./domEditing";
const fixtureDir = join(
dirname(fileURLToPath(import.meta.url)),
"../../../tests/e2e/fixtures/composition-reliability",
);
function fixture(path: string): string {
return readFileSync(join(fixtureDir, path), "utf8");
}
function parse(html: string): Document {
return new DOMParser().parseFromString(html, "text/html");
}
function inTemplate(document: Document, selector: string): Element | null {
for (const template of Array.from(document.querySelectorAll("template"))) {
const match = template.content.querySelector(selector);
if (match) return match;
}
return null;
}
describe("composition reliability acceptance fixture", () => {
const indexSource = fixture("index.html");
const titleSource = fixture("compositions/title-card.html");
const nestedSource = fixture("compositions/nested-shell.html");
it("owns repeated root hosts, a nested host, transparent headline topology, and collisions", () => {
const index = parse(indexSource);
const repeated = Array.from(
index.querySelectorAll('[data-composition-src="compositions/title-card.html"]'),
);
expect(repeated).toHaveLength(2);
expect(repeated.map((host) => host.getAttribute("data-start"))).toEqual(["0", "4"]);
expect(
index.querySelector('[data-composition-src="compositions/nested-shell.html"]'),
).toBeTruthy();
const nested = parse(nestedSource);
const nestedHost = inTemplate(nested, '[data-composition-src="title-card.html"]');
expect(nestedHost).toBeTruthy();
const nestedDependency = nestedHost?.getAttribute("data-composition-src");
expect(
nestedDependency ? existsSync(resolve(fixtureDir, "compositions", nestedDependency)) : false,
).toBe(true);
const title = parse(titleSource);
const mask = inTemplate(title, ".hl-mask");
const headline = inTemplate(title, ".hl-mask > .hl-text");
expect(mask?.textContent).toContain("Reliable compositions");
expect(inTemplate(title, "style")?.textContent).toMatch(
/\.hl-mask\s*\{[^}]*overflow:\s*hidden;[^}]*background:\s*transparent;/,
);
expect(headline?.tagName).toBe("H1");
const collisionA = index.querySelector('[data-hf-id="collision-a"]')!;
const collisionB = index.querySelector('[data-hf-id="collision-b"]')!;
const layered = index.querySelector('[data-hf-id="layer-overlap"]')!;
expect(collisionA.getAttribute("data-track-index")).toBe(
collisionB.getAttribute("data-track-index"),
);
expect(
Number(collisionA.getAttribute("data-start")) +
Number(collisionA.getAttribute("data-duration")),
).toBe(Number(collisionB.getAttribute("data-start")));
expect(layered.getAttribute("data-start")).toBe(collisionB.getAttribute("data-start"));
expect(layered.getAttribute("data-track-index")).not.toBe(
collisionB.getAttribute("data-track-index"),
);
});
it("keeps timeline host edits in the root source and headline color in the template source", () => {
const moved = patchElementInHtml(indexSource, { hfId: "title-host-a" }, [
{ type: "attribute", property: "start", value: "5" },
{ type: "attribute", property: "duration", value: "2" },
]);
expect(moved.matched).toBe(true);
expect(moved.html).toContain('data-hf-id="title-host-a"');
expect(moved.html).toContain('data-start="5"');
expect(moved.html).toContain('data-duration="2"');
expect(moved.html).not.toContain('data-hf-id="title-text"');
expect(titleSource).not.toContain("#12b886");
const recolored = patchElementInHtml(titleSource, { hfId: "title-text" }, [
buildDomEditStylePatchOperation("color", "#12b886"),
]);
expect(recolored.matched).toBe(true);
const recoloredDocument = parse(recolored.html);
expect(
inTemplate(recoloredDocument, '[data-hf-id="title-text"]')?.getAttribute("style"),
).toContain("color: #12b886");
expect(
inTemplate(recoloredDocument, '[data-hf-id="title-mask"]')?.getAttribute("style"),
).toBeNull();
expect(recolored.html).not.toContain('data-hf-id="title-host-a"');
expect(indexSource).not.toContain("#12b886");
});
});