From 4d682eed17334c73086c2d1771f519a78aa7ffc6 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sat, 11 Apr 2026 12:16:16 +0200 Subject: [PATCH] Add unit test for ThirdPersonNavigationController --- ...third-person-navigation-controller.test.ts | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/unit/third-person-navigation-controller.test.ts diff --git a/tests/unit/third-person-navigation-controller.test.ts b/tests/unit/third-person-navigation-controller.test.ts new file mode 100644 index 00000000..910874fd --- /dev/null +++ b/tests/unit/third-person-navigation-controller.test.ts @@ -0,0 +1,94 @@ +import { PerspectiveCamera } from "three"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import type { Vec3 } from "../../src/core/vector"; +import { createEmptySceneDocument } from "../../src/document/scene-document"; +import { createPlayerStartEntity } from "../../src/entities/entity-instances"; +import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build"; +import { ThirdPersonNavigationController } from "../../src/runtime-three/third-person-navigation-controller"; + +function createRuntimeControllerContext() { + const runtimeScene = buildRuntimeSceneFromDocument( + { + ...createEmptySceneDocument({ name: "Third Person Binding Scene" }), + entities: { + "entity-player-start-main": createPlayerStartEntity({ + id: "entity-player-start-main", + inputBindings: { + keyboard: { + moveForward: "ArrowUp", + moveBackward: "ArrowDown", + moveLeft: "ArrowLeft", + moveRight: "ArrowRight" + } + } + }) + } + }, + { + navigationMode: "thirdPerson" + } + ); + const domElement = document.createElement("canvas"); + + return { + context: { + camera: new PerspectiveCamera(70, 1, 0.05, 1000), + domElement, + getRuntimeScene: () => runtimeScene, + resolveFirstPersonMotion: ( + feetPosition: Vec3, + motion: Vec3, + _shape + ) => ({ + feetPosition: { + x: feetPosition.x + motion.x, + y: feetPosition.y + motion.y, + z: feetPosition.z + motion.z + }, + grounded: false + }), + resolvePlayerVolumeState: () => ({ + inWater: false, + inFog: false + }), + resolveThirdPersonCameraCollision: ( + _pivot: Vec3, + desiredCameraPosition: Vec3 + ) => ({ + ...desiredCameraPosition + }), + setRuntimeMessage: vi.fn(), + setFirstPersonTelemetry: vi.fn() + } + }; +} + +describe("ThirdPersonNavigationController", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("uses authored keyboard bindings instead of hardcoded WASD movement", () => { + const { context } = createRuntimeControllerContext(); + const controller = new ThirdPersonNavigationController(); + + controller.activate(context); + + const initialCameraZ = context.camera.position.z; + + window.dispatchEvent(new KeyboardEvent("keydown", { code: "KeyW" })); + controller.update(1); + + expect(context.camera.position.z).toBe(initialCameraZ); + + window.dispatchEvent(new KeyboardEvent("keyup", { code: "KeyW" })); + window.dispatchEvent(new KeyboardEvent("keydown", { code: "ArrowUp" })); + controller.update(1); + + expect(context.camera.position.z).toBeGreaterThan(initialCameraZ); + + window.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); + controller.deactivate(context); + }); +}); \ No newline at end of file