aether-shards/test/core/GameStateManager.test.js

150 lines
4.7 KiB
JavaScript
Raw Normal View History

import { expect } from "@esm-bundle/chai";
import sinon from "sinon";
// Import the singleton instance AND the class for constants
import {
gameStateManager,
GameStateManager,
} from "../../src/core/GameStateManager.js";
describe("Core: GameStateManager (Singleton)", () => {
let mockPersistence;
let mockGameLoop;
beforeEach(() => {
// 1. Reset Singleton State
gameStateManager.reset();
// 2. Mock Persistence
mockPersistence = {
init: sinon.stub().resolves(),
saveRun: sinon.stub().resolves(),
loadRun: sinon.stub().resolves(null),
loadRoster: sinon.stub().resolves(null),
saveRoster: sinon.stub().resolves(),
};
// Inject Mock (replacing the real Persistence instance)
gameStateManager.persistence = mockPersistence;
// 3. Mock GameLoop
mockGameLoop = {
init: sinon.spy(),
startLevel: sinon.spy(),
stop: sinon.spy(),
};
// 4. Mock MissionManager
gameStateManager.missionManager = {
setupActiveMission: sinon.stub(),
getActiveMission: sinon.stub().returns({
id: "MISSION_TUTORIAL_01",
config: { title: "Test Mission" },
biome: {
generator_config: {
seed_type: "RANDOM",
seed: 12345,
},
},
objectives: [],
}),
playIntro: sinon.stub().resolves(),
};
});
it("CoA 1: Should initialize and transition to MAIN_MENU", async () => {
const eventSpy = sinon.spy();
window.addEventListener("gamestate-changed", eventSpy);
await gameStateManager.init();
expect(mockPersistence.init.calledOnce).to.be.true;
expect(gameStateManager.currentState).to.equal(
GameStateManager.STATES.MAIN_MENU
);
expect(eventSpy.called).to.be.true;
});
it("CoA 2: startNewGame should transition to TEAM_BUILDER", async () => {
await gameStateManager.init();
gameStateManager.startNewGame();
expect(gameStateManager.currentState).to.equal(
GameStateManager.STATES.TEAM_BUILDER
);
});
it("CoA 3: handleEmbark should initialize run, save, and start engine", async () => {
// Mock RosterManager.recruitUnit to return async unit with generated name
const mockRecruitedUnit = {
id: "UNIT_123",
name: "Valerius", // Generated character name
className: "Vanguard", // Class name
classId: "CLASS_VANGUARD",
};
gameStateManager.rosterManager.recruitUnit = sinon.stub().resolves(mockRecruitedUnit);
gameStateManager.setGameLoop(mockGameLoop);
await gameStateManager.init();
const mockSquad = [{ id: "u1", isNew: false }]; // Existing unit, not new
// Mock startLevel to resolve immediately
mockGameLoop.startLevel = sinon.stub().resolves();
// Await the full async chain
await gameStateManager.handleEmbark({ detail: { squad: mockSquad, mode: "SELECT" } });
expect(gameStateManager.currentState).to.equal(
GameStateManager.STATES.DEPLOYMENT
);
expect(mockPersistence.saveRun.calledWith(gameStateManager.activeRunData))
.to.be.true;
expect(mockGameLoop.startLevel.calledWith(gameStateManager.activeRunData))
.to.be.true;
});
it("CoA 3b: handleEmbark should dispatch run-data-updated event", async () => {
// Mock RosterManager.recruitUnit
const mockRecruitedUnit = {
id: "UNIT_123",
name: "Valerius",
className: "Vanguard",
classId: "CLASS_VANGUARD",
};
gameStateManager.rosterManager.recruitUnit = sinon.stub().resolves(mockRecruitedUnit);
gameStateManager.setGameLoop(mockGameLoop);
await gameStateManager.init();
const mockSquad = [{ id: "u1", isNew: true, name: "Vanguard", classId: "CLASS_VANGUARD" }];
mockGameLoop.startLevel = sinon.stub().resolves();
let eventDispatched = false;
let eventData = null;
window.addEventListener("run-data-updated", (e) => {
eventDispatched = true;
eventData = e.detail.runData;
});
await gameStateManager.handleEmbark({ detail: { squad: mockSquad, mode: "DRAFT" } });
expect(eventDispatched).to.be.true;
expect(eventData).to.exist;
expect(eventData.squad).to.exist;
expect(eventData.squad[0].name).to.equal("Valerius");
expect(eventData.squad[0].className).to.equal("Vanguard");
});
it("CoA 4: continueGame should load save and resume engine", async () => {
gameStateManager.setGameLoop(mockGameLoop);
const savedData = { seed: 999, depth: 5, squad: [] };
mockPersistence.loadRun.resolves(savedData);
await gameStateManager.init();
await gameStateManager.continueGame();
expect(mockPersistence.loadRun.called).to.be.true;
expect(gameStateManager.activeRunData).to.deep.equal(savedData);
expect(mockGameLoop.startLevel.calledWith(savedData)).to.be.true;
});
});