diff options
Diffstat (limited to 'apps/web/src/lib/animation/__tests__/keyframe-query.test.ts')
| -rw-r--r-- | apps/web/src/lib/animation/__tests__/keyframe-query.test.ts | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/apps/web/src/lib/animation/__tests__/keyframe-query.test.ts b/apps/web/src/lib/animation/__tests__/keyframe-query.test.ts new file mode 100644 index 00000000..319493c8 --- /dev/null +++ b/apps/web/src/lib/animation/__tests__/keyframe-query.test.ts @@ -0,0 +1,139 @@ +import { describe, expect, test } from "bun:test"; +import { + getElementKeyframes, + getKeyframeById, + getKeyframeAtTime, +} from "@/lib/animation/keyframe-query"; +import type { + ElementAnimations, + ScalarAnimationKey, +} from "@/lib/animation/types"; + +function createScalarKey({ + id, + time, + value, +}: { + id: string; + time: number; + value: number; +}): ScalarAnimationKey { + return { + id, + time, + value, + segmentToNext: "linear", + tangentMode: "flat", + }; +} + +function buildPositionAnimations({ + xKeys, + yKeys, +}: { + xKeys: ScalarAnimationKey[]; + yKeys: ScalarAnimationKey[]; +}): ElementAnimations { + return { + bindings: { + "transform.position": { + path: "transform.position", + kind: "vector2", + components: [ + { key: "x", channelId: "transform.position:x" }, + { key: "y", channelId: "transform.position:y" }, + ], + }, + }, + channels: { + "transform.position:x": { + kind: "scalar", + keys: xKeys, + }, + "transform.position:y": { + kind: "scalar", + keys: yKeys, + }, + }, + }; +} + +describe("keyframe query", () => { + test("returns keyframes from any component channel", () => { + const animations = buildPositionAnimations({ + xKeys: [createScalarKey({ id: "x-1", time: 1, value: 10 })], + yKeys: [createScalarKey({ id: "y-2", time: 2, value: 20 })], + }); + + expect( + getElementKeyframes({ animations }).map(({ id, time }) => ({ + id, + time, + })), + ).toEqual([ + { id: "x-1", time: 1 }, + { id: "y-2", time: 2 }, + ]); + }); + + test("finds a keyframe at time on a non-primary component", () => { + const animations = buildPositionAnimations({ + xKeys: [createScalarKey({ id: "x-1", time: 1, value: 10 })], + yKeys: [createScalarKey({ id: "y-2", time: 2, value: 20 })], + }); + + expect( + getKeyframeAtTime({ + animations, + propertyPath: "transform.position", + time: 2, + }), + ).toMatchObject({ + id: "y-2", + time: 2, + }); + }); + + test("finds a keyframe by id on a non-primary component", () => { + const animations = buildPositionAnimations({ + xKeys: [createScalarKey({ id: "x-1", time: 1, value: 10 })], + yKeys: [createScalarKey({ id: "y-2", time: 2, value: 20 })], + }); + + expect( + getKeyframeById({ + animations, + propertyPath: "transform.position", + keyframeId: "y-2", + }), + ).toMatchObject({ + id: "y-2", + time: 2, + value: { x: 10, y: 20 }, + }); + }); + + test("prefers the primary component when multiple components share a time", () => { + const animations = buildPositionAnimations({ + xKeys: [createScalarKey({ id: "x-1", time: 1, value: 10 })], + yKeys: [createScalarKey({ id: "y-1", time: 1, value: 20 })], + }); + + expect( + getElementKeyframes({ animations }).map(({ id, time }) => ({ + id, + time, + })), + ).toEqual([{ id: "x-1", time: 1 }]); + expect( + getKeyframeAtTime({ + animations, + propertyPath: "transform.position", + time: 1, + }), + ).toMatchObject({ + id: "x-1", + time: 1, + }); + }); +}); |
