import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; import { get } from "svelte/store"; import { characterState, characterInfo } from "./character"; describe("characterState store", () => { beforeEach(() => { vi.useFakeTimers(); characterState.reset(); }); afterEach(() => { vi.useRealTimers(); }); describe("initial state", () => { it("starts in idle state", () => { expect(get(characterState)).toBe("idle"); }); }); describe("setState", () => { it("sets the character state", () => { characterState.setState("thinking"); expect(get(characterState)).toBe("thinking"); }); it("can set any valid state", () => { const states = [ "idle", "thinking", "typing", "coding", "searching", "mcp", "permission", "success", "error", ] as const; for (const state of states) { characterState.setState(state); expect(get(characterState)).toBe(state); } }); it("cancels any active temporary state timer", () => { characterState.setTemporaryState("success", 5000); characterState.setState("thinking"); // Advance past the temporary state duration — should stay as thinking vi.advanceTimersByTime(6000); expect(get(characterState)).toBe("thinking"); }); }); describe("setTemporaryState", () => { it("sets the character state immediately", () => { characterState.setTemporaryState("success", 2000); expect(get(characterState)).toBe("success"); }); it("reverts to idle after the specified duration", () => { characterState.setTemporaryState("success", 2000); vi.advanceTimersByTime(2000); expect(get(characterState)).toBe("idle"); }); it("uses 2000ms as the default duration", () => { characterState.setTemporaryState("error"); vi.advanceTimersByTime(1999); expect(get(characterState)).toBe("error"); vi.advanceTimersByTime(1); expect(get(characterState)).toBe("idle"); }); it("cancels a previous temporary state timer when a new one is set", () => { characterState.setTemporaryState("success", 5000); characterState.setTemporaryState("error", 1000); // First timer would have fired at 5000ms but was cancelled vi.advanceTimersByTime(1000); expect(get(characterState)).toBe("idle"); }); }); describe("reset", () => { it("resets the state to idle", () => { characterState.setState("thinking"); characterState.reset(); expect(get(characterState)).toBe("idle"); }); it("cancels any pending temporary state timer", () => { characterState.setTemporaryState("success", 5000); characterState.reset(); // Should now be idle and should NOT revert again after timer fires expect(get(characterState)).toBe("idle"); vi.advanceTimersByTime(5000); expect(get(characterState)).toBe("idle"); }); }); }); describe("characterInfo derived store", () => { beforeEach(() => { characterState.reset(); }); it("returns the info object for the current state", () => { const info = get(characterInfo); expect(info).toBeDefined(); expect(typeof info.label).toBe("string"); }); it("updates when the character state changes", () => { characterState.setState("thinking"); const thinkingInfo = get(characterInfo); characterState.setState("idle"); const idleInfo = get(characterInfo); expect(thinkingInfo.label).not.toBe(idleInfo.label); }); });