R65 A1-A6:Session 稳定性与 409 资源错误修复
这个提交包含在:
@@ -12,6 +12,66 @@ function envelope(data: unknown) {
|
||||
};
|
||||
}
|
||||
|
||||
function mockPasswordLogin(page: import("@playwright/test").Page) {
|
||||
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: false,
|
||||
})),
|
||||
});
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
function mockNotificationList(page: import("@playwright/test").Page) {
|
||||
return page.route((url) => {
|
||||
return url.pathname === "/api/notifications";
|
||||
}, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(envelope({
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
total: 0,
|
||||
unreadOnly: true,
|
||||
unreadCount: 0,
|
||||
items: [],
|
||||
})),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mockMe(page: import("@playwright/test").Page, roleCodes: string[], name: string) {
|
||||
return page.route("**/api/me", async (route) => {
|
||||
await route.fulfill({
|
||||
@@ -211,9 +271,11 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
|
||||
}
|
||||
|
||||
test("Portal 员工视角优先显示今日动作顺序", async ({ page }) => {
|
||||
await mockPasswordLogin(page);
|
||||
await mockNavigation(page, "employee");
|
||||
await mockPortalHome(page, "employee");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["employee"], "Employee");
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
@@ -241,9 +303,11 @@ test("Portal 员工视角优先显示今日动作顺序", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("Portal 管理视角重排正确,备用模块默认折叠", async ({ page }) => {
|
||||
await mockPasswordLogin(page);
|
||||
await mockNavigation(page, "manager");
|
||||
await mockPortalHome(page, "manager");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["admin"], "Admin");
|
||||
|
||||
await login(page, ADMIN);
|
||||
@@ -274,25 +338,81 @@ test("Portal 新员工引导先显示欢迎弹窗,再显示快速上手清单"
|
||||
window.localStorage.removeItem("hyb_oa_getting_started_dismissed");
|
||||
});
|
||||
|
||||
await mockPasswordLogin(page);
|
||||
await mockNavigation(page, "employee");
|
||||
await mockPortalHome(page, "employee");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["employee"], "Employee");
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
|
||||
await expect(page.getByTestId(TEST_IDS.onboarding.welcomeDialog)).toBeVisible();
|
||||
await page.getByTestId(TEST_IDS.onboarding.welcomeClose).click();
|
||||
await page.getByTestId(TEST_IDS.onboarding.welcomeClose).evaluate((element: HTMLButtonElement) => {
|
||||
element.click();
|
||||
});
|
||||
await expect(page.getByTestId(TEST_IDS.onboarding.welcomeDialog)).not.toBeVisible();
|
||||
await expect(page.getByTestId(TEST_IDS.onboarding.gettingStartedCard)).toBeVisible();
|
||||
await page.getByTestId(TEST_IDS.onboarding.gettingStartedDismiss).click();
|
||||
await expect(page.getByTestId(TEST_IDS.onboarding.gettingStartedCard)).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("Portal bootstrap refresh 首次瞬时失败后重试成功仍保留登录态", async ({ page }) => {
|
||||
let refreshAttempts = 0;
|
||||
|
||||
await page.addInitScript(() => {
|
||||
document.cookie = "has_session=1; path=/";
|
||||
window.sessionStorage.removeItem("hyb_oa_chunk_reload_attempted");
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/refresh", async (route) => {
|
||||
refreshAttempts += 1;
|
||||
|
||||
if (refreshAttempts === 1) {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
requestId: "mock-request",
|
||||
error: {
|
||||
code: "SYS_001",
|
||||
message: "transient bootstrap error",
|
||||
details: {},
|
||||
},
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(envelope({
|
||||
accessToken: "bootstrap-token",
|
||||
expiresIn: 900,
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
await mockNavigation(page, "employee");
|
||||
await mockPortalHome(page, "employee");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["employee"], "Employee");
|
||||
|
||||
await page.goto("/portal");
|
||||
|
||||
await expect(page).toHaveURL(/\/portal/);
|
||||
await expect(page.locator('[aria-label^="portal-card-"]').first()).toBeVisible();
|
||||
expect(refreshAttempts).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
test("Portal 域管理角色也进入经理视图", async ({ page }) => {
|
||||
await mockPasswordLogin(page);
|
||||
await mockNavigation(page, "manager");
|
||||
await mockPortalHome(page, "manager");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["administration"], "Administration");
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
@@ -309,9 +429,11 @@ test("企微 WebView 下顶栏显示入口提示", async ({ page }) => {
|
||||
value: "Mozilla/5.0 wxwork/4.1.20 MicroMessenger/7.0.1",
|
||||
});
|
||||
});
|
||||
await mockPasswordLogin(page);
|
||||
await mockNavigation(page, "employee");
|
||||
await mockPortalHome(page, "employee");
|
||||
await mockAttendanceToday(page);
|
||||
await mockNotificationList(page);
|
||||
await mockMe(page, ["employee"], "Employee");
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
|
||||
在新工单中引用
屏蔽一个用户