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}… +

+ ); +}