From 7d0e65b2d7dc811b18374f65fd9016387e128129 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Thu, 23 Jul 2026 15:36:34 +0200 Subject: [PATCH 1/2] fix(dashboard): disable sidebar route prefetch Co-Authored-By: Claude --- src/shared/components/Sidebar.tsx | 2 ++ tests/unit/sidebar-prefetch-policy-8281.test.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/unit/sidebar-prefetch-policy-8281.test.ts diff --git a/src/shared/components/Sidebar.tsx b/src/shared/components/Sidebar.tsx index ed2188055e6..45e7af32317 100644 --- a/src/shared/components/Sidebar.tsx +++ b/src/shared/components/Sidebar.tsx @@ -431,6 +431,7 @@ export default function Sidebar({
diff --git a/tests/unit/sidebar-prefetch-policy-8281.test.ts b/tests/unit/sidebar-prefetch-policy-8281.test.ts new file mode 100644 index 00000000000..02c170cc711 --- /dev/null +++ b/tests/unit/sidebar-prefetch-policy-8281.test.ts @@ -0,0 +1,17 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; + +const SIDEBAR = "src/shared/components/Sidebar.tsx"; + +test("#8281: dashboard navigation opts out of automatic route prefetch", async () => { + const source = await readFile(SIDEBAR, "utf8"); + + const internalLink = source.match(//); + assert.ok(internalLink, "expected the sidebar's internal navigation Link"); + assert.match(internalLink[0], /prefetch=\{false\}/); + + const logoLink = source.match(//); + assert.ok(logoLink, "expected the sidebar logo home Link"); + assert.match(logoLink[0], /prefetch=\{false\}/); +}); From b3f89a86b9d2596774eecd525fe73e1bac31e818 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Thu, 23 Jul 2026 15:41:30 +0200 Subject: [PATCH 2/2] test(dashboard): cover sidebar prefetch traffic Co-Authored-By: Claude --- tests/e2e/navigation.spec.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/e2e/navigation.spec.ts b/tests/e2e/navigation.spec.ts index 0f23b14bff9..76db5db458b 100644 --- a/tests/e2e/navigation.spec.ts +++ b/tests/e2e/navigation.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from "@playwright/test"; +import { gotoDashboardRoute } from "./helpers/dashboardAuth"; test.describe("Dashboard Navigation", () => { test("redirects unauthenticated user to /login", async ({ page }) => { @@ -10,6 +11,30 @@ test.describe("Dashboard Navigation", () => { expect(url).toMatch(/\/(login|dashboard)/); }); + test("does not prefetch dashboard routes and preserves client navigation", async ({ page }) => { + const speculativeRequests: string[] = []; + + page.on("request", (request) => { + const headers = request.headers(); + const isRscPrefetch = + headers.rsc === "1" && + (headers["next-router-prefetch"] === "1" || + headers.purpose?.toLowerCase().includes("prefetch") || + headers["sec-purpose"]?.toLowerCase().includes("prefetch")); + + if (isRscPrefetch) speculativeRequests.push(request.url()); + }); + + await gotoDashboardRoute(page, "/home"); + await expect(page.getByRole("link", { name: /providers/i }).first()).toBeVisible(); + await page.waitForTimeout(500); + + expect(speculativeRequests).toEqual([]); + + await page.getByRole("link", { name: /providers/i }).first().click(); + await expect(page).toHaveURL(/\/dashboard\/providers/); + }); + test("login page renders with form elements", async ({ page }) => { await page.goto("/login"); // Should show some form of authentication UI