Initial commit

This commit is contained in:
wangxuming committed 2026-07-12 15:53:24 +08:00
commit 68d61700d5
252 files changed
+23291

No files matched your search

+167
View File
@@ -0,0 +1,167 @@
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { MemoryRouter } from "react-router-dom";
vi.mock("../components/AppShell", () => ({
AppShell: ({ children, variant }: { children: React.ReactNode; variant?: string }) => (
<div data-testid="app-shell" data-variant={variant}>{children}</div>
),
}));
vi.mock("../hooks/usePollingResource", () => ({
usePollingResource: (_load: unknown, options: { intervalMs: number }) => options.intervalMs === 60_000
? {
data: { projects: [
{ id: "project-1", name: "东门观光车", status: "RUNNING", batch_size: 2 },
{ id: "project-2", name: "西门观光车", status: "RUNNING", batch_size: 2 },
] },
loading: false,
refreshing: false,
error: null,
offline: false,
lastClientSuccessAt: new Date().toISOString(),
refresh: vi.fn(),
}
: {
data: {
project: { id: "project-1", name: "东门观光车", status: "RUNNING", batch_size: 2 },
revision: 8,
waiting: [{
id: "waiting-1",
ticket_number: "00013",
phone: "13900139000",
last_name: "李",
honorific: "女士",
status: "WAITING",
created_at: "2026-07-10T09:02:00Z",
}],
current_batch: {
id: "batch-1",
batch_number: 4,
status: "ACTIVE",
called_at: "2026-07-10T09:01:00Z",
tickets: [{
id: "called-1",
ticket_number: "00012",
phone: "13800138000",
last_name: "张",
honorific: "先生",
status: "CALLED",
}],
},
metrics: {
waiting_count: 1,
estimated_wait: { available: true, estimate_minutes: 15, min_minutes: 15, max_minutes: 15 },
last_ticket_number: "00013",
next_ticket_number: "00014",
},
last_success_at: new Date().toISOString(),
},
loading: false,
refreshing: false,
error: null,
offline: false,
lastClientSuccessAt: new Date().toISOString(),
refresh: vi.fn(),
},
}));
vi.mock("../auth/AuthContext", () => ({
useAuth: () => ({ user: { id: "staff-1", username: "staff01", display_name: "小李", role: "STAFF" } }),
}));
import { StaffPage } from "./StaffPage";
import { api } from "../api";
function renderScene(path: string) {
return render(<MemoryRouter initialEntries={[path]}><StaffPage /></MemoryRouter>);
}
describe("StaffPage scene-focused H5", () => {
beforeEach(() => sessionStorage.setItem("scenic-current-project", "project-1"));
it("keeps the call scene focused on the active batch and the primary call action", () => {
renderScene("/staff");
expect(screen.getByTestId("app-shell")).toHaveAttribute("data-variant", "mobile");
expect(screen.getByRole("link", { name: "叫号" })).toHaveClass("active");
expect(screen.getByRole("link", { name: "我的" })).toBeVisible();
expect(screen.getByRole("region", { name: "叫号" })).toBeVisible();
expect(screen.queryByText("13800138000")).not.toBeInTheDocument();
expect(screen.queryByText("张先生")).not.toBeInTheDocument();
expect(screen.queryByRole("link", { name: "核验" })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "到场" })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "过号" })).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "快速叫下一个号" })).toBeEnabled();
expect(screen.getByRole("button", { name: "叫号段" })).toBeEnabled();
expect(screen.queryByText(/本次共叫/)).not.toBeInTheDocument();
expect(screen.queryByLabelText("等待队列表,可横向滚动")).not.toBeInTheDocument();
expect(screen.queryByRole("group", { name: "数据视图" })).not.toBeInTheDocument();
expect(screen.queryByText("演示")).not.toBeInTheDocument();
expect(screen.queryByText("实时")).not.toBeInTheDocument();
expect(screen.getByRole("region", { name: "叫号" })).toHaveTextContent("最末号预计时长约 15 分钟");
});
it("updates number indicators after creation without showing a success card", async () => {
vi.spyOn(api, "createTicket").mockResolvedValueOnce({
ticket: { id: "ticket-30", ticket_number: "00030", status: "WAITING" },
revision: 9,
});
renderScene("/staff/tickets");
expect(screen.getByRole("link", { name: "取号" })).toHaveClass("active");
expect(screen.getByRole("region", { name: "取号" })).toBeVisible();
expect(screen.getByLabelText("手机号")).toBeVisible();
expect(screen.getByRole("button", { name: "创建排队号码" })).toBeVisible();
expect(screen.getByRole("region", { name: "取号指标" })).toHaveTextContent("上个取号00013");
expect(screen.getByRole("region", { name: "取号指标" })).toHaveTextContent("下个取号00014");
expect(screen.queryByLabelText("等待队列表,可横向滚动")).not.toBeInTheDocument();
expect(screen.queryByText("叫下一批(1 个号码)")).not.toBeInTheDocument();
fireEvent.change(screen.getByLabelText("手机号"), { target: { value: "13800138000" } });
fireEvent.click(screen.getByRole("button", { name: "创建排队号码" }));
await waitFor(() => expect(screen.getByRole("region", { name: "取号指标" })).toHaveTextContent("上个取号00030"));
expect(screen.getByRole("region", { name: "取号指标" })).toHaveTextContent("下个取号00031");
expect(screen.queryByText("取号成功:00030")).not.toBeInTheDocument();
expect(screen.queryByText("打开游客状态页")).not.toBeInTheDocument();
});
it("only switches project after explicit confirmation", async () => {
renderScene("/staff/me");
expect(screen.getByRole("link", { name: "我的" })).toHaveClass("active");
expect(screen.getByRole("region", { name: "我的" })).toBeVisible();
expect(screen.getByText("小李")).toBeVisible();
expect(screen.getByText("staff01")).toBeVisible();
const switchButton = screen.getByRole("button", { name: "切换项目" });
expect(screen.queryByLabelText("选择项目")).not.toBeInTheDocument();
fireEvent.click(switchButton);
const dialog = screen.getByRole("dialog", { name: "切换项目" });
const projectOptions = screen.getByRole("group", { name: "选择项目" });
const currentProject = screen.getByRole("radio", { name: "东门观光车" });
const nextProject = screen.getByRole("radio", { name: "西门观光车" });
expect(dialog).toBeVisible();
expect(projectOptions).toBeVisible();
expect(currentProject).toBeChecked();
expect(screen.getByRole("button", { name: "确认切换" })).toBeDisabled();
fireEvent.click(nextProject);
expect(sessionStorage.getItem("scenic-current-project")).toBe("project-1");
expect(screen.getByRole("button", { name: "确认切换" })).toBeEnabled();
fireEvent.click(screen.getByRole("button", { name: "确认切换" }));
await waitFor(() => expect(sessionStorage.getItem("scenic-current-project")).toBe("project-2"));
expect(screen.queryByRole("dialog", { name: "切换项目" })).not.toBeInTheDocument();
});
it("closes the project switcher with Escape and returns focus", async () => {
renderScene("/staff/me");
const switchButton = screen.getByRole("button", { name: "切换项目" });
fireEvent.click(switchButton);
expect(screen.getByRole("dialog", { name: "切换项目" })).toBeVisible();
fireEvent.keyDown(document, { key: "Escape" });
await waitFor(() => expect(screen.queryByRole("dialog", { name: "切换项目" })).not.toBeInTheDocument());
expect(switchButton).toHaveFocus();
});
});