282 lines
9.5 KiB
TypeScript
282 lines
9.5 KiB
TypeScript
/**
|
||
* useExamStateMachine Hook 测试
|
||
*
|
||
* 测试考试状态机的状态流转:
|
||
* NotStarted → InProgress → AutoSaving → Submitting → Submitted
|
||
* 验证合法转移与非法转移拒绝。
|
||
*/
|
||
|
||
import { describe, it, expect } from "vitest";
|
||
import { renderHook, act } from "@testing-library/react";
|
||
import { useExamStateMachine } from "./use-exam-state-machine";
|
||
|
||
describe("useExamStateMachine", () => {
|
||
it("初始状态应为 NotStarted", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
expect(result.current.state).toBe("NotStarted");
|
||
});
|
||
|
||
it("初始时 canSubmit 应为 false", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
|
||
describe("合法状态转移", () => {
|
||
it("start: NotStarted → InProgress", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
expect(result.current.canSubmit).toBe(true);
|
||
});
|
||
|
||
it("beginAutoSave: InProgress → AutoSaving", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("AutoSaving");
|
||
expect(result.current.canSubmit).toBe(true);
|
||
});
|
||
|
||
it("endAutoSave: AutoSaving → InProgress", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
result.current.actions.endAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
});
|
||
|
||
it("beginSubmit: InProgress → Submitting", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
});
|
||
expect(result.current.state).toBe("Submitting");
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
|
||
it("beginSubmit: AutoSaving → Submitting", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
result.current.actions.beginSubmit();
|
||
});
|
||
expect(result.current.state).toBe("Submitting");
|
||
});
|
||
|
||
it("completeSubmit: Submitting → Submitted", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.completeSubmit();
|
||
});
|
||
expect(result.current.state).toBe("Submitted");
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
|
||
it("完整流程: NotStarted → InProgress → AutoSaving → InProgress → Submitting → Submitted", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
|
||
act(() => result.current.actions.start());
|
||
expect(result.current.state).toBe("InProgress");
|
||
|
||
act(() => result.current.actions.beginAutoSave());
|
||
expect(result.current.state).toBe("AutoSaving");
|
||
|
||
act(() => result.current.actions.endAutoSave());
|
||
expect(result.current.state).toBe("InProgress");
|
||
|
||
act(() => result.current.actions.beginSubmit());
|
||
expect(result.current.state).toBe("Submitting");
|
||
|
||
act(() => result.current.actions.completeSubmit());
|
||
expect(result.current.state).toBe("Submitted");
|
||
});
|
||
});
|
||
|
||
describe("非法状态转移(应保持原状态)", () => {
|
||
it("NotStarted 时 beginAutoSave 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.beginAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("NotStarted");
|
||
});
|
||
|
||
it("NotStarted 时 endAutoSave 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.endAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("NotStarted");
|
||
});
|
||
|
||
it("NotStarted 时 beginSubmit 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.beginSubmit();
|
||
});
|
||
expect(result.current.state).toBe("NotStarted");
|
||
});
|
||
|
||
it("NotStarted 时 completeSubmit 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.completeSubmit();
|
||
});
|
||
expect(result.current.state).toBe("NotStarted");
|
||
});
|
||
|
||
it("InProgress 时 start 应无效果(不能重复 start)", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
act(() => {
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
});
|
||
|
||
it("InProgress 时 endAutoSave 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.endAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
});
|
||
|
||
it("InProgress 时 completeSubmit 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.completeSubmit();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
});
|
||
|
||
it("AutoSaving 时 start 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("AutoSaving");
|
||
});
|
||
|
||
it("AutoSaving 时 endAutoSave 后再 endAutoSave 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
result.current.actions.endAutoSave();
|
||
result.current.actions.endAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("InProgress");
|
||
});
|
||
|
||
it("Submitting 时 start 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("Submitting");
|
||
});
|
||
|
||
it("Submitting 时 beginAutoSave 应无效果", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.beginAutoSave();
|
||
});
|
||
expect(result.current.state).toBe("Submitting");
|
||
});
|
||
|
||
it("Submitted 时 start 应无效果(终态)", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.completeSubmit();
|
||
result.current.actions.start();
|
||
});
|
||
expect(result.current.state).toBe("Submitted");
|
||
});
|
||
|
||
it("Submitted 时 beginSubmit 应无效果(终态)", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.completeSubmit();
|
||
result.current.actions.beginSubmit();
|
||
});
|
||
expect(result.current.state).toBe("Submitted");
|
||
});
|
||
});
|
||
|
||
describe("canSubmit 状态", () => {
|
||
it("NotStarted 时 canSubmit=false", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
|
||
it("InProgress 时 canSubmit=true", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => result.current.actions.start());
|
||
expect(result.current.canSubmit).toBe(true);
|
||
});
|
||
|
||
it("AutoSaving 时 canSubmit=true", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginAutoSave();
|
||
});
|
||
expect(result.current.canSubmit).toBe(true);
|
||
});
|
||
|
||
it("Submitting 时 canSubmit=false", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
});
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
|
||
it("Submitted 时 canSubmit=false", () => {
|
||
const { result } = renderHook(() => useExamStateMachine());
|
||
act(() => {
|
||
result.current.actions.start();
|
||
result.current.actions.beginSubmit();
|
||
result.current.actions.completeSubmit();
|
||
});
|
||
expect(result.current.canSubmit).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe("actions 引用稳定性", () => {
|
||
it("actions 对象应在多次渲染间保持引用稳定", () => {
|
||
const { result, rerender } = renderHook(() => useExamStateMachine());
|
||
const firstActions = result.current.actions;
|
||
rerender();
|
||
expect(result.current.actions).toBe(firstActions);
|
||
});
|
||
});
|
||
});
|