From c2504d9361f4a72b371ff117d51b162ae839326b Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Fri, 24 Apr 2026 19:43:46 -0700 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20page.goto=20waitUntil=20networkidle?= =?UTF-8?q?=20never=20settles=20=E2=80=94=20switch=20to=20domcontentloaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fifth E2E bug surfaced by the previous run. After the four setup- phase fixes (instance_status, DNS zone, X-Molecule-Org-Id, hydration selector) plus CP#259 ending the pq cache class, the harness finally reached the actual page navigation step — and timed out there: TimeoutError: page.goto: Timeout 45000ms exceeded. navigating to "https://...staging.moleculesai.app/", waiting until "networkidle" `waitUntil: "networkidle"` waits for 500ms of network silence. The canvas keeps a WebSocket connection open + polls /events and /workspaces every few seconds for status updates, so the network is never idle — page.goto sits on it until the default 45s timeout and throws. Fix: switch to `waitUntil: "domcontentloaded"`. Returns as soon as the HTML is parsed. React hydration plus the existing `waitForSelector` line below is what actually gates ready-for- interaction; the goto's job is just to land on the page. This is a generally-applicable lesson — networkidle is broken for any SPA with a heartbeat. Notably, our existing canvas unit tests that mock @xyflow/react and don't open WebSockets DON'T hit this, which is why this only surfaces against staging. Co-Authored-By: Claude Opus 4.7 (1M context) --- canvas/e2e/staging-tabs.spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/canvas/e2e/staging-tabs.spec.ts b/canvas/e2e/staging-tabs.spec.ts index fa99fa5e..6d444d86 100644 --- a/canvas/e2e/staging-tabs.spec.ts +++ b/canvas/e2e/staging-tabs.spec.ts @@ -70,7 +70,13 @@ test.describe("staging canvas tabs", () => { } }); - await page.goto(tenantURL, { waitUntil: "networkidle" }); + // waitUntil="networkidle" is wrong here — the canvas keeps a + // WebSocket open + polls /events and /workspaces every few + // seconds, so the network is *never* idle for 500ms. page.goto + // would hang until its 45s default timeout. "domcontentloaded" + // returns as soon as the HTML is parsed; React hydration + the + // selector wait below is what actually gates ready-for-interaction. + await page.goto(tenantURL, { waitUntil: "domcontentloaded" }); // Canvas hydration races WebSocket connect + /workspaces fetch. // Wait for the React Flow canvas wrapper (always present once