补齐发票验真测试覆盖(05-28-oa-test-coverage-gap-fill)

这个提交包含在:
2026-05-28 08:45:19 +08:00
父节点 ab802cbf2c
当前提交 1c3f0c4a15
共修改 9 个文件,包含 1021 行新增和 3 行删除
@@ -65,6 +65,21 @@ async function fulfillJson<T>(route: Route, data: T) {
});
}
async function fulfillApiError(route: Route, status: number, details: Record<string, unknown>) {
await route.fulfill({
status,
contentType: "application/json",
body: JSON.stringify({
requestId: "mock-pc-wave4",
error: {
code: "VALID_001",
message: "参数校验失败",
details,
},
}),
});
}
function navigationItem(key: string, href: string, label: string, icon: string, placement = "main") {
return { key, href, label, icon, placement, permissionCodes: [], featureFlagKey: null };
}
@@ -394,7 +409,41 @@ const notificationDeliveries = [
},
];
async function mockWave4Apis(page: Page) {
type MockVerificationRecord = {
id: string;
provider: string;
status: string;
resultCode: string | null;
resultMessage: string | null;
errorCode: string | null;
errorMessage: string | null;
durationMs: number | null;
requestedBy: string | null;
requestedAt: string | null;
completedAt: string | null;
retryOfId: string | null;
attempts: number;
};
type Wave4MockOptions = {
detailVerification?: MockVerificationRecord | null;
verificationRecords?: MockVerificationRecord[];
triggerVerificationError?: Record<string, unknown>;
retryVerificationError?: Record<string, unknown>;
onTriggerVerification?: (route: Route) => void;
onRetryVerification?: (route: Route) => void;
};
async function mockWave4Apis(page: Page, options: Wave4MockOptions = {}) {
const defaultVerification = invoiceListItem.verification as MockVerificationRecord;
const initialVerificationRecords =
options.verificationRecords ??
[options.detailVerification ?? defaultVerification].filter(
(item): item is MockVerificationRecord => Boolean(item),
);
let currentVerificationRecords = initialVerificationRecords;
let currentDetailVerification = options.detailVerification ?? currentVerificationRecords[0] ?? defaultVerification;
await page.route((url) => url.pathname.startsWith("/api/"), async (route) => {
const request = route.request();
const url = new URL(request.url());
@@ -593,13 +642,20 @@ async function mockWave4Apis(page: Page) {
}
if (pathName === "/api/invoice-pool") {
await fulfillJson(route, { page: 1, pageSize: 20, total: 1, ownerScope: "own", items: [invoiceListItem] });
await fulfillJson(route, {
page: 1,
pageSize: 20,
total: 1,
ownerScope: "own",
items: [{ ...invoiceListItem, verification: currentDetailVerification }],
});
return;
}
if (pathName === `/api/invoice-pool/${invoiceListItem.id}`) {
await fulfillJson(route, {
...invoiceListItem,
verification: currentDetailVerification,
archivedReason: null,
archivedAt: null,
ownerEmployeeId: visualMe.id,
@@ -616,8 +672,64 @@ async function mockWave4Apis(page: Page) {
return;
}
if (pathName === `/api/invoice-pool/${invoiceListItem.id}/verify`) {
options.onTriggerVerification?.(route);
if (options.triggerVerificationError) {
await fulfillApiError(route, 409, options.triggerVerificationError);
return;
}
const pendingRecord: MockVerificationRecord = {
...defaultVerification,
id: "verify-wave4-triggered",
status: "pending",
resultCode: null,
resultMessage: null,
errorCode: null,
errorMessage: null,
completedAt: null,
attempts: 1,
};
currentDetailVerification = pendingRecord;
currentVerificationRecords = [pendingRecord, ...currentVerificationRecords];
await fulfillJson(route, {
invoiceId: invoiceListItem.id,
verificationId: pendingRecord.id,
provider: "mock",
triggerMode: "queued",
});
return;
}
if (pathName === `/api/invoice-pool/${invoiceListItem.id}/verification`) {
await fulfillJson(route, { invoiceId: invoiceListItem.id, items: [invoiceListItem.verification] });
await fulfillJson(route, { invoiceId: invoiceListItem.id, items: currentVerificationRecords });
return;
}
if (pathName === `/api/invoice-pool/${invoiceListItem.id}/verification/retry`) {
options.onRetryVerification?.(route);
if (options.retryVerificationError) {
await fulfillApiError(route, 409, options.retryVerificationError);
return;
}
const retryRecord: MockVerificationRecord = {
...defaultVerification,
id: "verify-wave4-retry",
status: "pending",
resultCode: null,
resultMessage: null,
errorCode: null,
errorMessage: null,
completedAt: null,
retryOfId: currentDetailVerification?.id ?? null,
attempts: (currentDetailVerification?.attempts ?? 1) + 1,
};
currentDetailVerification = retryRecord;
currentVerificationRecords = [retryRecord, ...currentVerificationRecords];
await fulfillJson(route, {
invoiceId: invoiceListItem.id,
verificationId: retryRecord.id,
status: "pending",
});
return;
}
@@ -796,3 +908,72 @@ test("PC 财务与管理页 Wave4 视觉冒烟", async ({ page }) => {
await capture(page, target.screenshot);
}
});
test("PC 票据中心辅助核验动作可提交并重试", async ({ page }) => {
const failedVerification: MockVerificationRecord = {
...(invoiceListItem.verification as MockVerificationRecord),
id: "verify-wave4-failed",
status: "error",
resultCode: "tax_provider_not_configured",
resultMessage: null,
errorCode: "tax_provider_not_configured",
errorMessage: "tax provider is not configured in current environment",
attempts: 1,
};
const triggerRequests: string[] = [];
const retryRequests: string[] = [];
await mockWave4Apis(page, {
detailVerification: failedVerification,
verificationRecords: [failedVerification],
onTriggerVerification: (route) => triggerRequests.push(route.request().method()),
onRetryVerification: (route) => retryRequests.push(route.request().method()),
});
await page.setViewportSize({ width: 1440, height: 960 });
await login(page);
await page.goto("/invoice-pool");
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
await expect(page.getByTestId("invoice-trigger-verification")).toBeVisible();
await expect(page.getByTestId("invoice-retry-verification")).toBeVisible();
await expect(page.getByTestId("invoice-verification-records")).toContainText("当前环境未配置自动核验服务");
await page.getByTestId("invoice-retry-verification").click();
await expect(page.getByText("发票核验已重新提交")).toBeVisible();
expect(retryRequests).toEqual(["POST"]);
await page.getByTestId("invoice-trigger-verification").click();
await expect(page.getByText("发票核验已提交")).toBeVisible();
expect(triggerRequests).toEqual(["POST"]);
});
test("PC 票据中心辅助核验错误提示可理解", async ({ page }) => {
const failedVerification: MockVerificationRecord = {
...(invoiceListItem.verification as MockVerificationRecord),
id: "verify-wave4-attempts",
status: "error",
resultCode: "mock_transport_error",
resultMessage: null,
errorCode: "mock_transport_error",
errorMessage: "mock provider transport error",
attempts: 3,
};
await mockWave4Apis(page, {
detailVerification: failedVerification,
verificationRecords: [failedVerification],
triggerVerificationError: { verification: "too_frequent" },
retryVerificationError: { verification: "attempts_exceeded" },
});
await page.setViewportSize({ width: 1440, height: 960 });
await login(page);
await page.goto("/invoice-pool");
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
await page.getByTestId("invoice-trigger-verification").click();
await expect(page.getByRole("main").getByText("VALID_001: 验真触发过于频繁,请稍后重试。")).toBeVisible();
await page.getByTestId("invoice-retry-verification").click();
await expect(page.getByRole("main").getByText("VALID_001: 验真重试次数已达上限,请联系财务人工核验。")).toBeVisible();
});