// company-api-test/index.html 自动化验收测试 const { chromium } = require("playwright"); const path = require("path"); const OUT = (n) => path.join(require("os").tmpdir(), n); (async () => { const errors = []; const browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); page.on("pageerror", (e) => errors.push(String(e))); page.on("console", (m) => { // 故意触发的 405/404 网络日志不算页面错误 if (m.type() === "error" && !/Failed to load resource/.test(m.text())) errors.push(m.text()); }); await page.goto("http://localhost:9527"); await page.waitForLoadState("networkidle"); // 1) 主链路:公网 api-cx 实名查询(真实网络请求) const idv = await page.inputValue("#idPlain"); if (idv !== "430703198610010059") throw new Error("默认身份证明文不对: " + idv); const preview = await page.textContent("#cxPreview"); if (!preview.includes("sign=") || !preview.includes("zjhm=J4jfHM")) throw new Error("签名 URL 预览异常: " + preview); await page.click("#btnSendCx"); await page.waitForTimeout(4000); const chipCode = await page.textContent("#chipCode"); if (!chipCode.includes("0")) throw new Error("respCode 应为 0: " + chipCode); const meta = await page.textContent("#metaLine"); if (!meta.includes("430703198610010059")) throw new Error("台账 meta 应含身份证明文"); const emptyMsg = await page.textContent("#ledgerBody"); if (!emptyMsg.includes("暂无企业数据")) throw new Error("应显示空数据提示: " + emptyMsg); await page.screenshot({ path: OUT("v2_1_cx_query.png"), fullPage: true }); // 2) 校验密文解密(后端解密回显 → 证明 AES 正确) await page.click("#btnVerifyZjhm"); await page.waitForTimeout(3000); const ledger = await page.textContent("#ledgerBody"); if (!ledger.includes("430703198610010059")) throw new Error("校验台账应含明文"); if (!ledger.includes("一致")) throw new Error("校验结论应为一致"); await page.screenshot({ path: OUT("v2_2_verify.png"), fullPage: true }); // 3) JSON 标签页 await page.click('.tab[data-tab="json"]'); const j = await page.textContent("#jsonScreen"); if (!j.includes("respCode")) throw new Error("JSON 视图应含 respCode"); // 4) 次链路:tax-appoint 离线示例 await page.check("#mockToggle"); await page.click("#btnSend"); await page.waitForTimeout(400); const rows = await page.locator("#ledgerBody tr").count(); if (rows !== 2) throw new Error("示例模式应渲染 2 行, 实际 " + rows); if (!(await page.textContent("#ledgerBody")).includes("义乌市万竹贤口腔诊所")) throw new Error("示例台账缺少企业"); const t2 = await page.textContent("#ledgerBody"); if (t2.includes("330725198202084328")) throw new Error("脱敏开启时不应显示完整证件号"); // 5) 关闭脱敏 → 完整证件号 await page.uncheck("#maskToggle"); await page.waitForTimeout(200); if (!(await page.textContent("#ledgerBody")).includes("330725198202084328")) throw new Error("关闭脱敏后应显示完整证件号"); await page.check("#maskToggle"); // 6) tax-appoint 真实请求(经代理 → 405)→ 友好错误面板 await page.uncheck("#mockToggle"); await page.click("#btnSameOrigin"); await page.click("#btnSend"); await page.waitForTimeout(2500); if (!(await page.locator("#stageError").isVisible())) throw new Error("后端不可用时应展示错误面板"); const chipHttp = await page.textContent("#chipHttp"); if (!/405|502/.test(chipHttp)) throw new Error("状态条应显示 405/502: " + chipHttp); await page.screenshot({ path: OUT("v2_3_ta_error.png"), fullPage: true }); // 7) 历史 if ((await page.locator(".h-item").count()) < 2) throw new Error("应有 ≥2 条历史记录"); // 8) AES 工具 await page.fill("#aesPlain", "330725198202084328"); await page.click("#btnAesEnc"); const enc = await page.inputValue("#aesCipherOut"); if (enc !== "MoV2Ra2USY/6tc6b9xQXZkuEA+I6X3u90CSNgvFqzlE=") throw new Error("AES 加密与文档示例不一致: " + enc); await page.fill("#aesCipherIn", enc); await page.click("#btnAesDec"); if ((await page.inputValue("#aesPlainOut")) !== "330725198202084328") throw new Error("AES 解密不一致"); await page.fill("#aesKey", "shortkey"); await page.click("#btnAesEnc"); if (!/错误/.test(await page.inputValue("#aesCipherOut"))) throw new Error("非法密钥应提示错误"); await page.fill("#aesKey", "Gi4swf3llyafmsuK"); // 9) 密文一键填入 zjhm await page.click("#btnAesEnc"); await page.click("#btnFillZjhm"); if ((await page.inputValue("#zjhm")) !== "MoV2Ra2USY/6tc6b9xQXZkuEA+I6X3u90CSNgvFqzlE=") throw new Error("填入 zjhm 失败"); await page.screenshot({ path: OUT("v2_4_final.png"), fullPage: true }); await browser.close(); if (errors.length) { console.log("CONSOLE/PAGE ERRORS:"); errors.forEach((e) => console.log(" -", e.slice(0, 300))); process.exit(1); } console.log("ALL CHECKS PASSED"); })().catch((e) => { console.error("FAIL:", e.message); process.exit(1); });