diff --git a/.gitignore b/.gitignore index 4bf6d5386..d7ddf951c 100644 --- a/.gitignore +++ b/.gitignore @@ -593,3 +593,7 @@ src/ingestion/tools/seed/manifest.json # run, exactly like .env.compose.test-stand and the generated realm. .artifacts/ coverage.md + +# playwright-cli session output: snapshots, console logs and screenshots from +# driving the UI against a stand. +.playwright-cli/ diff --git a/src/frontend/src/routes/index.test.tsx b/src/frontend/src/routes/index.test.tsx new file mode 100644 index 000000000..088a5842c --- /dev/null +++ b/src/frontend/src/routes/index.test.tsx @@ -0,0 +1,55 @@ +// @vitest-environment jsdom +/** + * The preview flag can flip while "/" is mounted, and the route's `beforeLoad` + * guard does not run again for that — only the component can send the reader + * into the portal without a reload. + */ +vi.mock("@tanstack/react-router", async () => { + const { portalRouterMock } = await import("@/test/portal-router"); + return { + ...portalRouterMock(), + redirect: vi.fn(), + createFileRoute: () => (options: Record) => options, + }; +}); +vi.mock("@/auth", () => ({ + useViewer: () => ({ personId: "person-1" }), +})); +vi.mock("@/screens/dashboard", () => ({ + DashboardScreen: () =>
, +})); + +import { act, render, screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { setPortalEnabled } from "@/lib/portal/portal-store"; +import { portalRouter } from "@/test/portal-router"; + +import { Route } from "./index"; + +const Component = (Route as unknown as { component: () => React.ReactNode }) + .component; + +beforeEach(() => { + act(() => { + setPortalEnabled(false); + portalRouter.reset("/"); + }); +}); + +describe("/", () => { + it("renders the dashboard while the preview is off", () => { + render(); + expect(screen.getByTestId("dashboard")).toBeInTheDocument(); + expect(portalRouter.navigations).toEqual([]); + }); + + it("enters the portal when the preview is turned on", () => { + render(); + act(() => setPortalEnabled(true)); + expect(screen.queryByTestId("dashboard")).not.toBeInTheDocument(); + expect(portalRouter.navigations).toEqual([ + { to: "/portal", replace: true }, + ]); + }); +}); diff --git a/src/frontend/src/routes/index.tsx b/src/frontend/src/routes/index.tsx index 9af267ec0..6b31e1a01 100644 --- a/src/frontend/src/routes/index.tsx +++ b/src/frontend/src/routes/index.tsx @@ -1,7 +1,7 @@ -import { createFileRoute, redirect } from "@tanstack/react-router"; +import { Navigate, createFileRoute, redirect } from "@tanstack/react-router"; import { useViewer } from "@/auth"; -import { readPortalEnabled } from "@/lib/portal/portal-store"; +import { readPortalEnabled, usePortalEnabled } from "@/lib/portal/portal-store"; import { FullScreenLoading } from "@/components/full-screen-loading"; import { DashboardScreen } from "@/screens/dashboard"; @@ -17,6 +17,10 @@ export const Route = createFileRoute("/")({ function IndexRoute() { const { personId } = useViewer(); + const portal = usePortalEnabled(); + // `beforeLoad` only runs on navigation, so a reader who turns the preview on + // while standing here would sit on the dashboard until a reload. + if (portal) return ; // An authenticated session always carries the person id (the gateway JWT // `sub`); the loading fallback only shows in the brief window before the // store resolves.