Initial commit
This commit is contained in:
commit
68d61700d5
252 files changed
+23291
No files matched your search
@@ -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();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user