文件
hyb-oa/client/tests/e2e/manual/live-invoice-ocr-batch.spec.ts
T

104 行
3.9 KiB
TypeScript

import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
import path from "node:path";
import { expect, test } from "../helpers/fixtures";
import { EMPLOYEE, login, logout } from "../helpers/auth";
import { openInvoicePool, uploadLiveInvoiceAndWait, type LiveInvoiceOcrResult } from "../helpers/liveInvoiceOcr";
type BatchResult = LiveInvoiceOcrResult & {
filePath: string;
fileName: string;
};
const rootDir = path.resolve(process.cwd(), "..");
const reportPath = path.join(rootDir, "artifacts/r70/live-ocr-batch-report.md");
function resolveInvoiceFiles() {
const files = new Set<string>();
const directFile = process.env.PLAYWRIGHT_OCR_LIVE_FILE?.trim();
const batchFiles = process.env.PLAYWRIGHT_OCR_LIVE_FILES?.split(/[\n,]/).map((item) => item.trim()).filter(Boolean) ?? [];
const batchDir = process.env.PLAYWRIGHT_OCR_LIVE_DIR?.trim() ?? "";
if (directFile) {
files.add(path.resolve(directFile));
}
for (const file of batchFiles) {
files.add(path.resolve(file));
}
if (batchDir && existsSync(batchDir) && statSync(batchDir).isDirectory()) {
for (const entry of readdirSync(batchDir).sort((left, right) => left.localeCompare(right, "zh-CN"))) {
const fullPath = path.join(batchDir, entry);
if (!statSync(fullPath).isFile()) continue;
if (!/\.(pdf|png|jpg|jpeg|webp)$/i.test(entry)) continue;
files.add(path.resolve(fullPath));
}
}
return [...files].filter((filePath) => existsSync(filePath));
}
function writeReport(results: BatchResult[]) {
mkdirSync(path.dirname(reportPath), { recursive: true });
const lines = [
"# R70 批量 live OCR 辅助复核报告",
"",
`生成时间:${new Date().toISOString()}`,
"",
"## 结果",
"",
`- 总文件数:${results.length}`,
`- 成功:${results.filter((item) => item.status === "available").length}`,
`- 失败:${results.filter((item) => item.status === "ocr_failed").length}`,
"",
"## 明细",
"",
"| 文件 | 状态 | 耗时(ms) | 票据ID | 发票代码 | 发票号码 | 销售方 | 开票日期 | 备注 |",
"| --- | --- | ---: | --- | --- | --- | --- | --- | --- |",
...results.map((item) => {
const remark = item.status === "ocr_failed" ? (item.bannerText || item.detailText).replaceAll("|", "\\|") : "ok";
return `| ${item.fileName} | ${item.status} | ${item.durationMs} | ${item.invoiceId ?? "-"} | ${item.invoiceCode ?? "-"} | ${item.invoiceNumber ?? "-"} | ${item.sellerName ?? "-"} | ${item.invoiceDate ?? "-"} | ${remark} |`;
}),
"",
"## 说明",
"",
"- 本报告用于真实腾讯 OCR 的本地辅助复核,不进入常规本地回归。",
"- 如某张票失败,会继续处理后续文件,并在本报告统一汇总。",
];
writeFileSync(reportPath, `${lines.join("\n")}\n`, "utf-8");
}
test("手工 live 腾讯 OCR 批量辅助复核:真实票样逐张上传并输出报告", async ({ page }) => {
test.skip(process.env.PLAYWRIGHT_MANUAL !== "1", "仅在显式手工 Playwright 模式下执行");
test.skip(process.env.PLAYWRIGHT_OCR_LIVE !== "1", "仅在显式 live OCR 辅助复核模式下执行");
const invoiceFiles = resolveInvoiceFiles();
test.skip(!invoiceFiles.length, "需要提供 PLAYWRIGHT_OCR_LIVE_FILE / PLAYWRIGHT_OCR_LIVE_FILES / PLAYWRIGHT_OCR_LIVE_DIR");
const results: BatchResult[] = [];
await login(page, EMPLOYEE);
try {
await openInvoicePool(page);
for (const invoiceFilePath of invoiceFiles) {
await openInvoicePool(page);
const result = await uploadLiveInvoiceAndWait(page, invoiceFilePath);
results.push({
...result,
filePath: invoiceFilePath,
fileName: path.basename(invoiceFilePath),
});
}
} finally {
writeReport(results);
await logout(page, EMPLOYEE.name).catch(() => undefined);
}
const failures = results.filter((item) => item.status === "ocr_failed");
expect(failures, JSON.stringify(failures, null, 2)).toHaveLength(0);
});