import { expect, test } from "@playwright/test"; function envelope(data: unknown) { return { requestId: "mock-request", data, }; } function mockCommonAuth(page: import("@playwright/test").Page, options: { mustChangePassword?: boolean; meName?: string; meEmployeeNo?: string; }) { const mustChangePassword = options.mustChangePassword === true; const meName = options.meName ?? "Admin"; const meEmployeeNo = options.meEmployeeNo ?? "HYD000001"; return Promise.all([ page.route("**/api/auth/sso/providers", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope([])), }); }), page.route("**/api/auth/wecom/config", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ enabled: false, entryEnabled: false, oauthEnabled: false, corpId: "", agentId: "", loginUrl: null, bindEnabled: false, defaultReturnTo: "/portal", status: "disabled", statusReasons: ["disabled"], }), ), }); }), page.route("**/api/auth/login", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ accessToken: "test-access-token", expiresIn: 900, mustChangePassword, wecomBindingStatus: null, }), ), }); }), page.route("**/api/me", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ id: "employee-admin-1", employeeNo: meEmployeeNo, name: meName, phone: "13800000000", email: "admin@hyinda.com", departmentId: "dept-1", directManagerId: null, status: "active", notificationPushEnabled: true, roleCodes: ["admin"], permissions: ["system.users"], profileCompleteness: { hasEmergencyContact: true, hasBankAccount: true, hasAvatar: false, hasIdNumber: false, score: 50, }, wecomBinding: null, }), ), }); }), page.route("**/api/system/navigation", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ workspace: { id: "workspace-1", persisted: true, profile: "small_team_internal", orgMode: "single_company", primaryEntryMode: "password", notificationStrategy: "in_app", devicePriority: "desktop", moduleVisibility: {}, createdAt: null, updatedAt: null, 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 }], backup: [], }, recommendedRolePresets: [], }), ), }); }), page.route((url) => url.pathname === "/api/notifications", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ page: 1, pageSize: 10, total: 0, unreadOnly: true, unreadCount: 0, items: [], }), ), }); }), ]); } test("E2E 用户开通:新建用户时由系统生成工号和一次性口令", async ({ page }) => { let createPayload: Record | null = null; await mockCommonAuth(page, {}); await page.route("**/api/users?**", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ page: 1, pageSize: 20, total: 0, items: [], }), ), }); }); await page.route("**/api/departments?includeArchived=0", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ includeArchived: false, items: [{ id: "dept-1", name: "行政部", parentId: null, archivedAt: null, createdAt: null, updatedAt: null }], tree: [], }), ), }); }); await page.route("**/api/employees?page=1&pageSize=200&status=active", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ page: 1, pageSize: 200, total: 0, items: [], }), ), }); }); await page.route("**/api/users", async (route) => { if (route.request().method() !== "POST") { await route.fallback(); return; } createPayload = route.request().postDataJSON() as Record; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ id: "user-1", employeeNo: "HYD000123", name: "张三", phone: "13800000001", email: "zhangsan@example.com", status: "active", departmentId: "dept-1", title: "员工", roleCodes: ["employee"], createdAt: "2026-05-04T12:00:00.000Z", updatedAt: "2026-05-04T12:00:00.000Z", oneTimePassword: "ResetA1abc123def456", }), ), }); }); await page.goto("/login?redirect=%2Fsystem%2Fusers"); await page.getByTestId("login-identifier").fill("admin@hyinda.com"); await page.getByTestId("login-password").fill("AdminPassword1234"); await page.getByTestId("login-submit").click(); await expect(page).toHaveURL(/\/system\/users/); await page.getByTestId("users-create-open").click(); await expect(page.getByTestId("users-create-employeeNo")).toHaveCount(0); await expect(page.getByTestId("users-create-password")).toHaveCount(0); await page.getByTestId("users-create-name").fill("张三"); await page.getByTestId("users-create-phone").fill("13800000001"); await page.getByTestId("users-create-email").fill("zhangsan@example.com"); await page.getByTestId("users-create-submit").click(); expect(createPayload).toEqual({ name: "张三", phone: "13800000001", email: "zhangsan@example.com", departmentId: undefined, directManagerId: null, roles: ["employee"], }); await expect(page.getByTestId("users-reset-result")).toContainText("工号=HYD000123"); await expect(page.getByTestId("users-reset-result")).toContainText("ResetA1abc123def456"); }); test("E2E 首次登录:一次性口令登录后强制改密", async ({ page }) => { let changePasswordPayload: Record | null = null; await mockCommonAuth(page, { mustChangePassword: true, meName: "新员工", meEmployeeNo: "HYD000123" }); await page.route("**/api/auth/change-password", async (route) => { changePasswordPayload = route.request().postDataJSON() as Record; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ accessToken: "post-change-token", expiresIn: 900, mustChangePassword: false, }), ), }); }); await page.route("**/api/portal/home", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify( envelope({ workspace: { id: "workspace-1", persisted: true, profile: "small_team_internal" }, greeting: { name: "新员工" }, cards: [], pendingTasks: { count: 0, href: "/workflow/tasks" }, myApplications: [], riskSummary: null, announcements: [], quickLinks: [], todayPresence: null, teamPresence: null, weeklyReport: null, weeklySummary: null, todoSummary: { openCount: 0, highPriorityCount: 0, href: "/todos" }, projectSummary: { myCount: 0, overdueMilestonesCount: 0, highPriorityTaskCount: 0, openRiskCount: 0, followUpDueTodayCount: 0, closurePendingProjectCount: 0, href: "/projects" }, peopleLifecycle: null, adoptionSummary: null, recentActivities: [], teamStatusBoard: [], }), ), }); }); await page.goto("/login"); await page.getByTestId("login-identifier").fill("new.user@hyinda.com"); await page.getByTestId("login-password").fill("ResetA1abc123def456"); await page.getByTestId("login-submit").click(); await expect(page).toHaveURL(/\/change-password/); await page.getByTestId("change-password-old").fill("ResetA1abc123def456"); await page.getByTestId("change-password-new").fill("NewPassword1234"); await page.getByLabel("确认新密码").fill("NewPassword1234"); await page.getByTestId("change-password-submit").click(); expect(changePasswordPayload).toEqual({ oldPassword: "ResetA1abc123def456", newPassword: "NewPassword1234", }); await expect(page).toHaveURL(/\/portal/); });