Skip to content

Commit 63ff0c1

Browse files
authored
test(studio): add T5b rotation+motion build-patches characterization (heygen-com#1258)
Extends manualEditsDomPatches.test.ts with rotation and motion pairs. Same 4-pattern structure: populated, empty, clear restores originals, build/clear symmetry. Merges duplicate manualEditsTypes import block.
1 parent 19f46cd commit 63ff0c1

1 file changed

Lines changed: 130 additions & 0 deletions

File tree

packages/studio/src/components/editor/manualEditsDomPatches.test.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import {
77
STUDIO_OFFSET_Y_PROP,
88
STUDIO_WIDTH_PROP,
99
STUDIO_HEIGHT_PROP,
10+
STUDIO_ROTATION_PROP,
1011
STUDIO_PATH_OFFSET_ATTR,
1112
STUDIO_BOX_SIZE_ATTR,
13+
STUDIO_ROTATION_ATTR,
14+
STUDIO_ROTATION_DRAFT_ATTR,
1215
STUDIO_ORIGINAL_TRANSLATE_ATTR,
1316
STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR,
1417
STUDIO_ORIGINAL_WIDTH_ATTR,
@@ -24,13 +27,26 @@ import {
2427
STUDIO_ORIGINAL_SCALE_ATTR,
2528
STUDIO_ORIGINAL_TRANSFORM_ORIGIN_ATTR,
2629
STUDIO_ORIGINAL_DISPLAY_ATTR,
30+
STUDIO_ORIGINAL_ROTATE_ATTR,
31+
STUDIO_ORIGINAL_INLINE_ROTATE_ATTR,
32+
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR,
2733
STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR,
2834
} from "./manualEditsTypes";
35+
import {
36+
STUDIO_MOTION_ATTR,
37+
STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR,
38+
STUDIO_MOTION_ORIGINAL_OPACITY_ATTR,
39+
STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR,
40+
} from "./studioMotionTypes";
2941
import {
3042
buildPathOffsetPatches,
3143
buildClearPathOffsetPatches,
3244
buildBoxSizePatches,
3345
buildClearBoxSizePatches,
46+
buildRotationPatches,
47+
buildClearRotationPatches,
48+
buildMotionPatches,
49+
buildClearMotionPatches,
3450
} from "./manualEditsDomPatches";
3551

3652
/* ── helpers ── */
@@ -210,3 +226,117 @@ describe("buildBoxSizePatches / buildClearBoxSizePatches", () => {
210226
assertClearCoversKeys(buildBoxSizePatches(e), buildClearBoxSizePatches(e));
211227
});
212228
});
229+
230+
/* ── Rotation ────────────────────────────────────────────────────────────── */
231+
232+
describe("buildRotationPatches / buildClearRotationPatches", () => {
233+
function populatedRotEl(): HTMLElement {
234+
const e = div();
235+
e.style.setProperty(STUDIO_ROTATION_PROP, "45");
236+
e.style.setProperty("rotate", "45deg");
237+
e.style.setProperty("transform-origin", "left center");
238+
e.style.setProperty("display", "block");
239+
e.setAttribute(STUDIO_ORIGINAL_ROTATE_ATTR, "0deg");
240+
e.setAttribute(STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, "0deg");
241+
e.setAttribute(STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, "center center");
242+
e.setAttribute(STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, "flex");
243+
return e;
244+
}
245+
246+
it("populated: captures rotation styles, attrs, and transform-display marker in declaration order", () => {
247+
const ops = buildRotationPatches(populatedRotEl());
248+
expect(ops).toEqual([
249+
{ type: "inline-style", property: STUDIO_ROTATION_PROP, value: "45" },
250+
{ type: "inline-style", property: "rotate", value: "45deg" },
251+
{ type: "inline-style", property: "transform-origin", value: "left center" },
252+
{ type: "inline-style", property: "display", value: "block" },
253+
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: "true" },
254+
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATE_ATTR, value: "0deg" },
255+
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, value: "0deg" },
256+
{
257+
type: "attribute",
258+
property: STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR,
259+
value: "center center",
260+
},
261+
{ type: "attribute", property: STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, value: "flex" },
262+
]);
263+
});
264+
265+
it("empty: bare element yields only the rotation marker", () => {
266+
expect(buildRotationPatches(div())).toEqual([
267+
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: "true" },
268+
]);
269+
});
270+
271+
it("clear: restores rotate and transform-origin from orig attrs, nulls draft attr", () => {
272+
const e = div();
273+
e.setAttribute(STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, "30deg");
274+
e.setAttribute(STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, "top left");
275+
e.setAttribute(STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, "grid");
276+
const ops = buildClearRotationPatches(e);
277+
expect(ops).toEqual([
278+
{ type: "inline-style", property: STUDIO_ROTATION_PROP, value: null },
279+
{ type: "inline-style", property: "rotate", value: "30deg" },
280+
{ type: "inline-style", property: "transform-origin", value: "top left" },
281+
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: null },
282+
{ type: "attribute", property: STUDIO_ROTATION_DRAFT_ATTR, value: null },
283+
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATE_ATTR, value: null },
284+
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, value: null },
285+
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, value: null },
286+
{ type: "inline-style", property: "display", value: "grid" },
287+
{ type: "attribute", property: STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, value: null },
288+
]);
289+
});
290+
291+
it("build/clear symmetry: clear addresses every {type,property} key that build emits", () => {
292+
const e = populatedRotEl();
293+
assertClearCoversKeys(buildRotationPatches(e), buildClearRotationPatches(e));
294+
});
295+
});
296+
297+
/* ── Motion ──────────────────────────────────────────────────────────────── */
298+
299+
describe("buildMotionPatches / buildClearMotionPatches", () => {
300+
const MOTION_JSON = '{"kind":"gsap-motion","start":0,"duration":1}';
301+
302+
function populatedMotionEl(): HTMLElement {
303+
const e = div();
304+
e.setAttribute(STUDIO_MOTION_ATTR, MOTION_JSON);
305+
e.setAttribute(STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR, "translateX(0)");
306+
e.setAttribute(STUDIO_MOTION_ORIGINAL_OPACITY_ATTR, "1");
307+
e.setAttribute(STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR, "visible");
308+
return e;
309+
}
310+
311+
it("populated: captures motion JSON and all three original attrs when motion attr is present", () => {
312+
const ops = buildMotionPatches(populatedMotionEl());
313+
expect(ops).toEqual([
314+
{ type: "attribute", property: STUDIO_MOTION_ATTR, value: MOTION_JSON },
315+
{
316+
type: "attribute",
317+
property: STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR,
318+
value: "translateX(0)",
319+
},
320+
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_OPACITY_ATTR, value: "1" },
321+
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR, value: "visible" },
322+
]);
323+
});
324+
325+
it("empty: returns [] when STUDIO_MOTION_ATTR is absent", () => {
326+
expect(buildMotionPatches(div())).toEqual([]);
327+
});
328+
329+
it("clear: always nulls all four motion attrs regardless of element state", () => {
330+
expect(buildClearMotionPatches(div())).toEqual([
331+
{ type: "attribute", property: STUDIO_MOTION_ATTR, value: null },
332+
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_TRANSFORM_ATTR, value: null },
333+
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_OPACITY_ATTR, value: null },
334+
{ type: "attribute", property: STUDIO_MOTION_ORIGINAL_VISIBILITY_ATTR, value: null },
335+
]);
336+
});
337+
338+
it("build/clear symmetry: clear addresses every {type,property} key that build emits", () => {
339+
const e = populatedMotionEl();
340+
assertClearCoversKeys(buildMotionPatches(e), buildClearMotionPatches(e));
341+
});
342+
});

0 commit comments

Comments
 (0)