diff options
| author | cashmere <cashmere@autistici.org> | 2026-08-16 06:16:56 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-08-16 06:16:56 +0200 |
| commit | f1043734438c8518a302f698d9ae6583a75f0c14 (patch) | |
| tree | d3e636a96e6349c1ad17afa7b28c9ffe5c882e37 /apps/web/src/rendering/__tests__ | |
| parent | 37d22b1e390bd6cea04bf8e15069281ee125cf10 (diff) | |
| download | opencut-f1043734438c8518a302f698d9ae6583a75f0c14.tar.gz | |
feat: add transitions, track mixing, and voiceover controls
Diffstat (limited to 'apps/web/src/rendering/__tests__')
| -rw-r--r-- | apps/web/src/rendering/__tests__/transition.test.ts | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/apps/web/src/rendering/__tests__/transition.test.ts b/apps/web/src/rendering/__tests__/transition.test.ts new file mode 100644 index 00000000..4421fe58 --- /dev/null +++ b/apps/web/src/rendering/__tests__/transition.test.ts @@ -0,0 +1,166 @@ +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); + }); +}); |
