From bf0e4af46c2dbabbc79041789bcaef980130648e Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Sun, 7 Jun 2026 19:04:35 -0700 Subject: [PATCH] test(core): address review feedback on T6a / T10 / T7 stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T6a (#1263): add fromTo corpus script (third parser method) with negative position values to exercise UnaryExpression arm; drop unused breatheRepeats from COMPLEX_SCRIPT; generate fromto.parsed.json + fromto.serialized.js goldens. T10 (#1262): split applyDraft move/resize into two stubs; add applyDraft edge- case describe block (concurrent gestures, idempotent revert, playhead-change stability, nested sub-composition root); add getElementTimings stub for absent data-start/end on a data-hf-id element. T7 (#1267): expand to full parity with T3 — add text-content, attribute, and fallthrough stubs (Core sourceMutation supports all patch types via patchElementInHtml). --- .../parsers/__goldens__/fromto.parsed.json | 38 +++++++++++++++++++ .../parsers/__goldens__/fromto.serialized.js | 6 +++ .../src/parsers/gsapParser.golden.test.ts | 27 ++++++++++++- .../studio-api/helpers/previewAdapter.test.ts | 26 ++++++++++++- .../studio-api/helpers/sourceMutation.test.ts | 10 ++++- 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/parsers/__goldens__/fromto.parsed.json create mode 100644 packages/core/src/parsers/__goldens__/fromto.serialized.js diff --git a/packages/core/src/parsers/__goldens__/fromto.parsed.json b/packages/core/src/parsers/__goldens__/fromto.parsed.json new file mode 100644 index 000000000..1431064c3 --- /dev/null +++ b/packages/core/src/parsers/__goldens__/fromto.parsed.json @@ -0,0 +1,38 @@ +{ + "animations": [ + { + "targetSelector": "#hero", + "method": "fromTo", + "position": 0.1, + "properties": { + "x": 0, + "opacity": 1 + }, + "fromProperties": { + "x": -200, + "opacity": 0 + }, + "duration": 0.6, + "ease": "power3.out", + "id": "#hero-fromTo-100" + }, + { + "targetSelector": "#caption", + "method": "fromTo", + "position": 0.5, + "properties": { + "y": 0, + "opacity": 1 + }, + "fromProperties": { + "y": -30, + "opacity": 0 + }, + "duration": 0.45, + "id": "#caption-fromTo-500" + } + ], + "timelineVar": "tl", + "preamble": "var tl = gsap.timeline({ paused: true });", + "postamble": "window.__timelines[\"hero-reveal\"] = tl;" +} \ No newline at end of file diff --git a/packages/core/src/parsers/__goldens__/fromto.serialized.js b/packages/core/src/parsers/__goldens__/fromto.serialized.js new file mode 100644 index 000000000..9d62edd97 --- /dev/null +++ b/packages/core/src/parsers/__goldens__/fromto.serialized.js @@ -0,0 +1,6 @@ + + var tl = gsap.timeline({ paused: true }); + tl.fromTo("#hero", { x: -200, opacity: 0 }, { x: 0, opacity: 1, duration: 0.6, ease: "power3.out" }, 0.1); + tl.fromTo("#caption", { y: -30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.45 }, 0.5); + window.__timelines["hero-reveal"] = tl; + \ No newline at end of file diff --git a/packages/core/src/parsers/gsapParser.golden.test.ts b/packages/core/src/parsers/gsapParser.golden.test.ts index 97e915668..509d0be2f 100644 --- a/packages/core/src/parsers/gsapParser.golden.test.ts +++ b/packages/core/src/parsers/gsapParser.golden.test.ts @@ -51,13 +51,22 @@ const COMPLEX_SCRIPT = `\ window.__timelines = window.__timelines || {}; gsap.defaults({ force3D: true }); const tl = gsap.timeline({ paused: true, defaults: { duration: 0.45, ease: "power3.out" } }); -const breatheRepeats = Math.ceil(7 / 2.4) - 1; tl.from(".headline span", { y: 46, opacity: 0, stagger: 0.055, duration: 0.38, ease: "back.out(1.35)" }, 0.05) .from(".headline .sub", { y: 20, opacity: 0, duration: 0.28 }, 0.2) .from(".ambient-word", { scale: 0.92, opacity: 0, duration: 0.5 }, 0.08) .from(".ambient-line", { scaleX: 0, opacity: 0, stagger: 0.08, duration: 0.42 }, 0.16); window.__timelines["vpn-youtube-spot"] = tl;`; +// fromTo: exercises the three-argument (fromArg, toArg, position) AST path and +// negative numeric literals (UnaryExpression arm in resolveNode). +const FROMTO_SCRIPT = `\ +var tl = gsap.timeline({ paused: true }); +var hero = document.getElementById("hero"); +var caption = document.getElementById("caption"); +tl.fromTo(hero, { x: -200, opacity: 0 }, { x: 0, opacity: 1, duration: 0.6, ease: "power3.out" }, 0.1); +tl.fromTo(caption, { y: -30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.45 }, 0.5); +window.__timelines["hero-reveal"] = tl;`; + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -123,4 +132,20 @@ describe("T6a — GSAP parser golden tests (Recast/Babel baseline)", () => { await expect(serialized).toMatchFileSnapshot(g("complex.serialized.js")); }); }); + + describe("fromTo — two tl.fromTo calls with negative positions (hero-reveal)", () => { + let parsed: string; + let serialized: string; + beforeAll(() => { + ({ parsed, serialized } = parseAndSerialize(FROMTO_SCRIPT)); + }); + + it("parseGsapScript output matches golden", async () => { + await expect(parsed).toMatchFileSnapshot(g("fromto.parsed.json")); + }); + + it("serializeGsapAnimations output matches golden", async () => { + await expect(serialized).toMatchFileSnapshot(g("fromto.serialized.js")); + }); + }); }); diff --git a/packages/core/src/studio-api/helpers/previewAdapter.test.ts b/packages/core/src/studio-api/helpers/previewAdapter.test.ts index 7b5480628..860dd39f0 100644 --- a/packages/core/src/studio-api/helpers/previewAdapter.test.ts +++ b/packages/core/src/studio-api/helpers/previewAdapter.test.ts @@ -26,13 +26,33 @@ describe("T10 — PreviewAdapter contract (spec for R7)", () => { describe("applyDraft / revertDraft", () => { it.todo("applyDraft writes --hf-studio-* CSS props and sets the gesture marker"); - it.todo("applyDraft accepts both move (dx/dy) and resize (w/h) payloads"); + it.todo("applyDraft accepts a move payload (dx/dy) and writes the translate draft"); + + it.todo("applyDraft accepts a resize payload (w/h) and writes the size draft"); it.todo("revertDraft removes draft props and clears the gesture marker"); it.todo("revertDraft restores original translate when an original was recorded"); }); + describe("applyDraft edge cases (R7 implementation contract)", () => { + it.todo( + "second applyDraft before revert/commit overwrites first draft — does not accumulate (dx/dy)", + ); + + it.todo( + "revertDraft is safe to call when no gesture is in progress (idempotent / no-op on empty marker)", + ); + + it.todo( + "elementAtPoint filtering is stable when playhead changes mid-drag — opacity re-evaluated per call", + ); + + it.todo( + "stage-root exclusion applies only to the outermost data-hf-root; nested sub-composition roots count as targets", + ); + }); + describe("commitPreview", () => { it.todo("returns null when no gesture marker is present"); @@ -47,5 +67,9 @@ describe("T10 — PreviewAdapter contract (spec for R7)", () => { it.todo("reads authored absolute times from data-start / data-end"); it.todo("ignores elements without data-hf-id"); + + it.todo( + "returns a defined timing entry when data-hf-id is present but data-start / data-end are missing", + ); }); }); diff --git a/packages/core/src/studio-api/helpers/sourceMutation.test.ts b/packages/core/src/studio-api/helpers/sourceMutation.test.ts index 0b4d998d8..c23b45911 100644 --- a/packages/core/src/studio-api/helpers/sourceMutation.test.ts +++ b/packages/core/src/studio-api/helpers/sourceMutation.test.ts @@ -365,8 +365,16 @@ describe("probeElementInSource", () => { // T7 — data-hf-id targeting (spec for R1). // R1 adds `hfId?: string` to SourceMutationTarget and a `[data-hf-id="…"]` branch // in findTargetElement (sourceMutation.ts:34). Convert from it.todo in the R1 PR. +// Covers the same surface as T3 (Studio sourcePatcher) — Core sourceMutation supports +// all patch types (inline-style, attribute, text-content) via patchElementInHtml. describe("T7 — data-hf-id targeting (spec for R1)", () => { - it.todo("patches element by data-hf-id when no HTML id attribute is present"); + it.todo("updates inline style by data-hf-id when no HTML id attribute is present"); + + it.todo("updates text content by data-hf-id"); + + it.todo("updates attribute by data-hf-id"); it.todo("data-hf-id attribute survives the patch (can be targeted again)"); + + it.todo("hfId lookup falls through to selector when hfId is not found in the document"); });