mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
101 lines
4.4 KiB
TypeScript
101 lines
4.4 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { installStudioCustomEase } from "./customEase";
|
|
|
|
describe("installStudioCustomEase", () => {
|
|
it("resolves wiggle while preserving hold, spring, custom, and named eases", () => {
|
|
const namedEase = (progress: number) => progress * progress;
|
|
const originalParseEase = vi.fn(() => namedEase);
|
|
const gsap = { parseEase: originalParseEase };
|
|
|
|
expect(installStudioCustomEase(gsap)).toBe(true);
|
|
|
|
const wiggleEase = gsap.parseEase("wiggle(6,easeInOut)");
|
|
expect(wiggleEase).toBeTypeOf("function");
|
|
const samples = Array.from({ length: 401 }, (_, index) => wiggleEase(index / 400));
|
|
expect(samples[0]).toBe(0);
|
|
expect(samples.at(-1)).toBe(1);
|
|
expect(samples).toEqual(Array.from({ length: 401 }, (_, index) => wiggleEase(index / 400)));
|
|
const directions = samples
|
|
.slice(1)
|
|
.map((value, index) => Math.sign(value - samples[index]!))
|
|
.filter((direction) => direction !== 0);
|
|
expect(
|
|
directions.filter((direction, index) => index > 0 && direction !== directions[index - 1])
|
|
.length,
|
|
).toBeGreaterThanOrEqual(8);
|
|
|
|
expect(gsap.parseEase("hold")(0.5)).toBe(0);
|
|
expect(gsap.parseEase("spring(0.5)")(0)).toBe(0);
|
|
expect(gsap.parseEase("custom(M0,0 C0.25,0.1 0.25,1 1,1)")(1)).toBe(1);
|
|
expect(Number.isNaN(gsap.parseEase("custom(M0,0 C0.25,0.1 0.25,1 1,1)")(Number.NaN))).toBe(
|
|
true,
|
|
);
|
|
expect(gsap.parseEase("power2.out")).toBe(namedEase);
|
|
expect(originalParseEase).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("registers custom eases in GSAP's internal ease map for keyframe-segment resolution", () => {
|
|
// GSAP resolves keyframe SEGMENT eases via its internal _parseEase/_easeMap,
|
|
// not the public parseEase — so the eases must be registered there too, else
|
|
// a custom ease inside `keyframes:{...}` resolves to undefined and throws
|
|
// "_ease is not a function" on first render.
|
|
const easeMap = new Map<string, ((progress: number) => number) & { config?: unknown }>();
|
|
const gsap = {
|
|
parseEase: vi.fn((ease: unknown) => (typeof ease === "function" ? ease : null)),
|
|
registerEase: (name: string, ease: (progress: number) => number) => easeMap.set(name, ease),
|
|
};
|
|
|
|
expect(installStudioCustomEase(gsap)).toBe(true);
|
|
|
|
// Bare hold registers directly and holds at 0 until the end.
|
|
expect(easeMap.get("hold")?.(0.5)).toBe(0);
|
|
expect(easeMap.get("hold")?.(1)).toBe(1);
|
|
|
|
// GSAP calls a configurable ease's `.config` with the parenthesized params
|
|
// comma-split (custom's bezier path splits into several parts). The config
|
|
// must rejoin them and resolve a real ease.
|
|
const springConfig = easeMap.get("spring")?.config as (
|
|
...p: unknown[]
|
|
) => (n: number) => number;
|
|
expect(springConfig(0.5)(0)).toBe(0);
|
|
|
|
const customConfig = easeMap.get("custom")?.config as (
|
|
...p: unknown[]
|
|
) => (n: number) => number;
|
|
// "custom(M0,0 C0.25,0.1 0.25,1 1,1)" → params split on "," by GSAP:
|
|
const customEase = customConfig("M0", "0 C0.25", "0.1 0.25", "1 1", 1);
|
|
expect(customEase(0)).toBe(0);
|
|
expect(customEase(1)).toBe(1);
|
|
});
|
|
|
|
it("installs once per GSAP instance and reinstalls on a replacement instance", () => {
|
|
const firstParser = vi.fn(() => (progress: number) => progress);
|
|
const first = { parseEase: firstParser };
|
|
expect(installStudioCustomEase(first)).toBe(true);
|
|
const installed = first.parseEase;
|
|
expect(installStudioCustomEase(first)).toBe(true);
|
|
expect(first.parseEase).toBe(installed);
|
|
|
|
const replacementParser = vi.fn(() => (progress: number) => progress);
|
|
const replacement = { parseEase: replacementParser };
|
|
expect(installStudioCustomEase(replacement)).toBe(true);
|
|
expect(replacement.parseEase).not.toBe(replacementParser);
|
|
});
|
|
|
|
it("uses the same GSAP fallback for malformed public and segment eases", () => {
|
|
const fallback = (progress: number) => progress * progress;
|
|
const easeMap = new Map<string, ConfigurableEase>();
|
|
type ConfigurableEase = ((progress: number) => number) & {
|
|
config?: (...params: unknown[]) => (progress: number) => number;
|
|
};
|
|
const gsap = {
|
|
parseEase: vi.fn(() => fallback),
|
|
registerEase: (name: string, ease: ConfigurableEase) => easeMap.set(name, ease),
|
|
};
|
|
expect(installStudioCustomEase(gsap)).toBe(true);
|
|
|
|
expect(gsap.parseEase("spring(nope)")).toBe(fallback);
|
|
expect(easeMap.get("spring")?.config?.("nope")).toBe(fallback);
|
|
});
|
|
});
|