Files
Edu/apps/student-portal/src/hooks/use-exam-state-machine.test.ts

282 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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);
});
});
});