Files
Alfred/tests/web/routes/projects.test.tsx
lanyuanxiaoyao 6cb378d7cb feat: Admin/Workbench 双入口架构
- 抽取 ConsoleShell 共享外壳(Layout/Header/Sider/主题切换/侧边栏折叠)
- Sidebar 纯化为接受 menuItems prop 的展示组件
- Admin 管理台:/ 总览 + /projects 项目管理
- Workbench 工作台:/workbench/:projectId 项目作用域
- WorkbenchProjectGate 入口守卫(loading/error/archived/不存在拦截)
- ProjectContext 提供当前项目上下文
- 项目管理表格 active 行增加'进入工作台'按钮
- 项目名称 trim 后最多 10 字符(前后端一致)
- Workbench 总览页展示项目 Descriptions
- Header 区分:管理台显示副标题,工作台显示项目名 + 返回管理台按钮
- 28/28 前端测试通过
- 文档更新:frontend.md ConsoleShell 规范、usage.md 双入口说明
2026-05-28 22:33:03 +08:00

115 lines
3.3 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 { screen, waitFor } from "@testing-library/react";
import { describe, expect, test } from "bun:test";
import { createElement } from "react";
import { App } from "../../../src/web/app";
import { renderWithProviders } from "../test-utils";
const ACTIVE_PROJECT = {
createdAt: "2024-01-01T00:00:00.000Z",
description: "",
id: "p1",
name: "活跃项目",
status: "active",
updatedAt: "2024-01-01T00:00:00.000Z",
};
const ARCHIVED_PROJECT = {
createdAt: "2024-01-01T00:00:00.000Z",
description: "",
id: "p2",
name: "归档项目",
status: "archived",
updatedAt: "2024-01-01T00:00:00.000Z",
};
function createMockHandler(projectList?: unknown[]) {
const handler = (input: RequestInfo | URL) => {
const url = input instanceof Request ? input.url : typeof input === "string" ? input : input.toString();
if (url.includes("/api/meta")) {
return new Response(
JSON.stringify({
ok: true,
service: "test-app",
timestamp: new Date().toISOString(),
version: "0.1.0",
}),
{ headers: { "Content-Type": "application/json" }, status: 200 },
);
}
if (url.includes("/api/projects")) {
const items = projectList ?? [];
return new Response(JSON.stringify({ items, page: 1, pageSize: 10, total: items.length }), {
headers: { "Content-Type": "application/json" },
status: 200,
});
}
return new Response(JSON.stringify({ error: "Not Found" }), {
status: 404,
});
};
const mocked = handler as unknown as typeof fetch;
globalThis.fetch = mocked;
window.fetch = mocked;
}
describe("ProjectsPage", () => {
test("渲染 Tab、搜索框、新建按钮和表格", async () => {
createMockHandler();
renderWithProviders(createElement(App), { initialRoute: "/projects" });
await waitFor(
() => {
expect(screen.getByText("进行中")).not.toBeNull();
},
{ timeout: 10000 },
);
expect(screen.getByText("已归档")).not.toBeNull();
expect(screen.getByText("新建项目")).not.toBeNull();
expect(screen.getByPlaceholderText("搜索项目名称或描述")).not.toBeNull();
});
test("新建按钮点击打开弹窗", async () => {
createMockHandler();
renderWithProviders(createElement(App), { initialRoute: "/projects" });
await waitFor(
() => {
expect(screen.getByText("进行中")).not.toBeNull();
},
{ timeout: 10000 },
);
const createBtn = screen.getByRole("button", { name: /新建项目/ });
createBtn.click();
await waitFor(
() => {
expect(document.body.querySelector(".ant-modal")).not.toBeNull();
},
{ timeout: 10000 },
);
});
test("active 项目行显示'进入工作台'archived 行不显示", async () => {
createMockHandler([ACTIVE_PROJECT, ARCHIVED_PROJECT]);
renderWithProviders(createElement(App), { initialRoute: "/projects" });
await waitFor(
() => {
expect(screen.queryByText("活跃项目")).not.toBeNull();
},
{ timeout: 10000 },
);
const enterBtns = screen.getAllByText("进入工作台");
expect(enterBtns.length).toBe(1);
const archivedRow = screen.getByText("归档项目").closest("tr");
expect(archivedRow?.textContent).not.toContain("进入工作台");
});
});