feat(studio): route GSAP keyframe add through SDK (§3.5 PR2) (#1470)

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-17 16:43:34 -07:00
committed by GitHub
co-authored by Miguel Ángel
parent 592f7c775d
commit 7ca4490328
6 changed files with 211 additions and 9 deletions
@@ -5,6 +5,7 @@ import {
sdkDeletePersist,
sdkTimingPersist,
sdkGsapTweenPersist,
sdkGsapKeyframePersist,
} from "./sdkCutover";
import { openComposition } from "@hyperframes/sdk";
import { createMemoryAdapter } from "@hyperframes/sdk/adapters/memory";
@@ -556,6 +557,71 @@ describe("sdkGsapTweenPersist", () => {
});
});
describe("sdkGsapKeyframePersist", () => {
const makeRef = <T>(val: T): MutableRefObject<T> => ({ current: val });
const makeDeps = () => ({
editHistory: { recordEdit: vi.fn().mockResolvedValue(undefined) },
writeProjectFile: vi.fn().mockResolvedValue(undefined),
reloadPreview: vi.fn(),
domEditSaveTimestampRef: makeRef(0),
});
const makeSession = () =>
({
dispatch: vi.fn(),
serialize: vi
.fn()
.mockReturnValueOnce("<html>before</html>")
.mockReturnValue("<html>after</html>"),
}) as unknown as Parameters<typeof sdkGsapKeyframePersist>[4];
it("returns false when session is null", async () => {
expect(
await sdkGsapKeyframePersist("/comp.html", "tw-1", 50, { opacity: 0.5 }, null, makeDeps()),
).toBe(false);
});
it("dispatches addGsapKeyframe and writes serialized content", async () => {
const deps = makeDeps();
const session = makeSession();
const result = await sdkGsapKeyframePersist(
"/comp.html",
"tw-1",
50,
{ opacity: 0.5 },
session,
deps,
);
expect(result).toBe(true);
expect(session!.dispatch).toHaveBeenCalledWith({
type: "addGsapKeyframe",
animationId: "tw-1",
position: 50,
value: { opacity: 0.5 },
});
expect(deps.writeProjectFile).toHaveBeenCalledWith("/comp.html", "<html>after</html>");
expect(deps.reloadPreview).toHaveBeenCalled();
});
it("returns false and does not write on dispatch error", async () => {
const deps = makeDeps();
const session = makeSession();
(session!.dispatch as ReturnType<typeof vi.fn>).mockImplementation(() => {
throw new Error("dispatch failed");
});
const result = await sdkGsapKeyframePersist(
"/comp.html",
"tw-1",
25,
{ x: 100 },
session,
deps,
);
expect(result).toBe(false);
expect(deps.writeProjectFile).not.toHaveBeenCalled();
});
});
describe("sdkCutoverPersist — GSAP script preservation (integration)", () => {
const makeRef = <T>(val: T): MutableRefObject<T> => ({ current: val });
const makeDeps = () => ({