Files
hyperframes/packages/parsers/src/gsapObjectArrayTiming.test.ts
Miguel Ángel db12b3e022 fix(studio): place GSAP step lists and duration-less tweens where GSAP plays them (#5024)
* fix(studio): place GSAP step lists and duration-less tweens where GSAP plays them

* refactor(studio): drop narrated comments and unnest the extend clamp

* fix(studio): match keyframes in time, resolve partial step timing, start readout from prior tween

* fix(studio): time step-list keyframes from the parts' last end and format

* refactor(studio): trim comments to the package's comment share

* fix(parsers): apply timeline defaults as GSAP does to step lists and motion paths

* fix(studio): keep GSAP's ease for motion paths with keyframes and read finished tweens

* refactor(studio): read a live motion path in its own step

* refactor(parsers): leave step-list timing to its one owner and skip timeline defaults there

* refactor(studio): move the live motion-path read into its own module

* test(parsers): pin an unresolved length for a step list stretched by a variable duration

* fix(studio): time keyframes linearly under an outer ease that overshoots or steps

* fix(studio): time a converted expression-duration tween from its live tween, and pin no rewrite
2026-10-05 02:44:49 +00:00

68 lines
2.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
findObjectArrayKeyframeIndex,
getCompatibleObjectArrayKeyframeTiming,
getObjectArrayKeyframeTiming,
} from "./gsapObjectArrayTiming.js";
describe("getObjectArrayKeyframeTiming", () => {
it("ends each equal step at its share of the tween, to a tenth of a percent", () => {
expect(getObjectArrayKeyframeTiming([undefined, undefined, undefined])).toEqual({
percentages: [33.3, 66.7, 100],
totalDuration: 1.5,
});
expect(getObjectArrayKeyframeTiming([undefined])).toEqual({
percentages: [100],
totalDuration: 0.5,
});
});
it("gives a step without a duration GSAP's 0.5 s beside authored ones", () => {
expect(getObjectArrayKeyframeTiming([1, undefined, 1])).toEqual({
percentages: [40, 60, 100],
totalDuration: 2.5,
});
});
it("maps positive authored durations to cumulative percentages", () => {
expect(getObjectArrayKeyframeTiming([1, 2, 1])).toEqual({
percentages: [25, 75, 100],
totalDuration: 4,
});
});
it.each([
[[0, 1, 1], "zero"],
[[1, -1, 1], "negative"],
[[1, Number.NaN, 1], "non-finite"],
[[1, "__raw:total * 0.5", 1], "expression"],
])("rejects %s duration timing (%s)", (durations) => {
expect(getObjectArrayKeyframeTiming(durations)).toBeNull();
});
});
describe("findObjectArrayKeyframeIndex", () => {
it("falls back to the nearest array entry for an in-range percentage", () => {
expect(
findObjectArrayKeyframeIndex([undefined, undefined, undefined, undefined], 50, {
fallbackToNearest: true,
}),
).toBe(1);
});
it("rejects out-of-range and unresolved timing", () => {
expect(findObjectArrayKeyframeIndex([undefined, undefined], -1)).toBeNull();
expect(findObjectArrayKeyframeIndex([undefined, undefined], 101)).toBeNull();
expect(findObjectArrayKeyframeIndex([1, 0, 1], 50)).toBeNull();
});
});
describe("getCompatibleObjectArrayKeyframeTiming", () => {
it("accepts absent or matching outer durations and rejects conflicts", () => {
expect(getCompatibleObjectArrayKeyframeTiming([0.25, 0.75], undefined)).not.toBeNull();
expect(getCompatibleObjectArrayKeyframeTiming([0.25, 0.75], 1)).not.toBeNull();
expect(getCompatibleObjectArrayKeyframeTiming([0.25, 0.75], 2)).toBeNull();
expect(getCompatibleObjectArrayKeyframeTiming([0.25, 0.75], "1")).toBeNull();
});
});