/* smoke.js —— jsdom 冒烟:抓真实数据,跑完整 app,检查各视图能渲染、无异常。 * 这是"浏览器验证之前"的粗筛;真实验收以 verify.js(Chrome)为准。 */ const { JSDOM, VirtualConsole } = require("jsdom"); const fs = require("fs"); const path = require("path"); const ROOT = "/opt/data/work/kangshifu/impl"; const HEADERS = { "X-Parse-Application-Id": "FKieRbIoGt_main" }; (async () => { const cache = {}; async function pfetch(url) { if (url.includes("kangshifu.fmode.cn")) { const cls = url.match(/classes\/(\w+)/)[1]; if (!cache[cls]) { const r = await fetch(url, { headers: HEADERS }); cache[cls] = await r.text(); } return { ok: true, status: 200, json: async () => JSON.parse(cache[cls]), text: async () => cache[cls] }; } /* 本地文件走磁盘 */ const p = path.join(ROOT, new URL(url, "http://x/").pathname); if (!fs.existsSync(p)) return { ok: false, status: 404, text: async () => "" }; return { ok: true, status: 200, text: async () => fs.readFileSync(p, "utf8") }; } const vc = new VirtualConsole(); const errors = []; vc.on("jsdomError", (e) => errors.push("jsdomError: " + e.message)); vc.on("error", (...a) => errors.push("console.error: " + a.join(" "))); const html = fs.readFileSync(path.join(ROOT, "index.html"), "utf8"); const dom = new JSDOM(html, { url: "http://localhost/#/overview", runScripts: "outside-only", pretendToBeVisual: true, virtualConsole: vc }); const w = dom.window; w.fetch = pfetch; w.CSS = w.CSS || { escape: (s) => s }; if (!w.IntersectionObserver) w.IntersectionObserver = class { observe() {} disconnect() {} }; for (const f of ["config", "taxonomy", "util", "data", "model", "query", "router", "views/common", "views/overview", "views/artifacts", "views/insights", "views/projects", "views/members", "views/stages", "views/sources", "views/detail", "views/report", "app"]) { w.eval(fs.readFileSync(path.join(ROOT, "js", f + ".js"), "utf8")); } await new Promise((r) => setTimeout(r, 1500)); const db = w.__KF__.state.db; if (!db) { console.log("NO DB", errors); process.exit(1); } console.log("db: artifacts", db.artifacts.length, "projects", db.projects.length, "insights", db.insights.length); const rep = db.artifacts.find((a) => a.report); console.log("report artifact:", rep && rep.id, rep && rep.report.title); const routes = ["#/overview", "#/stages", "#/members", "#/insights", "#/artifacts", "#/reportIndex", "#/projects", "#/sources", "#/record", "#/report/" + rep.id, "#/report/" + rep.id + "?ev=c-texture", "#/artifacts?focus=" + rep.id, "#/artifacts?priority=S&stage=S1", "#/insights?member=张伟"]; for (const r of routes) { w.location.hash = r; await new Promise((res) => setTimeout(res, 100)); w.dispatchEvent(new w.Event("hashchange")); w.__KF__.render(); await new Promise((res) => setTimeout(res, 50)); const view = w.document.getElementById("view"); const len = (view.textContent || "").length; const flag = view.querySelector(".error-box") ? " ERRORBOX" : ""; console.log(String(len).padStart(6), r, flag); } console.log("errors:", errors.length ? errors : "none"); process.exit(0); })();