test(core): address review feedback on T6a / T10 / T7 stubs (#1268)

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).
This commit is contained in:
Vance Ingalls
2026-06-07 19:57:57 -07:00
committed by GitHub
parent 1e705b1107
commit 63fd322df1
5 changed files with 104 additions and 3 deletions
@@ -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;"
}
@@ -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;
@@ -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"));
});
});
});
@@ -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",
);
});
});
@@ -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");
});