import { describe, expect, test } from "bun:test"; import { applyVisualTransition, buildTransitionFromParams, } from "@/rendering/transition"; import type { Transform } from "@/rendering"; import { TICKS_PER_SECOND } from "@/wasm"; const baseTransform: Transform = { scaleX: 1, scaleY: 1, position: { x: 0, y: 0 }, rotate: 0, }; describe("visual transitions", () => { test("fade ramps opacity at clip edges", () => { const transition = buildTransitionFromParams({ params: { "transition.kind": "fade", "transition.inDuration": 1, "transition.outDuration": 1, }, }); expect( applyVisualTransition({ baseTransform, transition, localTime: 0, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }).opacityMultiplier, ).toBe(0); expect( applyVisualTransition({ baseTransform, transition, localTime: 2 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }).opacityMultiplier, ).toBe(1); expect( applyVisualTransition({ baseTransform, transition, localTime: 4 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }).opacityMultiplier, ).toBe(0); }); test("slide left moves in and out across canvas", () => { const transition = buildTransitionFromParams({ params: { "transition.kind": "push-left", "transition.inDuration": 1, "transition.outDuration": 1, }, }); const enter = applyVisualTransition({ baseTransform, transition, localTime: 0, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); const middle = applyVisualTransition({ baseTransform, transition, localTime: 2 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); const leave = applyVisualTransition({ baseTransform, transition, localTime: 4 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); expect(enter.transform.position.x).toBe(100); expect(middle.transform.position.x).toBe(0); expect(leave.transform.position.x).toBe(-100); expect(leave.opacityMultiplier).toBe(1); }); test("slide right mirrors horizontal entry and exit", () => { const transition = buildTransitionFromParams({ params: { "transition.kind": "push-right", "transition.inDuration": 1, "transition.outDuration": 1, }, }); const enter = applyVisualTransition({ baseTransform, transition, localTime: 0, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); const middle = applyVisualTransition({ baseTransform, transition, localTime: 2 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); const leave = applyVisualTransition({ baseTransform, transition, localTime: 4 * TICKS_PER_SECOND, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); expect(enter.transform.position.x).toBe(-100); expect(middle.transform.position.x).toBe(0); expect(leave.transform.position.x).toBe(100); expect(enter.opacityMultiplier).toBe(1); }); test("zoom and spin variants converge on the base transform", () => { const applyKind = ({ kind, localTime, }: { kind: "zoom" | "zoom-out" | "spin"; localTime: number; }) => applyVisualTransition({ baseTransform, transition: buildTransitionFromParams({ params: { "transition.kind": kind, "transition.inDuration": 1, "transition.outDuration": 0, }, }), localTime, duration: 4 * TICKS_PER_SECOND, canvasWidth: 100, }); expect(applyKind({ kind: "zoom", localTime: 0 }).transform.scaleX).toBe( 0.96, ); expect(applyKind({ kind: "zoom-out", localTime: 0 }).transform.scaleX).toBe( 1.04, ); expect(applyKind({ kind: "spin", localTime: 0 }).transform.rotate).toBe(-6); const settledSpin = applyKind({ kind: "spin", localTime: 2 * TICKS_PER_SECOND, }); expect(settledSpin.transform.scaleX).toBe(1); expect(settledSpin.transform.rotate).toBe(0); }); });