]> acesimba.cloud Git - StationBuilder.git/commitdiff
test: add network-path first-load diagnostics for public nginx route
authorroot <root@localhost.localdomain>
Wed, 9 Sep 2026 04:35:51 +0000 (12:35 +0800)
committerroot <root@localhost.localdomain>
Wed, 9 Sep 2026 04:35:51 +0000 (12:35 +0800)
Request-level tracing + wire-size measurement via nginx access log; used to
diagnose the ~1min incognito stuck-loading (13.5MB uncompressed transfer,
antd.js 10.1MB with inline sourcemap). Post gzip+http2 fix verified ~1.1MB.

scripts/timing-network.mjs [new file with mode: 0644]
scripts/verify-public-firstload.mjs [new file with mode: 0644]

diff --git a/scripts/timing-network.mjs b/scripts/timing-network.mjs
new file mode 100644 (file)
index 0000000..57f0efa
--- /dev/null
@@ -0,0 +1,60 @@
+import { chromium } from "playwright";
+
+const PUB = "https://aidemo.istation.cloud/t/stationbuilder/";
+
+const runOnce = async (n) => {
+  const browser = await chromium.launch();
+  const ctx = await browser.newContext();
+  const page = await ctx.newPage();
+  const started = new Map(); // url -> t
+  const done = new Map();    // url -> {status,size,enc,ms}
+  const failed = new Map();
+  page.on("request", (r) => { started.set(r.url(), Date.now()); });
+  page.on("response", (r) => {
+    const h = r.headers();
+    done.set(r.url(), { status: r.status(), size: Number(h["content-length"]||0), enc: h["content-encoding"]||"-", ms: Date.now()-(started.get(r.url())||Date.now()) });
+  });
+  page.on("requestfailed", (r) => { failed.set(r.url(), r.failure()?.errorText || "?"); });
+
+  const t0 = Date.now();
+  let resolved = null;
+  try {
+    await page.goto(PUB, { waitUntil: "commit", timeout: 60000 });
+    await page.waitForFunction(() => {
+      const els = Array.from(document.querySelectorAll(".empty-state"));
+      return !els.some(e => /加载中/.test(e.textContent||""));
+    }, undefined, { timeout: 60000 });
+    resolved = Date.now() - t0;
+  } catch (e) {
+    resolved = "TIMEOUT/HANG";
+  }
+
+  const hung = [...started.keys()].filter(u => !done.has(u) && !failed.has(u));
+  const apiDone = [...done.keys()].filter(u => u.includes("/api/"));
+  console.log(`\n===== run ${n}: resolved=${resolved}ms =====`);
+  console.log(`requests started=${started.size} done=${done.size} failed=${failed.size}`);
+  console.log(`API responses received: ${apiDone.length}`);
+  for (const u of apiDone) console.log(`  API OK ${done.get(u).status} ${done.get(u).ms}ms ${u.replace(PUB,"").slice(0,70)}`);
+  if (failed.size) { console.log("failed:"); for (const [u,e] of failed) console.log(`  ${e} ${u.replace(PUB,"").slice(0,90)}`); }
+  if (hung.length) {
+    console.log(`HUNG requests (${hung.length}) — sent but no response ever:`);
+    for (const u of hung) console.log(`  HUNG [${((Date.now()-started.get(u))/1000).toFixed(0)}s] ${u.slice(0,110)}`);
+    // server-side check of same paths while browser is hung
+    console.log("server-side direct check of hung paths (from node, same moment):");
+    for (const u of hung.slice(0, 6)) {
+      const t = Date.now();
+      try {
+        const r = await fetch(u, { headers: { host: "aidemo.istation.cloud" }, signal: AbortSignal.timeout(8000) });
+        await r.text();
+        console.log(`  direct ${r.status} in ${Date.now()-t}ms  ${u.replace(PUB,"").slice(0,70)}`);
+      } catch (e) { console.log(`  direct ERR ${Date.now()-t}ms ${e.message}  ${u.replace(PUB,"").slice(0,70)}`); }
+    }
+  }
+  // slowest responses
+  const slow = [...done.entries()].filter(([,v])=>v.ms>3000).sort((a,b)=>b[1].ms-a[1].ms).slice(0,6);
+  if (slow.length) { console.log("responses slower than 3s:"); for (const [u,v] of slow) console.log(`  ${v.ms}ms ${(v.size/1024).toFixed(0)}KB ${u.replace(PUB,"").slice(0,80)}`); }
+  await browser.close();
+};
+
+for (let i = 1; i <= 3; i++) await runOnce(i);
+process.exit(0);
diff --git a/scripts/verify-public-firstload.mjs b/scripts/verify-public-firstload.mjs
new file mode 100644 (file)
index 0000000..e2670e0
--- /dev/null
@@ -0,0 +1,26 @@
+import { chromium } from "playwright";
+
+const PUB = "https://aidemo.istation.cloud/t/stationbuilder/";
+
+const browser = await chromium.launch();
+const ctx = await browser.newContext();
+const page = await ctx.newPage();
+let wireBytes = 0, reqCount = 0;
+page.on("response", async (r) => {
+  reqCount++;
+  wireBytes += Number(r.headers()["content-length"] || 0);
+});
+const t0 = Date.now();
+await page.goto(PUB, { waitUntil: "commit", timeout: 60000 });
+// real completion: app shell mounted AND no 加载中 anywhere
+await page.waitForSelector(".company-side-nav", { timeout: 60000 });
+await page.waitForFunction(() => {
+  const els = Array.from(document.querySelectorAll(".empty-state"));
+  return !els.some(e => /加载中/.test(e.textContent || ""));
+}, undefined, { timeout: 60000 });
+const loadMs = Date.now() - t0;
+await page.waitForTimeout(1500);
+console.log(`first load complete: ${loadMs}ms, requests=${reqCount}, wireBytes(with content-length)=${(wireBytes/1024).toFixed(0)}KB`);
+console.log(`protocol check: response headers via CDP skipped; side-nav visible + data loaded OK`);
+await browser.close();
+process.exit(0);