smoke.js 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. /* smoke.js —— jsdom 冒烟:抓真实数据,跑完整 app,检查各视图能渲染、无异常。
  2. * 这是"浏览器验证之前"的粗筛;真实验收以 verify.js(Chrome)为准。 */
  3. const { JSDOM, VirtualConsole } = require("jsdom");
  4. const fs = require("fs");
  5. const path = require("path");
  6. const ROOT = "/opt/data/work/kangshifu/impl";
  7. const HEADERS = { "X-Parse-Application-Id": "FKieRbIoGt_main" };
  8. (async () => {
  9. const cache = {};
  10. async function pfetch(url) {
  11. if (url.includes("kangshifu.fmode.cn")) {
  12. const cls = url.match(/classes\/(\w+)/)[1];
  13. if (!cache[cls]) {
  14. const r = await fetch(url, { headers: HEADERS });
  15. cache[cls] = await r.text();
  16. }
  17. return { ok: true, status: 200, json: async () => JSON.parse(cache[cls]), text: async () => cache[cls] };
  18. }
  19. /* 本地文件走磁盘 */
  20. const p = path.join(ROOT, new URL(url, "http://x/").pathname);
  21. if (!fs.existsSync(p)) return { ok: false, status: 404, text: async () => "" };
  22. return { ok: true, status: 200, text: async () => fs.readFileSync(p, "utf8") };
  23. }
  24. const vc = new VirtualConsole();
  25. const errors = [];
  26. vc.on("jsdomError", (e) => errors.push("jsdomError: " + e.message));
  27. vc.on("error", (...a) => errors.push("console.error: " + a.join(" ")));
  28. const html = fs.readFileSync(path.join(ROOT, "index.html"), "utf8");
  29. const dom = new JSDOM(html, { url: "http://localhost/#/overview", runScripts: "outside-only", pretendToBeVisual: true, virtualConsole: vc });
  30. const w = dom.window;
  31. w.fetch = pfetch;
  32. w.CSS = w.CSS || { escape: (s) => s };
  33. if (!w.IntersectionObserver) w.IntersectionObserver = class { observe() {} disconnect() {} };
  34. for (const f of ["config", "taxonomy", "util", "data", "model", "query", "router",
  35. "views/common", "views/overview", "views/artifacts", "views/insights", "views/projects",
  36. "views/members", "views/stages", "views/sources", "views/detail", "views/report", "app"]) {
  37. w.eval(fs.readFileSync(path.join(ROOT, "js", f + ".js"), "utf8"));
  38. }
  39. await new Promise((r) => setTimeout(r, 1500));
  40. const db = w.__KF__.state.db;
  41. if (!db) { console.log("NO DB", errors); process.exit(1); }
  42. console.log("db: artifacts", db.artifacts.length, "projects", db.projects.length, "insights", db.insights.length);
  43. const rep = db.artifacts.find((a) => a.report);
  44. console.log("report artifact:", rep && rep.id, rep && rep.report.title);
  45. const routes = ["#/overview", "#/stages", "#/members", "#/insights", "#/artifacts", "#/reportIndex",
  46. "#/projects", "#/sources", "#/record", "#/report/" + rep.id, "#/report/" + rep.id + "?ev=c-texture",
  47. "#/artifacts?focus=" + rep.id, "#/artifacts?priority=S&stage=S1", "#/insights?member=张伟"];
  48. for (const r of routes) {
  49. w.location.hash = r;
  50. await new Promise((res) => setTimeout(res, 100));
  51. w.dispatchEvent(new w.Event("hashchange"));
  52. w.__KF__.render();
  53. await new Promise((res) => setTimeout(res, 50));
  54. const view = w.document.getElementById("view");
  55. const len = (view.textContent || "").length;
  56. const flag = view.querySelector(".error-box") ? " ERRORBOX" : "";
  57. console.log(String(len).padStart(6), r, flag);
  58. }
  59. console.log("errors:", errors.length ? errors : "none");
  60. process.exit(0);
  61. })();