From 9139b86e2e8a22ef8a891234a11c280ce5382a8b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 16 Jun 2026 08:22:33 -0400 Subject: [PATCH] Add /policybench vanity redirect to policybench.org Add an ExternalRedirect component (react-router's Navigate can't target another domain) and route /policybench and /[countryId]/policybench to https://policybench.org, so the short paths land on the standalone benchmark site. Covered by a unit test. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/PolicyEngine.jsx | 11 ++++++ .../routing/externalRedirect.test.js | 38 +++++++++++++++++++ src/routing/ExternalRedirect.jsx | 21 ++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/__tests__/routing/externalRedirect.test.js create mode 100644 src/routing/ExternalRedirect.jsx diff --git a/src/PolicyEngine.jsx b/src/PolicyEngine.jsx index 8a4ed9824..721334f5c 100644 --- a/src/PolicyEngine.jsx +++ b/src/PolicyEngine.jsx @@ -45,6 +45,7 @@ import style from "./style"; import RedirectToCountry from "./routing/RedirectToCountry"; import CountryIdLayout from "./routing/CountryIdLayout"; import RedirectBlogPost from "./routing/RedirectBlogPost"; +import ExternalRedirect from "./routing/ExternalRedirect"; import { StatusPage } from "./pages/StatusPage"; import ManifestosComparison from "./applets/ManifestosComparison"; import DeveloperLayout from "./pages/DeveloperLayout"; @@ -346,6 +347,11 @@ export default function PolicyEngine() { } /> } /> } /> + {/* Vanity redirect: /[countryId]/policybench -> policybench.org */} + } + /> } /> } /> + {/* Vanity redirect: bare /policybench -> policybench.org */} + } + /> } /> } /> { + const replaceMock = jest.fn(); + let originalLocation; + + beforeAll(() => { + originalLocation = window.location; + Object.defineProperty(window, "location", { + configurable: true, + writable: true, + value: { replace: replaceMock, href: "http://localhost/" }, + }); + }); + + afterAll(() => { + Object.defineProperty(window, "location", { + configurable: true, + writable: true, + value: originalLocation, + }); + }); + + beforeEach(() => replaceMock.mockClear()); + + test("redirects the browser to the external URL", () => { + render(); + expect(replaceMock).toHaveBeenCalledWith("https://policybench.org"); + }); + + test("renders a fallback link to the destination", () => { + render(); + expect(screen.getByRole("link").getAttribute("href")).toBe( + "https://policybench.org", + ); + }); +}); diff --git a/src/routing/ExternalRedirect.jsx b/src/routing/ExternalRedirect.jsx new file mode 100644 index 000000000..184e0f3ca --- /dev/null +++ b/src/routing/ExternalRedirect.jsx @@ -0,0 +1,21 @@ +import { useEffect } from "react"; + +/** + * Redirects the browser to an external URL outside the SPA's router. + * + * react-router's only resolves in-app paths, so vanity routes + * that point at another domain (e.g. /policybench -> https://policybench.org) + * use this instead. Renders a short fallback link in case the redirect is + * slow or scripting is disabled. + */ +export default function ExternalRedirect({ to }) { + useEffect(() => { + window.location.replace(to); + }, [to]); + + return ( +

+ Redirecting to {to}… +

+ ); +}