import { describe, it, expect } from "vitest"; import { gameReducer, createInitialState } from "./engine"; import type { ShuffledQuestion } from "./types"; const mockQuestion = (id: string, difficulty: "easy" | "medium" | "hard" = "medium"): ShuffledQuestion => ({ id, category: "General", difficulty, questionText: `Question ${id}?`, correctAnswer: "Correct", incorrectAnswers: ["Wrong1", "Wrong2", "Wrong3"], allAnswers: ["Correct", "Wrong1", "Wrong2", "Wrong3"], }); describe("createInitialState", () => { it("creates idle state for quick-fire", () => { const state = createInitialState("quick-fire", [mockQuestion("1")]); expect(state.phase).toBe("idle"); expect(state.mode).toBe("quick-fire"); expect(state.score).toBe(0); expect(state.currentQuestionIndex).toBe(0); }); }); describe("gameReducer", () => { it("START transitions from idle to playing", () => { const state = createInitialState("quick-fire", [mockQuestion("1")]); const next = gameReducer(state, { type: "START" }); expect(next.phase).toBe("playing"); expect(next.startedAt).not.toBeNull(); }); it("ANSWER_CORRECT increments score and streak", () => { let state = createInitialState("quick-fire", [mockQuestion("1"), mockQuestion("2")]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 200 }); expect(state.score).toBe(200); expect(state.streak).toBe(1); expect(state.questionsCorrect).toBe(1); expect(state.currentQuestionIndex).toBe(1); }); it("ANSWER_WRONG resets streak to 0", () => { let state = createInitialState("quick-fire", [mockQuestion("1"), mockQuestion("2")]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 100 }); state = gameReducer(state, { type: "ANSWER_WRONG" }); expect(state.streak).toBe(0); expect(state.questionsCorrect).toBe(1); expect(state.currentQuestionIndex).toBe(2); }); it("ANSWER_WRONG in streak mode ends the game", () => { let state = createInitialState("streak", [mockQuestion("1"), mockQuestion("2")]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "ANSWER_WRONG" }); expect(state.phase).toBe("finished"); }); it("answering last question finishes the game", () => { let state = createInitialState("quick-fire", [mockQuestion("1")]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 100 }); expect(state.phase).toBe("finished"); }); it("TIME_UP finishes the game", () => { let state = createInitialState("quick-fire", [mockQuestion("1")]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "TIME_UP" }); expect(state.phase).toBe("finished"); }); it("tracks streakMax across the game", () => { let state = createInitialState("quick-fire", [ mockQuestion("1"), mockQuestion("2"), mockQuestion("3"), mockQuestion("4"), ]); state = gameReducer(state, { type: "START" }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 100 }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 100 }); state = gameReducer(state, { type: "ANSWER_WRONG" }); state = gameReducer(state, { type: "ANSWER_CORRECT", points: 100 }); expect(state.streakMax).toBe(2); expect(state.streak).toBe(1); }); it("NEXT_PHASE transitions challenge-me phases", () => { let state = createInitialState("challenge-me", [mockQuestion("1")]); state = gameReducer(state, { type: "START" }); expect(state.challengePhase).toBe("easy"); state = gameReducer(state, { type: "NEXT_PHASE" }); expect(state.challengePhase).toBe("medium"); state = gameReducer(state, { type: "NEXT_PHASE" }); expect(state.challengePhase).toBe("hard"); state = gameReducer(state, { type: "NEXT_PHASE" }); expect(state.phase).toBe("finished"); }); });