fix: R27-5 R27-6 收口本地回归基线与E2E适配
这个提交包含在:
@@ -1,5 +1,7 @@
|
||||
import { expect, test, type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { openNavLink } from "./helpers/navigation";
|
||||
|
||||
const FINANCE_MGR = { identifier: "[email protected]", password: "FinanceMgrPassword1234", name: "Finance Manager" };
|
||||
const ADMINISTRATION = { identifier: "[email protected]", password: "AdministrationPassword1234", name: "Administration" };
|
||||
|
||||
@@ -125,7 +127,7 @@ test("E2E 资产流转主链路:建卡→领用归还→调拨→盘点", asyn
|
||||
await logout(page, ADMINISTRATION.name);
|
||||
|
||||
await login(page, FINANCE_MGR);
|
||||
await page.getByRole("link", { name: "资产盘点" }).click();
|
||||
await (await openNavLink(page, "资产盘点")).click();
|
||||
await page.getByTestId("inventory-create").click();
|
||||
|
||||
const inventoryRow = page.getByTestId("inventory-row").first();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { expect, type Locator, type Page } from "@playwright/test";
|
||||
|
||||
export async function openBackupModulesIfNeeded(page: Page) {
|
||||
const summary = page.locator("summary").filter({ hasText: "备用模块" }).first();
|
||||
if (await summary.count()) {
|
||||
const details = summary.locator("xpath=ancestor::details[1]");
|
||||
const isOpen = await details.evaluate((node) => (node as HTMLDetailsElement).open);
|
||||
if (!isOpen) {
|
||||
await summary.click();
|
||||
await expect(details).toHaveAttribute("open", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function openNavLink(page: Page, name: string | RegExp): Promise<Locator> {
|
||||
const link = page.getByRole("link", { name }).first();
|
||||
if (await link.isVisible().catch(() => false)) {
|
||||
return link;
|
||||
}
|
||||
|
||||
await openBackupModulesIfNeeded(page);
|
||||
await expect(link).toBeVisible();
|
||||
return link;
|
||||
}
|
||||
@@ -204,6 +204,8 @@ test("Portal 管理视角重排正确,备用模块默认折叠", async ({ page
|
||||
|
||||
await login(page, ADMIN);
|
||||
|
||||
await expect(page.locator('[aria-label^="portal-card-"]').first()).toBeVisible();
|
||||
|
||||
const labels = await page.locator('[aria-label^="portal-card-"]').evaluateAll((nodes) =>
|
||||
nodes.map((node) => node.getAttribute("aria-label")),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
|
||||
import { openNavLink } from "./helpers/navigation";
|
||||
|
||||
const EMPLOYEE = { identifier: "[email protected]", password: "EmployeePassword1234", name: "Employee" };
|
||||
const MANAGER = { identifier: "[email protected]", password: "ManagerPassword1234", name: "Manager" };
|
||||
const FINANCE = { identifier: "[email protected]", password: "FinancePassword1234", name: "Finance" };
|
||||
@@ -30,7 +32,7 @@ test("E2E 主链路:登录→报销→待办审批→票据OCR→支付→登
|
||||
// 1) employee: create reimbursement
|
||||
await login(page, EMPLOYEE);
|
||||
|
||||
await page.getByRole("link", { name: "我的报销" }).click();
|
||||
await page.locator("nav").getByRole("link", { name: "我的报销", exact: true }).click();
|
||||
const createResponsePromise = page.waitForResponse((response) => {
|
||||
const url = new URL(response.url());
|
||||
return response.request().method() === "POST" && url.pathname === "/api/reimbursements" && response.ok();
|
||||
@@ -47,7 +49,7 @@ test("E2E 主链路:登录→报销→待办审批→票据OCR→支付→登
|
||||
const reimbursementSummaryText = await page.getByTestId("reimbursement-summary").innerText();
|
||||
|
||||
// 2) employee: upload invoice and wait OCR
|
||||
await page.getByRole("link", { name: "票据中心" }).click();
|
||||
await (await openNavLink(page, "票据中心")).click();
|
||||
const marker = `MOCK_E2E_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
||||
await page.getByTestId("invoice-upload-file").setInputFiles({
|
||||
name: `e2e-${Date.now()}.png`,
|
||||
@@ -87,7 +89,7 @@ test("E2E 主链路:登录→报销→待办审批→票据OCR→支付→登
|
||||
await logout(page, EMPLOYEE.name);
|
||||
await login(page, MANAGER);
|
||||
|
||||
await page.getByRole("link", { name: "待我处理" }).click();
|
||||
await (await openNavLink(page, "待我处理")).click();
|
||||
await expect(page.getByTestId("workflow-tasks-table")).toBeVisible();
|
||||
const managerTaskRow = page.getByTestId("workflow-task-row").filter({ hasText: reimbursementId }).first();
|
||||
await expect(managerTaskRow).toBeVisible();
|
||||
@@ -99,7 +101,7 @@ test("E2E 主链路:登录→报销→待办审批→票据OCR→支付→登
|
||||
|
||||
// 6) finance approve + pay
|
||||
await login(page, FINANCE);
|
||||
await page.getByRole("link", { name: "待我处理" }).click();
|
||||
await (await openNavLink(page, "待我处理")).click();
|
||||
const financeTaskRow = page.getByTestId("workflow-task-row").filter({ hasText: reimbursementId }).first();
|
||||
await expect(financeTaskRow).toBeVisible();
|
||||
await financeTaskRow.getByTestId("workflow-task-detail-link").click();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { expect, test, type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { openNavLink } from "./helpers/navigation";
|
||||
|
||||
const EMPLOYEE = { identifier: "[email protected]", password: "EmployeePassword1234", name: "Employee" };
|
||||
const MANAGER = { identifier: "[email protected]", password: "ManagerPassword1234", name: "Manager" };
|
||||
const FINANCE_MGR = { identifier: "[email protected]", password: "FinanceMgrPassword1234", name: "Finance Manager" };
|
||||
@@ -45,7 +47,7 @@ test("E2E 采购主链路:员工提单→经理审批→财务归档", async (
|
||||
const title = `Playwright 采购 ${Date.now()}`;
|
||||
|
||||
await login(page, EMPLOYEE);
|
||||
await page.getByRole("link", { name: "采购申请", exact: true }).first().click();
|
||||
await (await openNavLink(page, "采购申请")).click();
|
||||
await page.getByTestId("purchase-requests-create").click();
|
||||
|
||||
await expect(page.getByTestId("purchase-title")).toBeVisible();
|
||||
@@ -74,7 +76,7 @@ test("E2E 采购主链路:员工提单→经理审批→财务归档", async (
|
||||
await logout(page, EMPLOYEE.name);
|
||||
|
||||
await login(page, MANAGER);
|
||||
await page.getByRole("link", { name: "待我处理" }).click();
|
||||
await (await openNavLink(page, "待我处理")).click();
|
||||
const managerTaskRow = page.getByTestId("workflow-task-row").filter({ hasText: currentTaskId }).first();
|
||||
await expect(managerTaskRow).toBeVisible({ timeout: 30_000 });
|
||||
await managerTaskRow.getByTestId("workflow-task-detail-link").click();
|
||||
@@ -85,7 +87,7 @@ test("E2E 采购主链路:员工提单→经理审批→财务归档", async (
|
||||
await logout(page, MANAGER.name);
|
||||
|
||||
await login(page, FINANCE_MGR);
|
||||
await page.getByRole("link", { name: "采购申请", exact: true }).first().click();
|
||||
await (await openNavLink(page, "采购申请")).click();
|
||||
await page.getByRole("button", { name: "全范围" }).click();
|
||||
await page.getByRole("combobox").first().selectOption("approved");
|
||||
const purchaseRow = page.getByRole("row").filter({ hasText: title }).first();
|
||||
|
||||
@@ -99,8 +99,8 @@ test("E2E 项目工作台:沟通回填 + 收口面板 + Portal 摘要", async
|
||||
await expect(page.getByTestId("project-closure-panel")).toContainText("确认交付/验收记录齐全");
|
||||
|
||||
await page.goto("/portal");
|
||||
await expect(page.getByText(/项目摘要:/)).toContainText("今日待跟进");
|
||||
await expect(page.getByText(/项目摘要:/)).toContainText("待收口项目");
|
||||
await expect(page.getByRole("heading", { name: "今日管理重点" })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: /待跟进 \/ 待收口项目/ })).toBeVisible();
|
||||
|
||||
await adminClient.dispose();
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户