123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import { DUNGEON_MANIFEST } from "./dungeonManifest";
|
|
import { useGameStore } from "./store";
|
|
|
|
describe("game store", () => {
|
|
beforeEach(() => {
|
|
useGameStore.getState().resetAtEntrance();
|
|
useGameStore.setState({
|
|
overlay: null,
|
|
paused: false,
|
|
mapOpen: false,
|
|
companionOpen: false,
|
|
assetStatus: "checking",
|
|
assetMessage: null,
|
|
distanceTravelled: 0,
|
|
hasMoved: false,
|
|
});
|
|
});
|
|
|
|
it("opens the map without leaving camera control active", () => {
|
|
useGameStore.setState({ cameraLookActive: true });
|
|
useGameStore.getState().toggleMap();
|
|
expect(useGameStore.getState()).toMatchObject({
|
|
overlay: "map",
|
|
paused: true,
|
|
mapOpen: true,
|
|
cameraLookActive: false,
|
|
});
|
|
});
|
|
|
|
it("keeps all full-screen game panels mutually exclusive", () => {
|
|
useGameStore.getState().openOverlay("spellbook");
|
|
expect(useGameStore.getState()).toMatchObject({ overlay: "spellbook", paused: true, mapOpen: false });
|
|
useGameStore.getState().openOverlay("talents");
|
|
expect(useGameStore.getState()).toMatchObject({ overlay: "talents", paused: true, mapOpen: false });
|
|
useGameStore.getState().openOverlay("bindings");
|
|
expect(useGameStore.getState()).toMatchObject({ overlay: "bindings", paused: true, mapOpen: false });
|
|
useGameStore.getState().closeOverlay();
|
|
expect(useGameStore.getState()).toMatchObject({ overlay: null, paused: false, mapOpen: false });
|
|
});
|
|
|
|
it("blocks camera-drag play while the one-screen companion is open", () => {
|
|
useGameStore.setState({ cameraLookActive: true });
|
|
useGameStore.getState().setCompanionOpen(true);
|
|
expect(useGameStore.getState()).toMatchObject({ companionOpen: true, cameraLookActive: false });
|
|
});
|
|
|
|
it("records throttled exploration snapshots", () => {
|
|
useGameStore.getState().updatePlayerSnapshot({
|
|
position: [164, -73.66, 132],
|
|
yaw: -2,
|
|
distanceDelta: 1.25,
|
|
});
|
|
const state = useGameStore.getState();
|
|
expect(state.distanceTravelled).toBeCloseTo(1.25);
|
|
expect(state.hasMoved).toBe(true);
|
|
expect(state.areaName).toContain("Entrance");
|
|
});
|
|
|
|
it("keeps a grounded navigation anchor while the physical player is airborne", () => {
|
|
useGameStore.getState().updatePlayerSnapshot({
|
|
position: [164, -73.66, 132],
|
|
yaw: -2,
|
|
grounded: true,
|
|
});
|
|
useGameStore.getState().updatePlayerSnapshot({
|
|
position: [166, -72.2, 133],
|
|
yaw: -2,
|
|
grounded: false,
|
|
});
|
|
|
|
expect(useGameStore.getState()).toMatchObject({
|
|
playerPosition: [166, -72.2, 133],
|
|
playerNavigationPosition: [164, -73.66, 132],
|
|
playerGrounded: false,
|
|
});
|
|
|
|
useGameStore.getState().updatePlayerSnapshot({
|
|
position: [167, -73.66, 134],
|
|
yaw: -2,
|
|
grounded: true,
|
|
});
|
|
expect(useGameStore.getState()).toMatchObject({
|
|
playerNavigationPosition: [167, -73.66, 134],
|
|
playerGrounded: true,
|
|
});
|
|
});
|
|
|
|
it("reset restores the authoritative transformed entrance", () => {
|
|
useGameStore.getState().setActionLayer("tertiary");
|
|
useGameStore.getState().updatePlayerSnapshot({ position: [0, 0, 0], yaw: 0, distanceDelta: 8 });
|
|
useGameStore.getState().resetAtEntrance();
|
|
const state = useGameStore.getState();
|
|
expect(state.playerPosition).toEqual(DUNGEON_MANIFEST.entrance.footPosition);
|
|
expect(state.playerNavigationPosition).toEqual(DUNGEON_MANIFEST.entrance.footPosition);
|
|
expect(state.playerGrounded).toBe(true);
|
|
expect(state.cameraYaw).toBe(DUNGEON_MANIFEST.entrance.yaw);
|
|
expect(state.distanceTravelled).toBe(0);
|
|
expect(state.actionLayer).toBe("primary");
|
|
});
|
|
|
|
it("does not let a late loading effect cover an already-ready scene", () => {
|
|
useGameStore.getState().setAssetStatus("ready");
|
|
useGameStore.getState().setAssetStatus("loading");
|
|
expect(useGameStore.getState().assetStatus).toBe("ready");
|
|
});
|
|
|
|
it("does not notify dual-screen subscribers for repeated input state", () => {
|
|
useGameStore.getState().setCameraLookActive(false);
|
|
useGameStore.getState().setInputMode("keyboard");
|
|
let notifications = 0;
|
|
const unsubscribe = useGameStore.subscribe(() => { notifications += 1; });
|
|
|
|
useGameStore.getState().setCameraLookActive(false);
|
|
useGameStore.getState().setInputMode("keyboard");
|
|
expect(notifications).toBe(0);
|
|
|
|
useGameStore.getState().setCameraLookActive(true);
|
|
expect(notifications).toBe(1);
|
|
unsubscribe();
|
|
});
|
|
});
|