From 6c70b413e009748cbce597c7568c1b6b07d76cfb Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Fri, 24 Apr 2026 19:59:04 -0700 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20mock=20/cp/auth/me=20=E2=80=94=20Au?= =?UTF-8?q?thGate=20redirect=20was=20preventing=20canvas=20render?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sixth E2E bug, surfaced after the page.goto-domcontentloaded fix finally let the navigation complete. The harness now reaches the canvas-root selector wait but still times out because the canvas never renders: TimeoutError: page.waitForSelector: Timeout 45000ms exceeded. waiting for [aria-label="Molecule AI workspace canvas"] Root cause: canvas/src/components/AuthGate.tsx wraps the page, fetches /cp/auth/me on mount, and redirects to the login page when the response is 401. The bearer header we set via context.setExtraHTTPHeaders works for platform API calls but does NOT satisfy /cp/auth/me — that endpoint is cookie-based (WorkOS session). So: 1. AuthGate mounts 2. Calls fetchSession() → /cp/auth/me → 401 (no session cookie) 3. AuthGate transitions to anonymous → redirectToLogin() 4. Browser navigates away from tenant URL 5. The React Flow canvas root with the aria-label never mounts 6. waitForSelector times out at 45s Fix: context.route() intercepts /cp/auth/me and returns a fake Session JSON so AuthGate resolves to "authenticated" and renders its children. The session contents are cosmetic — Session.org_id and Session.user_id appear in a few canvas surfaces but never fail on dummy values. This is the cleanest fix path. Alternatives considered + rejected: - Add a ?e2e=1 backdoor to AuthGate: production code shouldn't have a "skip auth" flag, even gated. - Real WorkOS login flow in Playwright: too much overhead per run. - Skip the canvas UI test, test only API: defeats the point of the staging E2E (which is to catch UI regressions before promotion). After this lands the harness should reach the workspace-node click step and exercise tabs — only then can a real product bug (rather than a test-harness bug) surface. The 6-bug chain mapped to: 1. instance_status field name (#2066) 2. staging.moleculesai.app DNS zone (#2066) 3. X-Molecule-Org-Id TenantGuard header (#2066) 4. Hydration selector waited pre-click (#2066) 5. networkidle never settles (this commit's parent) 6. AuthGate /cp/auth/me redirect (this commit) Co-Authored-By: Claude Opus 4.7 (1M context) --- canvas/e2e/staging-tabs.spec.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/canvas/e2e/staging-tabs.spec.ts b/canvas/e2e/staging-tabs.spec.ts index 6d444d86..9cd93a4d 100644 --- a/canvas/e2e/staging-tabs.spec.ts +++ b/canvas/e2e/staging-tabs.spec.ts @@ -63,6 +63,30 @@ test.describe("staging canvas tabs", () => { Authorization: `Bearer ${tenantToken}`, }); + // canvas/src/components/AuthGate.tsx fetches /cp/auth/me on mount + // and redirects to the login page on 401. The bearer header above + // is for platform API calls — it does NOT satisfy /cp/auth/me, + // which is cookie-based (WorkOS session). Without this mock, the + // canvas page mounts AuthGate, sees 401 from /cp/auth/me, and + // redirects away from the tenant URL before the React Flow root + // ever renders. The [aria-label] selector wait then times out. + // + // Intercept /cp/auth/me + return a fake Session shape so AuthGate + // resolves to "authenticated" and renders {children}. The session + // contents are cosmetic — the canvas only inspects org_id/user_id + // in a few places that don't fail when these are dummy values. + await context.route("**/cp/auth/me", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + user_id: `e2e-test-user-${workspaceId}`, + org_id: "e2e-test-org", + email: "e2e@test.local", + }), + }), + ); + const consoleErrors: string[] = []; page.on("console", (msg) => { if (msg.type() === "error") {