refactor: 菜单栏角色化降噪(role-based-navigation-simplification)
这个提交包含在:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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()}`);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
在新工单中引用
屏蔽一个用户