Files
XQKqueue/web/src/pages/AdminPage.test.tsx
2026-07-15 10:05:33 +08:00

224 lines
13 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.

import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { MemoryRouter } from "react-router-dom";
import { api } from "../api";
vi.mock("../components/AppShell", () => ({ AppShell: ({ children }: { children: React.ReactNode }) => <div>{children}</div> }));
vi.mock("../hooks/usePollingResource", () => ({
usePollingResource: (_loader: unknown, options: { intervalMs: number }) => ({
data: options.intervalMs === 30_000 ? {
users: [
{ id: "user-1", username: "staff01", role: "STAFF", active: true, project_ids: ["project-1"] },
{ id: "super-1", username: "xqkwljtadmin", role: "ADMIN", active: true, protected: true, project_ids: [] },
],
} : {
summary: { running_projects: 1, waiting_count: 1, anomaly_projects: 0, offline_devices: 0 },
projects: [{
id: "project-1",
code: "EAST-RIDE",
name: "东门观光车",
timezone: "Asia/Shanghai",
ticket_prefix: "A",
status: "RUNNING",
batch_size: 5,
call_batch_size: 5,
grace_period_minutes: 5,
visitor_notice: "请在入口附近等候。",
eta: { interval_per_number_seconds: 120 },
waiting_count: 1,
current_batch: {
id: "batch-1",
batch_number: 4,
status: "ACTIVE",
tickets: [
{ id: "ticket-2", ticket_number: "00013", phone: "13800138000", status: "CALLED" },
{ id: "ticket-3", ticket_number: "00014", phone: "13800138001", status: "CALLED" },
],
},
estimated_wait: { available: true, lower_minutes: 5, upper_minutes: 15 },
device_status: { label: "模拟设备执行成功" },
}],
active_tickets: [{
id: "ticket-1",
project_id: "project-1",
project_name: "东门观光车",
ticket_number: "00012",
phone: "13800138000",
last_name: "张",
honorific: "先生",
status: "WAITING",
created_at: "2026-07-10T09:00:00Z",
}],
},
loading: false,
refreshing: false,
error: null,
offline: false,
lastClientSuccessAt: new Date().toISOString(),
refresh: vi.fn(),
}),
}));
import { AdminPage } from "./AdminPage";
describe("AdminPage active tickets", () => {
it("保留核心排队数据并删除辅助概览模块", () => {
render(<MemoryRouter initialEntries={["/admin"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("list", { name: "项目队列" })).toHaveTextContent("东门观光车");
expect(screen.getByRole("list", { name: "项目队列" })).toHaveTextContent("00013 至 00014");
expect(screen.getByText("运行项目").parentElement).toHaveTextContent("1");
expect(screen.getByText("总等待号码").parentElement).toHaveTextContent("1");
expect(screen.queryByText("最近操作")).not.toBeInTheDocument();
expect(screen.queryByText("异常与提醒")).not.toBeInTheDocument();
expect(screen.queryByText("最新叫号")).not.toBeInTheDocument();
expect(screen.queryByText("异常项目")).not.toBeInTheDocument();
expect(screen.queryByText("离线设备")).not.toBeInTheDocument();
expect(screen.queryByRole("group", { name: "数据视图" })).not.toBeInTheDocument();
expect(screen.queryByText("演示视图")).not.toBeInTheDocument();
expect(screen.queryByText("实时数据")).not.toBeInTheDocument();
});
it("在项目管理中列出项目并提供创建与维护入口", () => {
render(<MemoryRouter initialEntries={["/admin/projects"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("heading", { name: "项目管理" })).toBeVisible();
expect(screen.getByRole("table", { name: "项目列表" })).toHaveTextContent("东门观光车");
expect(screen.getByRole("link", { name: "创建项目" })).toHaveAttribute("href", "/admin/projects/new");
expect(screen.getByRole("link", { name: "维护" })).toHaveAttribute("href", "/admin/projects/project-1");
expect(screen.queryByRole("form", { name: "规则配置" })).not.toBeInTheDocument();
});
it("将上线超级管理员显示为不可编辑", () => {
render(<MemoryRouter initialEntries={["/admin/accounts"]}><AdminPage /></MemoryRouter>);
const row = screen.getByText("xqkwljtadmin").closest('[role="row"]');
expect(row).toHaveTextContent("超级管理员");
expect(row).toHaveTextContent("不可编辑");
expect(row?.querySelector('a[href="/admin/accounts/super-1"]')).toBeNull();
});
it("在同一表单中维护项目全部字段", () => {
render(<MemoryRouter initialEntries={["/admin/projects/project-1"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("heading", { name: "东门观光车" })).toBeVisible();
expect(screen.queryByRole("heading", { name: "维护项目" })).not.toBeInTheDocument();
expect(screen.getByRole("form", { name: "维护项目" })).toBeVisible();
expect(screen.getByRole("form", { name: "维护项目" })).toHaveTextContent("项目名称");
expect(screen.getByRole("form", { name: "维护项目" })).toHaveTextContent("项目状态");
expect(screen.queryByText("基础信息")).not.toBeInTheDocument();
expect(screen.queryByText("规则配置")).not.toBeInTheDocument();
expect(screen.getByRole("combobox", { name: "票号格式" })).toHaveDisplayValue("00000");
expect(screen.queryByRole("textbox", { name: "时区" })).not.toBeInTheDocument();
expect(screen.queryByRole("textbox", { name: "票号前缀" })).not.toBeInTheDocument();
expect(screen.getByRole("spinbutton", { name: "每次叫号数量" })).toHaveValue(5);
expect(screen.getByRole("spinbutton", { name: "单个号码预计间隔时间(秒)" })).toHaveValue(120);
expect(screen.queryByRole("combobox", { name: "预计等待时间方式" })).not.toBeInTheDocument();
expect(screen.getByRole("textbox", { name: "游客官方提示" })).toHaveValue("请在入口附近等候。");
expect(screen.getByRole("button", { name: "保存项目" })).toBeVisible();
});
it("创建项目与维护项目使用相同字段", () => {
render(<MemoryRouter initialEntries={["/admin/projects/new"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("form", { name: "创建项目" })).toBeVisible();
expect(screen.getByRole("textbox", { name: "项目名称" })).toBeVisible();
expect(screen.getByRole("textbox", { name: "项目编码" })).toBeVisible();
expect(screen.getByRole("combobox", { name: "票号格式" })).toHaveDisplayValue("00000");
expect(screen.getByRole("combobox", { name: "项目状态" })).toHaveDisplayValue("未开放");
expect(screen.getByRole("spinbutton", { name: "每次叫号数量" })).toHaveValue(1);
expect(screen.getByRole("spinbutton", { name: "单个号码预计间隔时间(秒)" })).toHaveValue(60);
expect(screen.getByRole("textbox", { name: "游客官方提示" })).toHaveValue("请您在景区附近等候,注意听从工作人员指引。");
expect(screen.queryByRole("textbox", { name: "时区" })).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "创建项目" })).toBeVisible();
});
it("创建项目提交完整的默认配置", async () => {
const createdProject = { id: "project-2", code: "NEW-RIDE", name: "新项目", status: "NOT_OPEN", batch_size: 1, timezone: "Asia/Shanghai", ticket_prefix: "A" };
const createProject = vi.spyOn(api, "createProject").mockResolvedValue({ project: createdProject });
const updateProjectSettings = vi.spyOn(api, "updateProjectSettings").mockResolvedValue({ project: createdProject });
try {
render(<MemoryRouter initialEntries={["/admin/projects/new"]}><AdminPage /></MemoryRouter>);
fireEvent.change(screen.getByRole("textbox", { name: "项目名称" }), { target: { value: "新项目" } });
fireEvent.change(screen.getByRole("textbox", { name: "项目编码" }), { target: { value: "new-ride" } });
fireEvent.submit(screen.getByRole("form", { name: "创建项目" }));
await waitFor(() => expect(createProject).toHaveBeenCalledTimes(1));
expect(createProject).toHaveBeenCalledWith({ name: "新项目", code: "NEW-RIDE", timezone: "Asia/Shanghai", ticket_prefix: "A" });
expect(updateProjectSettings).toHaveBeenCalledWith("project-2", {
status: "NOT_OPEN",
call_batch_size: 1,
grace_period_minutes: 5,
eta_interval_seconds: 60,
visitor_notice: "请您在景区附近等候,注意听从工作人员指引。",
});
} finally {
vi.restoreAllMocks();
}
});
it("keeps the screen center free of contact details", () => {
render(<MemoryRouter initialEntries={["/admin/display"]}><AdminPage /></MemoryRouter>);
expect(screen.getAllByRole("button", { name: "全屏展示" }).length).toBeGreaterThan(0);
expect(screen.getByRole("region", { name: "项目实时监控墙" })).toBeVisible();
expect(screen.getByText("00013 至 00014")).toBeVisible();
expect(screen.queryByText("00013、00014")).not.toBeInTheDocument();
expect(screen.queryByText("13800138000")).not.toBeInTheDocument();
expect(screen.queryByText("全项目实时监控")).not.toBeInTheDocument();
expect(screen.queryByText("当前批次")).not.toBeInTheDocument();
expect(screen.queryByText("设备状态")).not.toBeInTheDocument();
expect(screen.queryByText("大屏中心只展示公开票号与运行状态,不展示游客联系方式。")).not.toBeInTheDocument();
expect(screen.getByLabelText(/当前时间/)).toBeInTheDocument();
expect(screen.queryByText("当前项目")).not.toBeInTheDocument();
expect(screen.getByRole("heading", { name: "东门观光车" })).toBeVisible();
expect(screen.getByRole("heading", { name: "最新取号码00015号" })).toBeInTheDocument();
expect(screen.getByLabelText("总计等待1 个号码")).toBeInTheDocument();
expect(screen.getByText("预计等待时间")).toBeVisible();
expect(screen.queryByText("后续号段预计")).not.toBeInTheDocument();
expect(screen.queryByText(/\d+ \/ \d+/)).not.toBeInTheDocument();
expect(screen.queryByText("运行中")).not.toBeInTheDocument();
});
it("先展示账号列表,不在列表页展示创建表单", () => {
render(<MemoryRouter initialEntries={["/admin/accounts"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("heading", { name: "账号管理" })).toBeVisible();
expect(screen.getByRole("table", { name: "账号列表" })).toBeVisible();
expect(screen.getByText("staff01")).toBeVisible();
expect(screen.getByRole("link", { name: "创建账号" })).toHaveAttribute("href", "/admin/accounts/new");
expect(screen.getByRole("link", { name: "维护" })).toHaveAttribute("href", "/admin/accounts/user-1");
expect(screen.queryByRole("combobox", { name: "角色" })).not.toBeInTheDocument();
expect(screen.queryByRole("form", { name: "创建账号" })).not.toBeInTheDocument();
});
it("点击创建后由独立路由维护新账号", () => {
render(<MemoryRouter initialEntries={["/admin/accounts/new"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("form", { name: "创建账号" })).toBeVisible();
expect(screen.getByRole("textbox", { name: "账号" })).toBeVisible();
expect(screen.getByLabelText("初始密码")).toBeVisible();
expect(screen.getByRole("link", { name: "取消" })).toHaveAttribute("href", "/admin/accounts");
expect(screen.queryByText("staff01")).not.toBeInTheDocument();
});
it("账号维护控件只在独立维护页展示", () => {
render(<MemoryRouter initialEntries={["/admin/accounts/user-1"]}><AdminPage /></MemoryRouter>);
expect(screen.getByRole("heading", { name: "维护账号" })).toBeVisible();
expect(screen.getByRole("radiogroup", { name: "角色" })).toBeVisible();
expect(screen.getByRole("radio", { name: "员工" })).toBeChecked();
expect(screen.getByRole("radio", { name: "管理员" })).not.toBeChecked();
expect(screen.getByRole("button", { name: "保存修改" })).toBeDisabled();
fireEvent.click(screen.getByRole("radio", { name: "管理员" }));
expect(screen.getByRole("radio", { name: "管理员" })).toBeChecked();
expect(screen.getByText("有未保存的修改")).toBeVisible();
expect(screen.getByRole("button", { name: "保存修改" })).toBeEnabled();
expect(screen.getByRole("button", { name: "放弃修改" })).toBeEnabled();
expect(screen.getByRole("group", { name: "所属项目" })).toBeVisible();
expect(screen.getByRole("checkbox", { name: /东门观光车/ })).toBeChecked();
expect(screen.getByText("已授权")).toBeVisible();
expect(screen.getByRole("link", { name: "返回列表" })).toHaveAttribute("href", "/admin/accounts");
expect(screen.queryByRole("table", { name: "账号列表" })).not.toBeInTheDocument();
});
});