import { expect, type Page } from "@playwright/test"; import { TEST_IDS } from "../testIds"; export class ReimbursementDetailPageObject { readonly page: Page; constructor(page: Page) { this.page = page; } get overview() { return this.page.getByTestId(TEST_IDS.reimbursement.overview); } get summary() { return this.page.getByTestId(TEST_IDS.reimbursement.summary); } get itemsTable() { return this.page.getByTestId(TEST_IDS.reimbursement.items); } get addItemBtn() { return this.page.getByTestId(TEST_IDS.reimbursement.itemAdd); } get budgetHint() { return this.page.getByTestId(TEST_IDS.reimbursement.budgetHint); } get workflowProgress() { 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); } get submitBtn() { return this.page.getByTestId(TEST_IDS.reimbursement.submit); } get withdrawBtn() { return this.page.getByTestId(TEST_IDS.reimbursement.withdraw); } get payBtn() { return this.page.getByTestId(TEST_IDS.reimbursement.pay); } async assertLoaded() { await expect(this.overview).toBeVisible({ timeout: 15_000 }); await expect(this.summary).toBeVisible({ timeout: 15_000 }); } async assertStatus(statusText: string) { 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 ? this.page.waitForEvent("dialog", { timeout: 2_000 }).then((dialog) => dialog.accept()).catch(() => undefined) : Promise.resolve(); await this.submitBtn.click(); await dialogPromise; } }