Test: Verify RuntimeHost uses authored input bindings for interaction

This commit is contained in:
2026-04-27 15:44:58 +02:00
parent 6fe5ddead5
commit 3eca45c54b

View File

@@ -105,6 +105,24 @@ function captureCameraPose(camera: PerspectiveCamera) {
};
}
function createRuntimeKeyEvent(
code: string,
overrides: Partial<KeyboardEvent> = {}
): KeyboardEvent {
return {
code,
defaultPrevented: false,
repeat: false,
altKey: false,
ctrlKey: false,
metaKey: false,
target: null,
preventDefault: vi.fn(),
stopImmediatePropagation: vi.fn(),
...overrides
} as unknown as KeyboardEvent;
}
function resolveShortestAngleDeltaDegrees(fromDegrees: number, toDegrees: number) {
return ((toDegrees - fromDegrees + 540) % 360) - 180;
}
@@ -169,6 +187,88 @@ describe("RuntimeHost", () => {
expect(collisionWorld.dispose).toHaveBeenCalledTimes(1);
});
it("uses the authored interact binding instead of always dispatching left mouse clicks", () => {
const playerStart = createPlayerStartEntity({
id: "entity-player-start-authored-interact",
inputBindings: {
keyboard: {
interact: "KeyE"
},
gamepad: {
interact: "buttonWest"
}
}
});
const interactable = createInteractableEntity({
id: "entity-authored-interact-target"
});
const runtimeScene = buildRuntimeSceneFromDocument({
...createEmptySceneDocument({ name: "Authored Interact Scene" }),
entities: {
[playerStart.id]: playerStart,
[interactable.id]: interactable
}
});
const host = new RuntimeHost({
enableRendering: false
});
host.loadScene(runtimeScene);
const hostInternals = host as unknown as {
sceneReady: boolean;
activeController: unknown;
thirdPersonController: unknown;
currentInteractionPrompt: {
sourceEntityId: string;
prompt: string;
distance: number;
range: number;
} | null;
interactionSystem: {
dispatchClickInteraction: ReturnType<typeof vi.fn>;
};
handleRuntimePointerDown(event: {
button: number;
clientX: number;
clientY: number;
preventDefault(): void;
stopImmediatePropagation(): void;
}): void;
handleRuntimeKeyDown(event: KeyboardEvent): void;
};
const dispatchClickInteraction = vi.fn();
hostInternals.sceneReady = true;
hostInternals.activeController = hostInternals.thirdPersonController;
hostInternals.currentInteractionPrompt = {
sourceEntityId: interactable.id,
prompt: interactable.prompt,
distance: 0,
range: interactable.radius
};
hostInternals.interactionSystem.dispatchClickInteraction =
dispatchClickInteraction;
hostInternals.handleRuntimePointerDown({
button: 0,
clientX: 0,
clientY: 0,
preventDefault: vi.fn(),
stopImmediatePropagation: vi.fn()
});
expect(dispatchClickInteraction).not.toHaveBeenCalled();
hostInternals.handleRuntimeKeyDown(createRuntimeKeyEvent("KeyE"));
expect(dispatchClickInteraction).toHaveBeenCalledTimes(1);
expect(dispatchClickInteraction).toHaveBeenCalledWith(
interactable.id,
runtimeScene,
expect.any(Object)
);
});
it("starts default-active rigs in place and blends rig-to-rig overrides", () => {
vi.spyOn(console, "warn").mockImplementation(() => undefined);
vi.spyOn(RapierCollisionWorld, "create").mockResolvedValue({