R6-ISSUE-002 对齐报销 E2E 导航口径

这个提交包含在:
2026-05-11 16:23:17 +08:00
父节点 c61151d54d
当前提交 bded569d92
共修改 6 个文件,包含 82 行新增和 56 行删除
@@ -33,6 +33,26 @@ export class ReimbursementDetailPageObject {
return this.page.getByTestId(TEST_IDS.reimbursement.workflowProgress);
}
get invoices() {
return this.page.getByTestId(TEST_IDS.reimbursement.invoices);
}
get invoiceAddBtn() {
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceAdd);
}
get invoiceDialog() {
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceDialog);
}
get invoiceUploadFile() {
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceUploadFile);
}
get invoiceUploadSubmit() {
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceUploadSubmit);
}
get erpExport() {
return this.page.getByTestId(TEST_IDS.reimbursement.erpExport);
}
@@ -58,6 +78,36 @@ export class ReimbursementDetailPageObject {
await expect(this.summary).toContainText(statusText);
}
async uploadInvoicePdf(fileName: string, buffer: Buffer) {
await this.invoiceAddBtn.click();
await expect(this.invoiceDialog).toBeVisible();
const uploadResponsePromise = this.page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
});
await this.invoiceUploadFile.setInputFiles({
name: fileName,
mimeType: "application/pdf",
buffer,
});
await this.invoiceUploadSubmit.scrollIntoViewIfNeeded();
await this.invoiceUploadSubmit.click();
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
const invoiceId = uploadPayload.data?.invoicePoolId;
if (!invoiceId) {
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
}
await expect(this.invoices).toContainText(fileName, { timeout: 90_000 });
await this.page.keyboard.press("Escape");
await expect(this.invoiceDialog).toHaveCount(0);
return invoiceId;
}
// 当前 seed 保证 precheck 不触发 warn/manual_review;若 seed 变更,此处需相应调整。
async submitReimbursement(acceptConfirm = true) {
const dialogPromise = acceptConfirm
@@ -81,60 +81,49 @@ test("E2E 报销支付批次主链路:提交 -> 审批 -> 创建批次 -> 复
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await (await openNavLink(page, "票据中心")).click();
const marker = `R78_BATCH_${Date.now()}_${Math.random().toString(16).slice(2)}`;
const uploadResponsePromise = page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
});
await page.getByTestId("invoice-upload-file").setInputFiles({
name: `r78-${Date.now()}.pdf`,
mimeType: "application/pdf",
buffer: Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
});
await page.getByTestId("invoice-upload-submit").click();
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
const invoiceId = uploadPayload.data?.invoicePoolId;
if (!invoiceId) {
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
}
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
await expect(page.getByTestId("invoice-detail-panel")).toContainText("状态:可用", { timeout: 90_000 });
await page.getByTestId("invoice-bind-invoice").selectOption(invoiceId);
await page.getByTestId("invoice-bind-reimbursement").selectOption(reimbursementId);
await page.getByTestId("invoice-bind-submit").click();
await expect(page.getByText("绑定成功")).toBeVisible({ timeout: 30_000 });
const invoiceId = await reimbursementDetail.uploadInvoicePdf(
`r78-${Date.now()}.pdf`,
Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
);
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await reimbursementDetail.submitReimbursement();
await expect(page.getByText(/状态:(reviewing|审批中)/)).toBeVisible();
const reimbursementAfterSubmit = await apiGetData<{ formNumber: string | null; totalAmount: string | null }>(
employeeApi.api,
`/api/reimbursements/${reimbursementId}`,
);
const reimbursementDisplayCode = reimbursementAfterSubmit.formNumber ?? reimbursementId;
const secondApprovalUser = Number(reimbursementAfterSubmit.totalAmount ?? 0) > 500 ? FINANCE_MGR : FINANCE;
await logout(page, EMPLOYEE.name);
await login(page, MANAGER);
await (await openNavLink(page, "待我处理")).click();
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
const managerTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
const managerTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementDisplayCode }).first();
await expect(managerTaskRow).toBeVisible();
await managerTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
await workflowDetail.assertLoaded();
await workflowDetail.approve();
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
await expect(workflowDetail.summary).toContainText(/状态:(已处理|completed|已完成)/);
await logout(page, MANAGER.name);
await login(page, FINANCE);
await login(page, secondApprovalUser);
await (await openNavLink(page, "待我处理")).click();
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
const financeTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
const financeTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementDisplayCode }).first();
await expect(financeTaskRow).toBeVisible();
await financeTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
await workflowDetail.assertLoaded();
await workflowDetail.approve();
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
await expect(workflowDetail.summary).toContainText(/状态:(已处理|completed|已完成)/);
await logout(page, secondApprovalUser.name);
await login(page, FINANCE);
const batchName = `R78-报销付款批次-${Date.now()}`;
await page.goto("/payment-batches");
@@ -24,30 +24,11 @@ test("E2E 报销审批进度:提交后详情页展示当前步骤进度", asyn
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await (await openNavLink(page, "票据中心")).click();
const marker = `MOCK_E2E_${Date.now()}_${Math.random().toString(16).slice(2)}`;
const uploadResponsePromise = page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
});
await page.getByTestId("invoice-upload-file").setInputFiles({
name: `progress-${Date.now()}.pdf`,
mimeType: "application/pdf",
buffer: Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
});
await page.getByTestId("invoice-upload-submit").click();
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
const invoiceId = uploadPayload.data?.invoicePoolId;
if (!invoiceId) {
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
}
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
await expect(page.getByTestId("invoice-detail-panel")).toContainText("状态:可用", { timeout: 90_000 });
await page.getByTestId("invoice-bind-invoice").selectOption(invoiceId);
await page.getByTestId("invoice-bind-reimbursement").selectOption(reimbursementId);
await page.getByTestId("invoice-bind-submit").click();
await reimbursementDetail.uploadInvoicePdf(
`progress-${Date.now()}.pdf`,
Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
);
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();