From ea7481acc9fc32998408d7ce5a763f616b360b6b Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Tue, 14 Apr 2026 22:26:16 +0200 Subject: [PATCH] Add test for project time pause control effects --- tests/unit/runtime-host.test.ts | 85 +++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/tests/unit/runtime-host.test.ts b/tests/unit/runtime-host.test.ts index 99ce5490..12bf1fac 100644 --- a/tests/unit/runtime-host.test.ts +++ b/tests/unit/runtime-host.test.ts @@ -10,7 +10,9 @@ import { createPlayActorAnimationControlEffect, createPlayModelAnimationControlEffect, createPlaySoundControlEffect, + createProjectGlobalControlTargetRef, createSetActorPresenceControlEffect, + createSetProjectTimePausedControlEffect, type ControlEffect, createSetAmbientLightColorControlEffect, createSetAmbientLightIntensityControlEffect, @@ -49,6 +51,7 @@ import { RapierCollisionWorld } from "../../src/runtime-three/rapier-collision-w import { RuntimeHost, type RuntimeDialogueState, + type RuntimePauseState, type RuntimeSceneLoadState } from "../../src/runtime-three/runtime-host"; import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build"; @@ -231,6 +234,88 @@ describe("RuntimeHost", () => { host.dispose(); }); + it("applies project time pause control effects through the runtime dispatcher", () => { + vi.spyOn(console, "warn").mockImplementation(() => undefined); + vi.spyOn(RapierCollisionWorld, "create").mockResolvedValue({ + dispose: vi.fn(), + resolveThirdPersonCameraCollision: vi.fn( + (_pivot, desiredCameraPosition) => desiredCameraPosition + ) + } as unknown as RapierCollisionWorld); + + const runtimeScene = buildRuntimeSceneFromDocument( + createEmptySceneDocument() + ); + const host = new RuntimeHost({ + enableRendering: false + }); + const pauseStates: RuntimePauseState[] = []; + host.setRuntimePauseStateHandler((state) => { + pauseStates.push(state); + }); + host.loadScene(runtimeScene); + + const pauseEffect = createSetProjectTimePausedControlEffect({ + target: createProjectGlobalControlTargetRef(), + paused: true + }); + const resumeEffect = createSetProjectTimePausedControlEffect({ + target: createProjectGlobalControlTargetRef(), + paused: false + }); + const pauseLink = createControlInteractionLink({ + id: "link-pause-time", + sourceEntityId: "entity-trigger-main", + effect: pauseEffect + }); + const resumeLink = createControlInteractionLink({ + id: "link-resume-time", + sourceEntityId: "entity-trigger-main", + effect: resumeEffect + }); + const hostInternals = host as unknown as { + createInteractionDispatcher(): { + dispatchControlEffect( + effect: ControlEffect, + link: InteractionLink + ): void; + }; + }; + const dispatcher = hostInternals.createInteractionDispatcher(); + + dispatcher.dispatchControlEffect(pauseEffect, pauseLink); + + expect(pauseStates).toContainEqual({ + paused: true, + source: "control" + }); + expect(runtimeScene.control.resolved.discrete).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "projectTimePaused", + target: { + kind: "global", + scope: "project" + }, + value: true, + source: { + kind: "interactionLink", + linkId: pauseLink.id + } + }) + ]) + ); + + dispatcher.dispatchControlEffect(resumeEffect, resumeLink); + + expect(pauseStates).toContainEqual({ + paused: false, + source: null + }); + + host.dispose(); + }); + it("opens, advances, and closes project dialogues through the runtime host", () => { vi.spyOn(console, "warn").mockImplementation(() => undefined); vi.spyOn(RapierCollisionWorld, "create").mockResolvedValue({