/** * AI Software Factory — E2E: the `/w/default` magic alias (post-login entry). * * Regression guard for the domain-deploy report "after login … the page is * not loaded, the status shows Offline, and none of the menu are clickable": * the middleware + post-login redirect land on `/w/default`, a routing * placeholder NO workspace has. The `[workspace]` layout used to fire * `GET /workspaces/default` (a guaranteed 404) through `useWorkspace`, whose * `isError` then blanked the whole shell (`return null`) BEFORE the index * page could resolve the alias → no redirect, dead sidebar, and no page * layout to open the realtime socket (chip stuck "Offline"). * * The fix: `useWorkspace` never fetches `default` (the index page owns * resolving it). So this spec asserts the happy path the user reported as * broken: login → `/w/default` resolves to a REAL workspace, the shell + * menu render and navigate, a page loads, and the connection is live. * * (The existing suite always navigated to `/w/` directly, so it * never exercised the alias and the bug survived.) */ import { test, expect } from "../fixtures"; test.describe("Default workspace alias — post-login entry", () => { test("resolves /w/default to a real workspace; shell + menu + page render", async ({ authPage, }) => { // Track the doomed request: the layout must NOT fetch the alias. const defaultGets: { url: string; status: number }[] = []; authPage.on("response", (res) => { const u = res.url(); if (u.includes("/workspaces/default")) { defaultGets.push({ url: u, status: res.status() }); } }); await authPage.goto("/"); // The alias must resolve to a REAL workspace id (not stay on "default"). await authPage.waitForURL( (u) => /\/w\/[^/]+$/.test(u.pathname) && !u.pathname.endsWith("/default"), { timeout: 30_000 } ); // The index page auto-redirects to the first page — a REAL page layout // must mount (this is also what opens the realtime socket). Wait for it, // so the "Live" assertion below is deterministic (a socket opened on a // page persists for the tab's lifetime). await authPage.waitForURL(/\/page\//, { timeout: 30_000 }); // A page actually loaded — the main content region is populated, not the // blank shell the bug produced (`isError` → `return null`). await expect(authPage.locator("#workspace-main")).not.toBeEmpty({ timeout: 15_000 }); // Realtime: the connection chip is NOT offline (the bug's "Offline" // status). On a page the realtime socket connects → "Live" (amber // "Reconnecting…" is tolerated for the brief window before connect). const chip = authPage.getByTestId("connection-chip"); await expect(chip).toBeVisible(); await expect .poll(async () => (await chip.getAttribute("aria-label")) ?? "", { timeout: 15_000 }) .toMatch(/^Connection: (Live|Reconnecting)/); // The menu is live: the "Files" nav link is wired to the REAL workspace // and navigating works (it used to point at /w/default/files → blank). const filesLink = authPage .getByRole("navigation", { name: "Primary" }) .getByRole("link", { name: "Files" }); await expect(filesLink).toBeVisible(); // Wired to the REAL workspace (not the dead /w/default/files). await expect(filesLink).toHaveAttribute("href", /^\/w\/[^/]+\/files$/); await filesLink.click(); await authPage.waitForURL(/\/files$/, { timeout: 15_000 }); // The regression's signature request never fires. expect( defaultGets, `GET /workspaces/default must not fire: ${JSON.stringify(defaultGets)}` ).toHaveLength(0); }); });