Files
hyperframes/packages/core/src/figma/emitTimelineScript.test.ts
T
Vance IngallsandClaude Fable 5 e92700acde feat(core): figma motion → GSAP translator + /figma skill v1 (#1869)
* feat(core): add figma motion easing mapping

* feat(core): translate figma motion doc to gsap timeline spec

* feat(core): emit paused GSAP timeline script from figma motion spec

* fix(core): restore type exports dropped from figma barrel in Task 8

* feat(skills): add /figma import skill + catalog wiring

Add the agent-facing /figma skill (asset + Figma Motion import via the
Figma MCP connector, built on @hyperframes/core/figma) and wire it into
the skill catalog across CLAUDE.md, README.md, docs/guides/skills.mdx,
and the hyperframes router's capability map. Bumps the skill count from
19 to 20 in CLAUDE.md and README.md.

* fix(core): use replaceAll for figma node-id dash-to-colon conversion

* style: format skills catalog tables

oxfmt-align the README and router SKILL.md tables after the /figma +
/hyperframes-keyframes merge left uneven column padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(cli): add missing cache fields to telemetry test fixture

ExtractionPhaseBreakdown gained cachePublishFailures/cacheGcEvictions/
cacheGcBytesFreed/cacheAgedPartialsCleared; the studioRenderTelemetry
test fixture was never updated, breaking Typecheck on main and every PR
based on it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:07 -07:00

49 lines
1.7 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from "vitest";
import { emitTimelineScript } from "./emitTimelineScript";
import { motionToGsap } from "./motionToGsap";
import type { MotionDoc } from "./types";
const doc: MotionDoc = {
selector: "#hero-headline",
tracks: [
{
property: "opacity",
values: [0, 1, 0],
times: [0, 0.5, 1],
ease: ["linear", [0.539, 0, 0.312, 0.995]],
duration: 2,
repeat: Infinity,
},
],
};
describe("emitTimelineScript", () => {
const script = emitTimelineScript(motionToGsap(doc));
it("creates a paused timeline and never emits repeat:-1", () => {
expect(script).toContain("gsap.timeline({ paused: true })");
expect(script).not.toContain("repeat: -1");
});
it("registers under a string-literal __timelines key", () => {
expect(script).toContain('window.__timelines["figma-hero-headline"] = tl;');
});
it("uses string-literal selectors and sets the initial value", () => {
expect(script).toContain('tl.set("#hero-headline", { opacity: 0 }, 0);');
expect(script).toContain('tl.to("#hero-headline", { keyframes: [');
});
it("registers a CustomEase for the bezier segment", () => {
expect(script).toContain('CustomEase.create("hfCe0", "M0,0 C0.539,0 0.312,0.995 1,1");');
});
});
describe("emitTimelineScript runtime guard", () => {
it("wraps the script in an IIFE that warns when gsap/CustomEase are missing", () => {
const script = emitTimelineScript(motionToGsap(doc));
expect(script).toContain('typeof gsap === "undefined"');
expect(script).toContain("console.warn");
expect(script.startsWith("(function () {")).toBe(true);
expect(script.endsWith("})();")).toBe(true);
});
});