R35-2 审批通知补结构化摘要
这个提交包含在:
@@ -0,0 +1,243 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
|
||||
import { EMPLOYEE, login, type E2EUser } from "./helpers/auth";
|
||||
|
||||
const LEAVE_CALENDAR_MANAGER: E2EUser = {
|
||||
identifier: "[email protected]",
|
||||
password: "HrPassword1234",
|
||||
name: "HR",
|
||||
};
|
||||
|
||||
function envelope<T>(data: T) {
|
||||
return {
|
||||
requestId: "mock-request",
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
function startOfUtcDay(value: Date) {
|
||||
return new Date(Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()));
|
||||
}
|
||||
|
||||
function addUtcDays(value: Date, days: number) {
|
||||
const next = startOfUtcDay(value);
|
||||
next.setUTCDate(next.getUTCDate() + days);
|
||||
return next;
|
||||
}
|
||||
|
||||
function toDateKey(value: Date) {
|
||||
return startOfUtcDay(value).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function buildCalendarItems(dateFrom: string, dateTo: string) {
|
||||
const start = startOfUtcDay(new Date(`${dateFrom}T00:00:00.000Z`));
|
||||
const end = startOfUtcDay(new Date(`${dateTo}T00:00:00.000Z`));
|
||||
const maxOffset = Math.max(0, Math.round((end.getTime() - start.getTime()) / 86400000));
|
||||
const second = addUtcDays(start, Math.min(1, maxOffset));
|
||||
const third = addUtcDays(start, Math.min(2, maxOffset));
|
||||
|
||||
return [
|
||||
{
|
||||
id: "leave-calendar-1",
|
||||
ownerId: "employee-manager-1",
|
||||
departmentId: "department-1",
|
||||
status: "approved",
|
||||
startDate: toDateKey(start),
|
||||
endDate: toDateKey(start),
|
||||
startSession: "am",
|
||||
endSession: "pm",
|
||||
requestedDays: 1,
|
||||
reason: "家庭事务",
|
||||
attachment: null,
|
||||
leaveType: {
|
||||
id: "leave-type-annual",
|
||||
code: "annual",
|
||||
name: "年假",
|
||||
requiresAttachment: false,
|
||||
attachmentMimeTypes: [],
|
||||
},
|
||||
owner: {
|
||||
id: "employee-manager-1",
|
||||
employeeNo: "E1001",
|
||||
name: "王小明",
|
||||
},
|
||||
createdAt: "2026-03-25T08:00:00.000Z",
|
||||
updatedAt: "2026-03-25T08:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "leave-calendar-2",
|
||||
ownerId: "employee-manager-2",
|
||||
departmentId: "department-1",
|
||||
status: "reviewing",
|
||||
startDate: toDateKey(second),
|
||||
endDate: toDateKey(third.getTime() > end.getTime() ? second : third),
|
||||
startSession: "am",
|
||||
endSession: "pm",
|
||||
requestedDays: third.getTime() > end.getTime() ? 1 : 2,
|
||||
reason: "出差调休",
|
||||
attachment: null,
|
||||
leaveType: {
|
||||
id: "leave-type-sick",
|
||||
code: "sick",
|
||||
name: "病假",
|
||||
requiresAttachment: false,
|
||||
attachmentMimeTypes: [],
|
||||
},
|
||||
owner: {
|
||||
id: "employee-manager-2",
|
||||
employeeNo: "E1002",
|
||||
name: "李四",
|
||||
},
|
||||
createdAt: "2026-03-25T08:00:00.000Z",
|
||||
updatedAt: "2026-03-25T08:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "leave-calendar-3",
|
||||
ownerId: "employee-manager-3",
|
||||
departmentId: "department-1",
|
||||
status: "approved",
|
||||
startDate: toDateKey(end),
|
||||
endDate: toDateKey(end),
|
||||
startSession: "pm",
|
||||
endSession: "pm",
|
||||
requestedDays: 0.5,
|
||||
reason: "下午就医",
|
||||
attachment: null,
|
||||
leaveType: {
|
||||
id: "leave-type-personal",
|
||||
code: "personal",
|
||||
name: "事假",
|
||||
requiresAttachment: false,
|
||||
attachmentMimeTypes: [],
|
||||
},
|
||||
owner: {
|
||||
id: "employee-manager-3",
|
||||
employeeNo: "E1003",
|
||||
name: "赵六",
|
||||
},
|
||||
createdAt: "2026-03-25T08:00:00.000Z",
|
||||
updatedAt: "2026-03-25T08:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "leave-calendar-4",
|
||||
ownerId: "employee-manager-4",
|
||||
departmentId: "department-1",
|
||||
status: "approved",
|
||||
startDate: toDateKey(second),
|
||||
endDate: toDateKey(second),
|
||||
startSession: "am",
|
||||
endSession: "am",
|
||||
requestedDays: 0.5,
|
||||
reason: "半天外出",
|
||||
attachment: null,
|
||||
leaveType: {
|
||||
id: "leave-type-annual",
|
||||
code: "annual",
|
||||
name: "年假",
|
||||
requiresAttachment: false,
|
||||
attachmentMimeTypes: [],
|
||||
},
|
||||
owner: {
|
||||
id: "employee-manager-4",
|
||||
employeeNo: "E1004",
|
||||
name: "孙七",
|
||||
},
|
||||
createdAt: "2026-03-25T08:00:00.000Z",
|
||||
updatedAt: "2026-03-25T08:00:00.000Z",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async function mockLeaveRequests(page: Page) {
|
||||
const observedCalendarQueries: Array<{ dateFrom: string; dateTo: string; pageSize: string }> = [];
|
||||
|
||||
await page.route("**/api/leave-requests?**", async (route) => {
|
||||
const url = new URL(route.request().url());
|
||||
const scope = url.searchParams.get("scope") ?? "own";
|
||||
const pageSize = url.searchParams.get("pageSize") ?? "10";
|
||||
const dateFrom = url.searchParams.get("dateFrom");
|
||||
const dateTo = url.searchParams.get("dateTo");
|
||||
|
||||
if (scope === "all" && dateFrom && dateTo) {
|
||||
observedCalendarQueries.push({ dateFrom, dateTo, pageSize });
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(
|
||||
envelope({
|
||||
page: 1,
|
||||
pageSize: Number(pageSize),
|
||||
total: 5,
|
||||
scope: "all",
|
||||
items: buildCalendarItems(dateFrom, dateTo),
|
||||
}),
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(
|
||||
envelope({
|
||||
page: 1,
|
||||
pageSize: Number(pageSize),
|
||||
total: 0,
|
||||
scope,
|
||||
items: [],
|
||||
}),
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
return observedCalendarQueries;
|
||||
}
|
||||
|
||||
test("E2E 请假团队日历:具备团队查看权限的用户可见周/月视图并显示截断提示", async ({ page }) => {
|
||||
const observedCalendarQueries = await mockLeaveRequests(page);
|
||||
|
||||
await login(page, LEAVE_CALENDAR_MANAGER);
|
||||
await page.goto("/leave-requests");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
|
||||
await expect(page.getByTestId("leave-view-calendar")).toBeVisible();
|
||||
|
||||
const weekResponse = page.waitForResponse((response) => {
|
||||
const url = new URL(response.url());
|
||||
return response.ok() && url.pathname === "/api/leave-requests" && url.searchParams.get("scope") === "all";
|
||||
});
|
||||
await page.getByTestId("leave-view-calendar").click();
|
||||
await weekResponse;
|
||||
|
||||
await expect(page.getByTestId("leave-calendar-mode-week")).toHaveAttribute("data-state", "active");
|
||||
await expect(page.getByTestId("leave-team-calendar-grid")).toBeVisible();
|
||||
await expect(page.getByTestId("leave-team-calendar-entry").filter({ hasText: "王小明" }).first()).toBeVisible();
|
||||
await expect(page.getByTestId("leave-team-calendar-entry").filter({ hasText: "李四" }).first()).toBeVisible();
|
||||
await expect(page.getByTestId("leave-team-calendar-truncation")).toContainText("仅显示前 4 条,共 5 条");
|
||||
await expect(page.getByTestId("leave-team-calendar-cell")).toHaveCount(7);
|
||||
|
||||
const monthResponse = page.waitForResponse((response) => {
|
||||
const url = new URL(response.url());
|
||||
return response.ok() && url.pathname === "/api/leave-requests" && url.searchParams.get("scope") === "all";
|
||||
});
|
||||
await page.getByTestId("leave-calendar-mode-month").click();
|
||||
await monthResponse;
|
||||
|
||||
await expect(page.getByTestId("leave-calendar-mode-month")).toHaveAttribute("data-state", "active");
|
||||
await expect.poll(() => page.getByTestId("leave-team-calendar-cell").count()).toBeGreaterThan(7);
|
||||
await expect.poll(() => observedCalendarQueries.length).toBeGreaterThanOrEqual(2);
|
||||
expect(observedCalendarQueries.every((query) => Boolean(query.dateFrom && query.dateTo))).toBe(true);
|
||||
expect(observedCalendarQueries.every((query) => query.pageSize === "100")).toBe(true);
|
||||
});
|
||||
|
||||
test("E2E 请假团队日历:员工视角不显示团队日历 Tab", async ({ page }) => {
|
||||
await mockLeaveRequests(page);
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
await page.goto("/leave-requests");
|
||||
|
||||
await expect(page.getByRole("heading", { name: "请假申请" })).toBeVisible();
|
||||
await expect(page.getByTestId("leave-view-calendar")).toHaveCount(0);
|
||||
await expect(page.getByTestId("leave-page-view-tabs")).toHaveCount(0);
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户