import { describe, expect, test } from "vitest"; import { OfficeSimulation, cameraFor, type MoveInput } from "../../src/renderer/src/pages/office/simulation"; import type { AgentActivity, OfficeAgentInput } from "../../src/renderer/src/pages/office/office-types"; const IDLE: MoveInput = { up: false, down: false, left: false, right: false }; function agentInput(id: string, roleLevel = "staff", activity: AgentActivity = "idle"): OfficeAgentInput { return { id, name: `Agent ${id}`, role: "Engineer", title: "Engineer", department: "Engineering", roleLevel, activity, taskTitle: null, }; } function steps(sim: OfficeSimulation, ms: number, input: Partial): void { const move = { ...IDLE, ...input }; for (let i = 0; i < ms / 16; i++) sim.step(16, move); } describe("office simulation", () => { test("the CEO takes the CEO office and staff fill open desks in order", () => { const sim = new OfficeSimulation(); sim.sync([agentInput("a"), agentInput("ceo", "c_suite"), agentInput("b")]); const ceo = sim.agents.find((agent) => agent.input.id === "ceo")!; const staffA = sim.agents.find((agent) => agent.input.id === "a")!; const staffB = sim.agents.find((agent) => agent.input.id === "b")!; expect(ceo.isCeo).toBe(true); expect(ceo.seat).toEqual(sim.plan.ceoSeat); expect(staffA.deskIndex).toBe(0); expect(staffB.deskIndex).toBe(1); }); test("positions survive snapshot refreshes without teleporting", () => { const sim = new OfficeSimulation(); sim.sync([agentInput("a")]); sim.agents[0].x = 500; sim.agents[0].y = 320; sim.sync([agentInput("a")]); expect(sim.agents[0].x).toBe(500); expect(sim.agents[0].y).toBe(320); }); test("the board member spawns at the entrance and keeps position on re-sync", () => { const sim = new OfficeSimulation(); sim.syncPlayer("emp-1", "Dana Lee"); expect(sim.player!.x).toBe(sim.plan.entrance.x); expect(sim.player!.y).toBe(sim.plan.entrance.y); steps(sim, 600, { right: true }); const movedX = sim.player!.x; sim.syncPlayer("emp-1", "Dana Lee"); expect(sim.player!.x).toBe(movedX); }); test("WASD input moves and faces the board member", () => { const sim = new OfficeSimulation(); sim.syncPlayer("emp-1", "Dana Lee"); const startX = sim.player!.x; steps(sim, 320, { right: true }); expect(sim.player!.x).toBeGreaterThan(startX + 30); expect(sim.player!.facing).toBe("east"); expect(sim.player!.walkPhase).toBeGreaterThan(0); sim.step(16, IDLE); expect(sim.player!.walkPhase).toBe(0); }); test("walls stop the board member", () => { const sim = new OfficeSimulation(); sim.syncPlayer("emp-1", "Dana Lee"); steps(sim, 4000, { left: true }); expect(sim.player!.x).toBeGreaterThanOrEqual(22); const y = sim.player!.y; steps(sim, 4000, { up: true }); expect(sim.player!.y).toBeGreaterThan(288); expect(sim.player!.y).toBeLessThanOrEqual(y); }); test("diagonal movement is not faster than straight movement", () => { const sim = new OfficeSimulation(); sim.syncPlayer("emp-1", "Dana Lee"); const start = { x: sim.player!.x, y: sim.player!.y }; steps(sim, 160, { right: true, down: true }); const diagonal = Math.hypot(sim.player!.x - start.x, sim.player!.y - start.y); const sim2 = new OfficeSimulation(); sim2.syncPlayer("emp-2", "Alex"); const start2 = sim2.player!.x; steps(sim2, 160, { right: true }); const straight = sim2.player!.x - start2; expect(diagonal).toBeLessThanOrEqual(straight + 0.5); expect(diagonal).toBeGreaterThan(straight * 0.7); }); test("idle agents take a coffee break and carry the cup back to their desk", () => { const sim = new OfficeSimulation(() => 0); sim.sync([agentInput("a")]); const agent = sim.agents[0]; const coffee = sim.plan.amenities.find((spot) => spot.kind === "coffee")!; let reachedCoffee = false; let backSeated = false; for (let i = 0; i < 6000; i++) { sim.step(16, IDLE); if (agent.behavior.kind === "dwelling") { expect(agent.x).toBe(coffee.stand.x); expect(agent.y).toBe(coffee.stand.y); reachedCoffee = true; } if (reachedCoffee && agent.behavior.kind === "seated") { backSeated = true; break; } } expect(reachedCoffee).toBe(true); expect(backSeated).toBe(true); expect(agent.x).toBe(agent.seat.x); expect(agent.y).toBe(agent.seat.y); expect(agent.carrying).toBe("coffee"); }); test("working agents never leave their desks", () => { const sim = new OfficeSimulation(() => 0); sim.sync([agentInput("a", "staff", "active")]); const agent = sim.agents[0]; for (let i = 0; i < 600; i++) sim.step(16, IDLE); expect(agent.behavior.kind).toBe("seated"); expect(agent.x).toBe(agent.seat.x); expect(agent.y).toBe(agent.seat.y); }); test("an agent called to work abandons the break and returns to their desk", () => { const sim = new OfficeSimulation(() => 0); sim.sync([agentInput("a")]); const agent = sim.agents[0]; for (let i = 0; i < 3000 && agent.behavior.kind === "seated"; i++) sim.step(16, IDLE); for (let i = 0; i < 30; i++) sim.step(16, IDLE); expect(agent.behavior.kind).toBe("walking"); expect(agent.x === agent.seat.x && agent.y === agent.seat.y).toBe(false); sim.sync([agentInput("a", "staff", "active")]); for (let i = 0; i < 6000 && sim.agents[0].behavior.kind !== "seated"; i++) sim.step(16, IDLE); expect(sim.agents[0].behavior.kind).toBe("seated"); expect(sim.agents[0].x).toBe(sim.agents[0].seat.x); expect(sim.agents[0].y).toBe(sim.agents[0].seat.y); }); test("clicks hit the agent drawn at that spot, frontmost first", () => { const sim = new OfficeSimulation(); sim.sync([agentInput("a"), agentInput("b")]); const first = sim.agents[0]; expect(sim.hitTest({ x: first.x, y: first.y - 10 })!.input.id).toBe("a"); expect(sim.hitTest({ x: first.x - 60, y: first.y - 10 })).toBeNull(); const second = sim.agents[1]; second.x = first.x; second.y = first.y + 6; expect(sim.hitTest({ x: first.x, y: first.y })!.input.id).toBe("b"); }); test("camera follows the player and clamps at world edges", () => { const sim = new OfficeSimulation(); sim.sync(Array.from({ length: 30 }, (_, i) => agentInput(`a${i}`))); const world = sim.plan.world; const centered = cameraFor(world, 600, 400, { x: 700, y: 700 }); expect(centered).toEqual({ x: 400, y: 500 }); const topLeft = cameraFor(world, 600, 400, { x: 0, y: 0 }); expect(topLeft).toEqual({ x: 0, y: 0 }); const bottomRight = cameraFor(world, 600, 400, { x: world.w, y: world.h }); expect(bottomRight).toEqual({ x: world.w - 600, y: world.h - 400 }); const wide = cameraFor(world, world.w + 200, 400, { x: 0, y: 0 }); expect(wide.x).toBe(-100); }); });