From 0987e222ddfe471211ed5456f74d4e876d5f4c26 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Thu, 14 May 2026 15:42:55 +0000 Subject: [PATCH] =?UTF-8?q?fix(web):=20Phase=203=20hotfix=20=E2=80=94=20Us?= =?UTF-8?q?ersPage.test.tsx=20Router=20context=20+=20Breadcrumbs=20defensi?= =?UTF-8?q?ve=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI failure on Phase 3 commit (e761ae40): FAIL src/pages/auth/UsersPage.test.tsx > 8 tests (all) Error: useLocation() may be used only in the context of a component. Root cause: Phase 3 wired into PageHeader (UX-M5 closure). UsersPage renders PageHeader at the top of its tree. UsersPage.test.tsx was the only auth-page test file whose renderWithProviders helper lacked a MemoryRouter wrapper — every other sibling (BreakglassPage, KeysPage, OIDCProvidersPage, SessionsPage, RolesPage, AuthSettingsPage, ApprovalsPage, etc.) already wraps in MemoryRouter. The 2026-05-11 MED-11 closure that shipped UsersPage + 8 tests predated Phase 3 and so predated the need for Router context in test trees. Fix is two-layered: (1) Targeted — add MemoryRouter to UsersPage.test.tsx renderWithProviders so the test tree has the same Router context the production tree gets from in main.tsx. (2) Defensive — Breadcrumbs.tsx now gates useLocation() behind useInRouterContext(). If a future test mounts PageHeader (or any other Breadcrumbs consumer) without a Router wrapper, the component renders null instead of crashing. The actual useLocation() + render work moves into a BreadcrumbsInner sub-component called only after the Router-context check passes. This prevents the same class of failure ever happening again — any new auth-page test author who forgets MemoryRouter will see a missing breadcrumb (cosmetic), not 8 red test failures. Verification (sandbox): • TypeScript clean — npx tsc --noEmit exits 0 • UsersPage suite — 8/8 green (was 0/8 in CI) • Breadcrumbs suite — 8/8 green • All sibling auth tests — 72/72 green (BreakglassPage 6 + KeysPage 7 + OIDCProvidersPage 13 + SessionsPage 11 + RolesPage 6 + AuthSettingsPage 6 + ApprovalsPage 23). Unchanged because they already had MemoryRouter; pinned to confirm defensive guard didn't regress them. CI expectation: web-test job goes from red to green on next push. No behavior change to production — Breadcrumbs still renders identically under at runtime; useInRouterContext returns true and delegates to BreadcrumbsInner unchanged. Touches: web/src/components/Breadcrumbs.tsx (+14 / -2) web/src/pages/auth/UsersPage.test.tsx (+8 / -1) --- web/src/components/Breadcrumbs.tsx | 14 +++++++++++++- web/src/pages/auth/UsersPage.test.tsx | 8 +++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/web/src/components/Breadcrumbs.tsx b/web/src/components/Breadcrumbs.tsx index 273e70a..69e78f6 100644 --- a/web/src/components/Breadcrumbs.tsx +++ b/web/src/components/Breadcrumbs.tsx @@ -20,7 +20,7 @@ // upgrading to data-router-driven crumbs is a future task once the // router migration ships. -import { Link, useLocation } from 'react-router-dom'; +import { Link, useLocation, useInRouterContext } from 'react-router-dom'; import { ChevronRight } from 'lucide-react'; // pathSegmentLabels — map first-segment URL keys to human labels. @@ -126,7 +126,19 @@ function looksLikeID(s: string): boolean { return s.includes('-') || /^[a-f0-9]{8,}$/i.test(s); } +// Breadcrumbs is the public entry. Defensive against missing Router +// context (a test that mounts a PageHeader without a +// wrapper used to crash here). useLocation() throws an invariant +// error if there's no Router; gate it behind useInRouterContext() +// + render the actual logic in a sibling so useLocation() is only +// called when we know the context is present. export default function Breadcrumbs() { + const inRouter = useInRouterContext(); + if (!inRouter) return null; + return ; +} + +function BreadcrumbsInner() { const { pathname } = useLocation(); const crumbs = crumbsFor(pathname); diff --git a/web/src/pages/auth/UsersPage.test.tsx b/web/src/pages/auth/UsersPage.test.tsx index 2b86e4c..fab106e 100644 --- a/web/src/pages/auth/UsersPage.test.tsx +++ b/web/src/pages/auth/UsersPage.test.tsx @@ -1,6 +1,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { MemoryRouter } from 'react-router-dom'; import type { ReactNode } from 'react'; // ============================================================================= @@ -29,8 +30,13 @@ function renderWithProviders(ui: ReactNode) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, }); + // MemoryRouter required because PageHeader now renders Breadcrumbs + // (Phase 3 UX-M5), which calls useLocation() and throws when there + // is no Router context in the tree. return render( - {ui}, + + {ui} + , ); }