refactor: 菜单栏角色化降噪(role-based-navigation-simplification)

这个提交包含在:
2026-05-29 21:11:32 +08:00
父节点 325a7950c9
当前提交 541593a5b6
共修改 58 个文件,包含 796 行新增和 366 行删除
@@ -135,7 +135,7 @@ async function mockApis(page: Page, ledgerQueries: string[]) {
sections: {
main: [navigationItem("portal", "/portal", "工作台", "home_app_logo")],
admin: [],
backup: [navigationItem("ap", "/ap-operations", "AP 运营", "account_balance_wallet")],
backup: [navigationItem("ap", "/ap-operations", "应付账款", "account_balance_wallet")],
},
});
return;
@@ -508,7 +508,7 @@ async function mockApis(page: Page, role: RolePreset, notifications: MockNotific
main: [
navigationItem("portal", "/portal", "工作台", "home"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("leave", "/leave-requests", "请假", "event_available"),
navigationItem("leave", "/leave-requests", "请假记录", "event_available"),
],
admin: [],
backup: [],
@@ -938,7 +938,7 @@ test.describe("MIF7 移动信息架构整改", () => {
await loginForMobile(mobile.page, "manager");
await mobile.page.goto("/mobile");
await expect(mobile.page.getByTestId("mobile-nav-approvals-label")).toHaveText("审批");
await expect(mobile.page.getByTestId("mobile-nav-approvals-label")).toHaveText("待办");
await expect(mobile.page.getByTestId("mobile-pending-summary")).toHaveCount(1);
await expect(mobile.page.getByTestId("mobile-notifications")).not.toContainText("报销待审批");
await expect(mobile.page.getByTestId("mobile-action-approvals")).toHaveCount(0);
@@ -31,7 +31,7 @@ export class AppShell {
}
async assertMainNavVisible() {
await expect(this.page.getByRole("link", { name: "工作台" }).or(this.page.getByRole("link", { name: "Portal" })).first()).toBeVisible();
await expect(this.page.getByRole("link", { name: "工作台" }).first()).toBeVisible();
await expect(this.page.getByRole("link", { name: /项目/ }).first()).toBeVisible();
}
+2 -10
查看文件
@@ -23,11 +23,7 @@ type Envelope<T> = {
export async function assertPortalLoaded(page: Page) {
await expect(page).toHaveURL(/\/portal/);
await expect(
page.getByRole("link", { name: "工作台", exact: true })
.or(page.getByRole("link", { name: "Portal", exact: true }))
.first(),
).toBeVisible();
await expect(page.getByRole("link", { name: "工作台", exact: true }).first()).toBeVisible();
await expect(page.getByRole("heading", { name: "今日任务" })).toBeVisible();
}
@@ -37,11 +33,7 @@ export async function assertForbiddenPage(page: Page) {
}
export async function assertMainNavVisible(page: Page) {
await expect(
page.getByRole("link", { name: "工作台", exact: true })
.or(page.getByRole("link", { name: "Portal", exact: true }))
.first(),
).toBeVisible();
await expect(page.getByRole("link", { name: "工作台", exact: true }).first()).toBeVisible();
await expect(page.getByRole("link", { name: "团队" }).first()).toBeVisible();
await expect(page.getByRole("link", { name: "项目" }).first()).toBeVisible();
}
+1 -1
查看文件
@@ -58,7 +58,7 @@ test("E2E 轻交互 OCR:票据中心缺关键字段后可在当前页修正并
await login(page, EMPLOYEE);
await (await openNavLink(page, "票据中心")).click();
await (await openNavLink(page, "票据")).click();
await expect(page.getByTestId("invoice-pool-table")).toBeVisible();
await page.getByTestId("invoice-upload-file").setInputFiles({
+3 -3
查看文件
@@ -81,7 +81,7 @@ test("E2E 请假编辑:草稿保存后保留原单并可继续提交", async (
try {
await login(page, EMPLOYEE);
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leaveId = await createLeaveDraft(page, startDate, startDate, `Playwright 编辑请假 ${Date.now()}`);
@@ -125,7 +125,7 @@ test("E2E 请假主链路:草稿 → 提交 → 撤回 + 提交 → 审批", a
try {
await login(page, EMPLOYEE);
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const firstLeaveId = await createLeaveDraft(page, firstDate, firstDate, `Playwright 撤回请假 ${Date.now()}`);
@@ -177,7 +177,7 @@ test("E2E 请假退回说明:审批退回后员工可看到处理意见", asyn
try {
await login(page, EMPLOYEE);
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leaveId = await createLeaveDraft(page, leaveDate, leaveDate, `Playwright 退回请假 ${Date.now()}`);
+1 -1
查看文件
@@ -89,7 +89,7 @@ test.describe("live smoke", () => {
await expect(page.getByRole("heading", { name: "工作区设置", exact: true })).toBeVisible();
await page.goto("/system/feature-flags");
await expect(page.getByRole("heading", { name: "功能开关", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "功能管理", exact: true })).toBeVisible();
await page.goto("/audit-logs");
await expect(page.getByRole("heading", { name: "审计工作台", exact: true })).toBeVisible();
+17 -24
查看文件
@@ -181,43 +181,36 @@ function mockNavigation(page: import("@playwright/test").Page, mode: "employee"
const sections = mode === "manager"
? {
main: [
{ key: "portal", href: "/portal", label: "Portal", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "team", href: "/team", label: "团队", icon: "groups", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "leave", href: "/leave-requests", label: "请假", icon: "event_available", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "portal", href: "/portal", label: "工作台", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "workflow", href: "/workflow/tasks", label: "待我处理", icon: "fact_check", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "expense", href: "/reimbursements", label: "报销", icon: "request_quote", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "assets", href: "/assets", label: "资产", icon: "inventory_2", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "weekly", href: "/weekly-reports", label: "周报", icon: "article", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "todos", href: "/todos", label: "待办", icon: "check_circle", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "announcements", href: "/announcements", label: "公告", icon: "campaign", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "projects", href: "/projects", label: "项目", icon: "workspaces", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "team", href: "/team", label: "团队", icon: "groups", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "leave", href: "/leave-requests", label: "请假记录", icon: "event_available", placement: "main", permissionCodes: [], featureFlagKey: null },
],
admin: [
{ key: "workspace", href: "/system/workspace", label: "工作区设置", icon: "tune", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "people", href: "/people", label: "人员管理", icon: "group", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "workflow", href: "/system/workflow-definitions", label: "流程定义", icon: "schema", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "settings", href: "/system/settings", label: "系统设置", icon: "settings", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "feature-flags", href: "/system/feature-flags", label: "功能开关", icon: "toggle_on", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "audit", href: "/audit-logs", label: "审计日志", icon: "manage_search", placement: "admin", permissionCodes: [], featureFlagKey: null },
{ key: "settings", href: "/system/settings", label: "管理", icon: "settings", placement: "admin", permissionCodes: [], featureFlagKey: null },
],
backup: [
{ key: "assets", href: "/assets", label: "资产", icon: "inventory_2", placement: "backup", permissionCodes: [], featureFlagKey: null },
{ key: "weekly", href: "/weekly-reports", label: "周报", icon: "article", placement: "backup", permissionCodes: [], featureFlagKey: null },
{ key: "announcements", href: "/announcements", label: "公告", icon: "campaign", placement: "backup", permissionCodes: [], featureFlagKey: null },
{ key: "purchase", href: "/purchase-requests", label: "采购申请", icon: "shopping_cart", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "travel", href: "/travel-requests", label: "差旅申请", icon: "flight", placement: "main", permissionCodes: [], featureFlagKey: null },
],
}
: {
main: [
{ key: "portal", href: "/portal", label: "Portal", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "team", href: "/team", label: "团队", icon: "groups", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "leave", href: "/leave-requests", label: "请假", icon: "event_available", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "portal", href: "/portal", label: "工作台", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "expense", href: "/reimbursements", label: "报销", icon: "request_quote", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "assets", href: "/assets", label: "资产", icon: "inventory_2", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "weekly", href: "/weekly-reports", label: "周报", icon: "article", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "todos", href: "/todos", label: "待办", icon: "check_circle", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "announcements", href: "/announcements", label: "公告", icon: "campaign", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "projects", href: "/projects", label: "项目", icon: "workspaces", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "leave", href: "/leave-requests", label: "请假记录", icon: "event_available", placement: "main", permissionCodes: [], featureFlagKey: null },
],
admin: [],
backup: [],
backup: [
{ key: "assets", href: "/assets", label: "资产", icon: "inventory_2", placement: "backup", permissionCodes: [], featureFlagKey: null },
{ key: "weekly", href: "/weekly-reports", label: "周报", icon: "article", placement: "backup", permissionCodes: [], featureFlagKey: null },
{ key: "announcements", href: "/announcements", label: "公告", icon: "campaign", placement: "backup", permissionCodes: [], featureFlagKey: null },
],
};
await route.fulfill({
@@ -266,7 +259,7 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
workspace: { id: "workspace-1", persisted: true, profile: "small_team_internal" },
greeting: { name: "Manager" },
cards: [
{ key: "pending_approvals", title: "审批待办", value: 3, href: "/workflow/tasks", severity: "warning" },
{ key: "pending_approvals", title: "待我处理", value: 3, href: "/workflow/tasks", severity: "warning" },
{ key: "team_presence", title: "今日团队状态", value: "6/8 在岗", href: "/team", severity: "warning" },
{ key: "weekly_missing", title: "未提交周报", value: 2, href: "/weekly-reports", severity: "warning" },
{ key: "weekly_help", title: "需协助员工", value: 1, href: "/weekly-reports", severity: "high" },
@@ -281,7 +274,7 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
{ domain: "leave", count: 0, href: "/leave-requests" },
],
riskSummary: { openCount: 1, href: "/ap-operations" },
announcements: [{ id: "announcement-1", title: "试点公告", summary: "今天统一从 Portal 进入。", href: null }],
announcements: [{ id: "announcement-1", title: "试点公告", summary: "今天统一从工作台进入。", href: null }],
quickLinks: [{ key: "projects", title: "项目", href: "/projects" }],
todayPresence: null,
teamPresence: { date: "2026-03-22", href: "/team", office: 6, remote: 1, out: 0, travel: 1, leave: 1, unspecified: 1, total: 8 },
+1 -1
查看文件
@@ -28,7 +28,7 @@ test("E2E 主链路:登录→报销→待办审批→票据OCR→支付→登
await reimbursementDetail.assertLoaded();
// 2) employee: upload invoice and wait OCR
await (await openNavLink(page, "票据中心")).click();
await (await openNavLink(page, "票据")).click();
const marker = `MOCK_E2E_${Date.now()}_${Math.random().toString(16).slice(2)}`;
const invoiceFileName = `e2e-${Date.now()}.pdf`;
const uploadResponsePromise = page.waitForResponse((response) => {
@@ -187,7 +187,7 @@ test("R70 自动辅助试运行:员工、经理、管理员路径巡检", asyn
});
await runStep("employee", "请假提交并生成经理待办", async () => {
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leave = await createAndSubmitLeaveForManager(page);
await page.goto("/leave-requests");
@@ -204,7 +204,7 @@ test("R70 自动辅助试运行:员工、经理、管理员路径巡检", asyn
await runStep("employee", "周报与待办入口", async () => {
await (await openNavLink(page, "周报")).click();
await expect(page.getByRole("heading", { name: "周报", exact: true })).toBeVisible();
await (await openNavLink(page, "待办")).click();
await page.goto("/todos");
await expect(page.getByRole("heading", { name: "待办", exact: true })).toBeVisible();
});
@@ -245,18 +245,20 @@ test("R70 自动辅助试运行:员工、经理、管理员路径巡检", asyn
await runStep("admin", "管理入口与人员组织", async () => {
await logout(page, MANAGER.name);
await login(page, ADMIN);
await (await openNavLink(page, "人员管理")).click();
await (await openNavLink(page, /^管理$/)).click();
await expect(page.getByRole("heading", { name: "管理", exact: true })).toBeVisible();
await page.getByRole("link", { name: /人员/ }).first().click();
await expect(page.getByRole("heading", { name: "人员管理" })).toBeVisible();
await (await openNavLink(page, "组织架构")).click();
await page.goto("/organization");
await expect(page.getByRole("heading", { name: "组织架构" })).toBeVisible();
});
await runStep("admin", "系统设置、功能开关、审计日志", async () => {
await (await openNavLink(page, "系统设置")).click();
await expect(page.getByRole("heading", { name: "系统设置" })).toBeVisible();
await (await openNavLink(page, "功能开关")).click();
await expect(page.getByRole("heading", { name: "功能开关" })).toBeVisible();
await (await openNavLink(page, "审计日志")).click();
await runStep("admin", "管理、功能管理、审计日志", async () => {
await (await openNavLink(page, /^管理$/)).click();
await expect(page.getByRole("heading", { name: "管理", exact: true })).toBeVisible();
await page.getByRole("link", { name: /功能管理/ }).first().click();
await expect(page.getByRole("heading", { name: "功能管理" })).toBeVisible();
await page.goto("/audit-logs");
await expect(page.getByRole("heading", { name: "审计工作台" })).toBeVisible();
});
} finally {
@@ -173,7 +173,7 @@ test("R70 深层自动辅助试运行:周报待办、公告团队状态、真
await submitResponse;
await expect(page.getByText("周报已提交")).toBeVisible();
await (await openNavLink(page, "待办")).click();
await page.goto("/todos");
const todoRow = page.getByTestId(TEST_IDS.todo.row).filter({ hasText: uniqueToken }).first();
await expect(todoRow).toBeVisible({ timeout: 30_000 });
await todoRow.getByTestId(TEST_IDS.todo.complete).click();
@@ -778,7 +778,7 @@ test(`${trialrunIssueId} 浏览器业务先行试运行:报销、审批、请
const startDate = futureWeekday(uniqueFutureOffset(5200));
const employee = await loginRole(page, "employee");
try {
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leaveId = await createLeaveDraft(page, startDate, `${trialrunMarkerPrefix} 审批边界 ${marker}`);
@@ -952,7 +952,7 @@ test(`${trialrunIssueId} 浏览器业务先行试运行:报销、审批、请
const startDate = futureWeekday(uniqueFutureOffset(3200));
const employee = await loginRole(page, "employee");
try {
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leaveId = await createLeaveDraft(page, startDate, `${trialrunMarkerPrefix} 浏览器请假 ${marker}`);
@@ -178,7 +178,7 @@ async function mockWave2Apis(page: Page) {
sections: {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("workflow", "/workflow/tasks", "审批待办", "fact_check"),
navigationItem("workflow", "/workflow/tasks", "待我处理", "fact_check"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
],
admin: [],
@@ -206,7 +206,7 @@ async function mockWave2Apis(page: Page) {
workspace: { id: "workspace-wave2", persisted: true, profile: "small_team_internal" },
greeting: { name: visualMe.name },
cards: [
{ key: "pending_approvals", title: "审批待办", value: 3, href: "/workflow/tasks", severity: "warning" },
{ key: "pending_approvals", title: "待我处理", value: 3, href: "/workflow/tasks", severity: "warning" },
{ key: "team_presence", title: "团队状态", value: "7/9 在岗", href: "/team", severity: "normal" },
{ key: "weekly_missing", title: "周报缺口", value: 1, href: "/weekly-reports", severity: "warning" },
{ key: "weekly_help", title: "需协助", value: 1, href: "/weekly-reports", severity: "normal" },
@@ -583,7 +583,7 @@ async function mockWave3Apis(page: Page) {
sections: {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("workflow", "/workflow/tasks", "审批待办", "fact_check"),
navigationItem("workflow", "/workflow/tasks", "待我处理", "fact_check"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("notifications", "/notifications", "通知", "notifications"),
],
@@ -1120,7 +1120,7 @@ test("Wave3 移动关键页视觉截图", async ({ page }) => {
await page.screenshot({ path: screenshotPath("06-mobile-reimbursement-detail.png"), fullPage: true });
await page.goto("/mobile/approvals");
await expect(page.getByRole("heading", { name: "审批", exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "待办", exact: true })).toBeVisible();
await expect(page.getByTestId("mobile-approvals-focus")).toBeVisible();
await expect(page.getByTestId("mobile-approvals-list")).toBeVisible();
await expect(page.locator("table")).toHaveCount(0);
@@ -515,22 +515,12 @@ async function mockWave4Apis(page: Page, options: Wave4MockOptions = {}) {
sections: {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("payment-batches", "/payment-batches", "付款批次", "payments"),
navigationItem("ap-operations", "/ap-operations", "AP 运营", "account_balance_wallet"),
navigationItem("invoice-pool", "/invoice-pool", "票据中心", "receipt_long"),
navigationItem("payment-batches", "/payment-batches", "付款", "payments"),
navigationItem("ap-operations", "/ap-operations", "应付账款", "account_balance_wallet"),
navigationItem("invoice-pool", "/invoice-pool", "票据", "receipt_long"),
],
admin: [
navigationItem("people", "/people", "人员管理", "badge", "admin"),
navigationItem("organization", "/organization", "组织架构", "account_tree", "admin"),
navigationItem("workspace", "/system/workspace", "工作区设置", "dashboard_customize", "admin"),
navigationItem("users", "/system/users", "用户管理", "group", "admin"),
navigationItem("master-data", "/system/master-data", "主数据", "database", "admin"),
navigationItem("notification-channels", "/system/notification-channels", "通知渠道", "notifications", "admin"),
navigationItem("approval-rules", "/system/approval-rules", "审批规则", "account_tree", "admin"),
navigationItem("settings", "/system/settings", "系统设置", "settings", "admin"),
navigationItem("feature-flags", "/system/feature-flags", "功能开关", "toggle_on", "admin"),
navigationItem("workflow-definitions", "/system/workflow-definitions", "流程定义", "schema", "admin"),
navigationItem("audit-logs", "/audit-logs", "审计日志", "manage_search", "admin"),
navigationItem("settings", "/system/settings", "管理", "settings", "admin"),
],
backup: [],
},
@@ -1020,7 +1010,7 @@ const pages = [
{ path: "/system/users", summary: "users-summary", proof: "data-testid=users-table", screenshot: "08-users" },
{ path: "/system/master-data", summary: "master-data-summary", proof: "text=法体列表", screenshot: "09-master-data" },
{ path: "/system/notification-channels", summary: "notification-channels-summary", proof: "data-testid=notification-channels-table", screenshot: "10-notification-channels" },
{ path: "/system/settings", summary: "system-settings-summary", proof: "text=系统设置", screenshot: "11-system-settings" },
{ path: "/system/settings", summary: "system-settings-summary", proof: "text=管理", screenshot: "11-system-settings" },
{ path: "/system/feature-flags", summary: "feature-flags-summary", proof: "data-testid=feature-flags-table", screenshot: "12-feature-flags" },
{ path: "/system/workflow-definitions", summary: "workflow-definitions-summary", proof: "text=版本列表", screenshot: "13-workflow-definitions" },
{ path: "/audit-logs", summary: "audit-summary", proof: "text=PAYMENT_BATCH_REVIEW", screenshot: "14-audit-logs" },
@@ -244,7 +244,7 @@ async function expectMobileApprovalEntry(browser: Browser, user: E2EUser, canApp
const approvalTab = page.getByTestId("mobile-nav-approvals");
if (canApprove) {
await expect(approvalTab).toHaveAttribute("href", "/mobile/approvals");
await expect(page.getByTestId("mobile-nav-approvals-label")).toHaveText("审批");
await expect(page.getByTestId("mobile-nav-approvals-label")).toHaveText("待办");
await expect(page.getByTestId("mobile-action-grid").locator('a[href="/mobile/approvals"]')).toHaveCount(0);
await page.goto("/mobile/approvals");
await expect(page).not.toHaveURL(/\/403/);
@@ -97,7 +97,7 @@ async function mockDesignApis(page: Page) {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("leave", "/leave-requests", "请假", "event_available"),
navigationItem("leave", "/leave-requests", "请假记录", "event_available"),
navigationItem("invoice", "/invoice-pool", "票据", "receipt_long"),
],
admin: [],
@@ -165,7 +165,7 @@ async function mockMobileApis(page: Page) {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("leave", "/leave-requests", "请假", "event_available"),
navigationItem("leave", "/leave-requests", "请假记录", "event_available"),
navigationItem("invoice", "/invoice-pool", "票据", "receipt_long"),
],
admin: [],
@@ -174,7 +174,7 @@ async function mockApis(page: Page) {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("leave", "/leave-requests", "请假", "event_available"),
navigationItem("leave", "/leave-requests", "请假记录", "event_available"),
],
admin: [],
backup: [navigationItem("invoice", "/invoice-pool", "票据", "receipt_long")],
@@ -224,11 +224,11 @@ async function mockApis(page: Page) {
main: [
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("purchase", "/purchase-requests", "采购申请", "shopping_cart"),
navigationItem("invoice", "/invoice-pool", "票据中心", "receipt_long"),
navigationItem("invoice", "/invoice-pool", "票据", "receipt_long"),
],
admin: [],
backup: [
navigationItem("payment", "/payment-batches", "付款批次", "payments"),
navigationItem("payment", "/payment-batches", "付款", "payments"),
navigationItem("dashboard", "/dashboard", "数据看板", "monitoring"),
],
},
@@ -524,7 +524,7 @@ test.describe("R124 企业微信移动入口矩阵", () => {
await mobile.page.getByTestId("mobile-nav-approvals").click();
await expect(mobile.page).toHaveURL(/\/mobile\/approvals$/);
await expect(mobile.page.getByText("审批").first()).toBeVisible();
await expect(mobile.page.getByRole("heading", { name: "待办", exact: true })).toBeVisible();
await expect(mobile.page.locator(".mobile-status-slot--approval").first()).toContainText("待处理");
await expectMobilePageContract(mobile.page);
@@ -538,7 +538,7 @@ test.describe("R6 移动响应式体系", () => {
await expect(page.getByTestId("mobile-action-grid")).toBeVisible();
const homeMetrics = await expectMobilePageStable(page, device.label, `${device.key}-mobile-home`);
expect(homeMetrics.actionItems.length, `${device.label} home action count`).toBeGreaterThanOrEqual(2);
const bottomTabTexts = new Set(["首页", "审批", "进度", "报销", "我的"]);
const bottomTabTexts = new Set(["首页", "待办", "进度", "报销", "我的"]);
for (const action of homeMetrics.actionItems) {
expect(bottomTabTexts.has(action.text), `${device.label} ${action.text} not duplicate bottom tab`).toBe(false);
expect(action.width, `${device.label} ${action.text} action width`).toBeGreaterThanOrEqual(44);
@@ -527,7 +527,7 @@ async function exerciseMobileRendering(page: Page, key: string, label: string) {
await expect(page.getByTestId("mobile-action-grid")).toBeVisible();
const homeMetrics = await expectRenderingStable(page, label, `${key}-mobile-home`);
expect(homeMetrics.actionItems.length, `${label} action count`).toBeGreaterThanOrEqual(2);
const bottomTabLabels = new Set(["首页", "审批", "进度", "报销", "我的"]);
const bottomTabLabels = new Set(["首页", "待办", "进度", "报销", "我的"]);
for (const action of homeMetrics.actionItems) {
expect(action.visible, `${label} ${action.key} action visible`).toBe(true);
expect(bottomTabLabels.has(action.key), `${label} ${action.key} not bottom tab key`).toBe(false);
@@ -387,7 +387,7 @@ test.describe("R75 自动化补盲", () => {
await login(page, ADMIN);
await page.goto("/system/feature-flags");
await expect(page.getByRole("heading", { name: "功能开关" })).toBeVisible();
await expect(page.getByRole("heading", { name: "功能管理" })).toBeVisible();
const row = page.getByTestId(`feature-flag-row-${flagKey}`);
await expect(row).toBeVisible();
@@ -210,12 +210,12 @@ async function mockR89Apis(page: Page) {
},
sections: {
main: [
navigationItem("portal", "/portal", "Portal", "home_app_logo"),
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("team", "/team", "团队", "groups"),
navigationItem("leave", "/leave-requests", "请假", "event_available"),
navigationItem("leave", "/leave-requests", "请假记录", "event_available"),
navigationItem("expense", "/reimbursements", "报销", "request_quote"),
navigationItem("workflow", "/workflow/tasks", "审批待办", "fact_check"),
navigationItem("invoice", "/invoice-pool", "票据中心", "receipt_long"),
navigationItem("workflow", "/workflow/tasks", "待我处理", "fact_check"),
navigationItem("invoice", "/invoice-pool", "票据", "receipt_long"),
],
admin: [],
backup: [navigationItem("settings", "/settings/profile", "个人设置", "manage_accounts")],
@@ -96,10 +96,10 @@ async function mockAuthenticatedShell(page: Page) {
},
sections: {
main: [
{ key: "portal", href: "/portal", label: "Portal", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "portal", href: "/portal", label: "工作台", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "mobile", href: "/mobile", label: "移动工作台", icon: "phone_iphone", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "expense", href: "/reimbursements", label: "我的报销", icon: "request_quote", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "invoice", href: "/invoice-pool", label: "票据中心", icon: "receipt_long", placement: "main", permissionCodes: [], featureFlagKey: null },
{ key: "invoice", href: "/invoice-pool", label: "票据", icon: "receipt_long", placement: "main", permissionCodes: [], featureFlagKey: null },
],
admin: [],
backup: [],
+11 -11
查看文件
@@ -2,25 +2,25 @@ import { test, expect } from "./helpers/fixtures";
import { ADMIN, login } from "./helpers/auth";
import { TEST_IDS } from "./helpers/testIds";
test("E2E 管理域 smoke:工作区设置、系统设置、功能开关、流程定义、审计日志", async ({ page, shell }) => {
test("E2E 管理域 smoke:工作区设置、管理、功能管理、流程定义、审计日志", async ({ page, shell }) => {
await login(page, ADMIN);
await page.goto("/portal");
await expect(shell.topbarProfileTrigger).toBeVisible();
await shell.assertBackupModulesCollapsed();
await (await shell.navigate("工作区设置")).click();
await (await shell.navigate(/^管理$/)).click();
await expect(page).toHaveURL(/\/system\/settings/);
await expect(page.getByRole("heading", { name: "管理", exact: true })).toBeVisible();
await expect(page.getByRole("link", { name: /工作区/ }).first()).toBeVisible();
await page.goto("/system/workspace");
await expect(page).toHaveURL(/\/system\/workspace/);
await expect(page.getByRole("heading", { name: "工作区设置" })).toBeVisible();
await expect(page.getByText("产品模式")).toBeVisible();
await expect(page.getByText("工作区基线")).toBeVisible();
await (await shell.navigate("系统设置")).click();
await expect(page).toHaveURL(/\/system\/settings/);
await expect(page.getByRole("heading", { name: "系统设置" })).toBeVisible();
await expect(page.getByRole("link", { name: "工作区设置", exact: true }).first()).toBeVisible();
await (await shell.navigate("功能开关")).click();
await page.goto("/system/feature-flags");
await expect(page).toHaveURL(/\/system\/feature-flags/);
await expect(page.getByRole("heading", { name: "功能开关" })).toBeVisible();
await expect(page.getByRole("heading", { name: "功能管理" })).toBeVisible();
await expect(page.getByTestId("feature-flags-table")).toBeVisible();
await page.goto("/system/workflow-definitions");
@@ -28,7 +28,7 @@ test("E2E 管理域 smoke:工作区设置、系统设置、功能开关、流
await expect(page.getByRole("heading", { name: "流程定义" })).toBeVisible();
await expect(page.getByText("版本列表")).toBeVisible();
await (await shell.navigate("审计日志")).click();
await page.goto("/audit-logs");
await expect(page).toHaveURL(/\/audit-logs/);
await expect(page.getByRole("heading", { name: "审计工作台" })).toBeVisible();
await expect(page.getByTestId(TEST_IDS.audit.export)).toBeVisible();
@@ -209,7 +209,7 @@ async function mockSystemActionApis(page: Page) {
items: [
navigationItem("portal", "/portal", "首页", "home"),
navigationItem("reimbursements", "/reimbursements", "报销", "receipt"),
navigationItem("payment-batches", "/payment-batches", "付款批次", "credit_card"),
navigationItem("payment-batches", "/payment-batches", "付款", "credit_card"),
],
});
return;
+2 -2
查看文件
@@ -107,8 +107,8 @@ function mockCommonAuth(page: import("@playwright/test").Page, options: {
updatedBy: null,
},
sections: {
main: [{ key: "portal", href: "/portal", label: "Portal", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null }],
admin: [{ key: "users", href: "/system/users", label: "系统用户", icon: "manage_accounts", placement: "admin", permissionCodes: ["system.users"], featureFlagKey: null }],
main: [{ key: "portal", href: "/portal", label: "工作台", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null }],
admin: [{ key: "settings", href: "/system/settings", label: "管理", icon: "settings", placement: "admin", permissionCodes: ["system.users"], featureFlagKey: null }],
backup: [],
},
recommendedRolePresets: [],
+6 -7
查看文件
@@ -116,13 +116,12 @@ async function mockVisualApis(page: Page) {
},
sections: {
main: [
navigationItem("portal", "/portal", "Portal", "home_app_logo"),
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("reimbursements", "/reimbursements", "报销", "request_quote"),
navigationItem("approvals", "/workflow/tasks", "审批待办", "fact_check"),
navigationItem("invoice", "/invoice-pool", "票据中心", "receipt_long"),
navigationItem("approvals", "/workflow/tasks", "待我处理", "fact_check"),
],
admin: [],
backup: [navigationItem("settings", "/settings/profile", "个人设置", "manage_accounts")],
backup: [navigationItem("invoice", "/invoice-pool", "票据", "receipt_long")],
},
recommendedRolePresets: [],
});
@@ -323,7 +322,7 @@ test.describe("visual shell", () => {
await expect(page.locator("aside")).toBeVisible();
await expect(page.getByTestId("topbar-profile-trigger")).toBeVisible();
await expect(page.locator("aside").getByRole("link", { name: "Portal", exact: true })).toBeVisible();
await expect(page.locator("aside").getByRole("link", { name: "工作台", exact: true })).toBeVisible();
await expect(page.locator("summary").filter({ hasText: "更多" })).toBeVisible();
await attachScreenshot(page, testInfo, "app-shell.png");
@@ -364,7 +363,7 @@ test.describe("visual shell", () => {
await attachScreenshot(page, testInfo, "mobile-today.png");
});
test("审批待办手机卡片布局基线", async ({ page }, testInfo) => {
test("待我处理手机卡片布局基线", async ({ page }, testInfo) => {
await mockVisualApis(page);
await page.setViewportSize({ width: 390, height: 844 });
await loginVisualUser(page);
@@ -372,7 +371,7 @@ test.describe("visual shell", () => {
await expect(page.getByTestId("workflow-tasks-mobile-list")).toBeVisible();
await expect(page.getByTestId("workflow-tasks-table")).toBeHidden();
await expect(page.getByRole("link", { name: "详情" })).toBeVisible();
await expect(page.getByRole("link", { name: "查看" })).toBeVisible();
await attachScreenshot(page, testInfo, "r97-workflow-tasks-mobile.png");
});
+1 -1
查看文件
@@ -123,7 +123,7 @@ test("E2E 企微首次绑定:未绑定回退到密码登录后可继续进入
updatedBy: null,
},
sections: {
main: [{ key: "portal", href: "/portal", label: "Portal", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null }],
main: [{ key: "portal", href: "/portal", label: "工作台", icon: "home_app_logo", placement: "main", permissionCodes: [], featureFlagKey: null }],
admin: [],
backup: [],
},
+1 -2
查看文件
@@ -1,7 +1,6 @@
import { test, expect } from "./helpers/fixtures";
import { EMPLOYEE, login, logout } from "./helpers/auth";
import { openNavLink } from "./helpers/navigation";
import { TEST_IDS } from "./helpers/testIds";
function formatWeekStart(offsetDays: number) {
@@ -53,7 +52,7 @@ test("E2E 周报待办联动:保存 → 生成待办 → 提交 → 完成待
await expect(page.getByText("周报已提交")).toBeVisible();
await expect(page.getByText("已提交", { exact: true })).toBeVisible();
await (await openNavLink(page, "待办")).click();
await page.goto("/todos");
const todoRow = page.getByTestId(TEST_IDS.todo.row).filter({ hasText: uniqueToken }).first();
await expect(todoRow).toBeVisible({ timeout: 30_000 });
await todoRow.getByTestId(TEST_IDS.todo.complete).click();
@@ -187,8 +187,8 @@ async function mockWorkflowConfirmApis(page: Page) {
},
sections: {
main: [
navigationItem("portal", "/portal", "Portal", "home_app_logo"),
navigationItem("workflow", "/workflow/tasks", "审批待办", "fact_check"),
navigationItem("portal", "/portal", "工作台", "home_app_logo"),
navigationItem("workflow", "/workflow/tasks", "待我处理", "fact_check"),
],
admin: [],
backup: [],
@@ -73,7 +73,7 @@ test("E2E 审批列表快捷通过:经理可在待办列表行内直接通过
try {
await login(page, EMPLOYEE);
await (await openNavLink(page, "请假")).click();
await (await openNavLink(page, "请假记录")).click();
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
const leaveRequestId = await createLeaveDraft(page, startDate, startDate, reason);