1/** 2 * AI Software Factory — E2E: the `/w/default` magic alias (post-login entry). 3 * 4 * Regression guard for the domain-deploy report "after login … the page is 5 * not loaded, the status shows Offline, and none of the menu are clickable": 6 * the middleware + post-login redirect land on `/w/default`, a routing 7 * placeholder NO workspace has. The `[workspace]` layout used to fire 8 * `GET /workspaces/default` (a guaranteed 404) through `useWorkspace`, whose 9 * `isError` then blanked the whole shell (`return null`) BEFORE the index 10 * page could resolve the alias → no redirect, dead sidebar, and no page 11 * layout to open the realtime socket (chip stuck "Offline"). 12 * 13 * The fix: `useWorkspace` never fetches `default` (the index page owns 14 * resolving it). So this spec asserts the happy path the user reported as 15 * broken: login → `/w/default` resolves to a REAL workspace, the shell + 16 * menu render and navigate, a page loads, and the connection is live. 17 * 18 * (The existing suite always navigated to `/w/<realId>` directly, so it 19 * never exercised the alias and the bug survived.) 20 */ 21import { test, expect } from "../fixtures"; 22 23test.describe("Default workspace alias — post-login entry", () => { 24 test("resolves /w/default to a real workspace; shell + menu + page render", async ({ 25 authPage, 26 }) => { 27 // Track the doomed request: the layout must NOT fetch the alias. 28 const defaultGets: { url: string; status: number }[] = []; 29 authPage.on("response", (res) => { 30 const u = res.url(); 31 if (u.includes("/workspaces/default")) { 32 defaultGets.push({ url: u, status: res.status() }); 33 } 34 }); 35 36 await authPage.goto("/"); 37 38 // The alias must resolve to a REAL workspace id (not stay on "default"). 39 await authPage.waitForURL( 40 (u) => /\/w\/[^/]+$/.test(u.pathname) && !u.pathname.endsWith("/default"), 41 { timeout: 30_000 } 42 ); 43 44 // The index page auto-redirects to the first page — a REAL page layout 45 // must mount (this is also what opens the realtime socket). Wait for it, 46 // so the "Live" assertion below is deterministic (a socket opened on a 47 // page persists for the tab's lifetime). 48 await authPage.waitForURL(/\/page\//, { timeout: 30_000 }); 49 50 // A page actually loaded — the main content region is populated, not the 51 // blank shell the bug produced (`isError` → `return null`). 52 await expect(authPage.locator("#workspace-main")).not.toBeEmpty({ 53 timeout: 15_000 54 }); 55 56 // Realtime: the connection chip is NOT offline (the bug's "Offline" 57 // status). On a page the realtime socket connects → "Live" (amber 58 // "Reconnecting…" is tolerated for the brief window before connect). 59 const chip = authPage.getByTestId("connection-chip"); 60 await expect(chip).toBeVisible(); 61 await expect 62 .poll(async () => (await chip.getAttribute("aria-label")) ?? "", { 63 timeout: 15_000 64 }) 65 .toMatch(/^Connection: (Live|Reconnecting)/); 66 67 // The menu is live: the "Files" nav link is wired to the REAL workspace 68 // and navigating works (it used to point at /w/default/files → blank). 69 const filesLink = authPage 70 .getByRole("navigation", { name: "Primary" }) 71 .getByRole("link", { name: "Files" }); 72 await expect(filesLink).toBeVisible(); 73 // Wired to the REAL workspace (not the dead /w/default/files). 74 await expect(filesLink).toHaveAttribute("href", /^\/w\/[^/]+\/files$/); 75 await filesLink.click(); 76 await authPage.waitForURL(/\/files$/, { timeout: 15_000 }); 77 78 // The regression's signature request never fires. 79 expect( 80 defaultGets, 81 `GET /workspaces/default must not fire: ${JSON.stringify(defaultGets)}` 82 ).toHaveLength(0); 83 }); 84}); 85