diff --git a/packages/react-router/__tests__/dom/concurrent-mode-navigations-test.tsx b/packages/react-router/__tests__/dom/concurrent-mode-navigations-test.tsx index b9f8d17be0..a2be53a977 100644 --- a/packages/react-router/__tests__/dom/concurrent-mode-navigations-test.tsx +++ b/packages/react-router/__tests__/dom/concurrent-mode-navigations-test.tsx @@ -17,9 +17,9 @@ import { screen, waitFor, } from "@testing-library/react"; -import { JSDOM } from "jsdom"; import { createDeferred } from "../router/utils/utils"; import getHtml from "../utils/getHtml"; +import getWindow from "../utils/getWindow"; describe("Handles concurrent mode features during navigations", () => { function getComponents() { @@ -149,7 +149,7 @@ describe("Handles concurrent mode features during navigations", () => { getComponents(); let { container } = render( - + } /> { getComponents(); let { container } = render( - + } /> { getComponents(); let { container } = render( - + } /> } /> @@ -324,7 +324,7 @@ describe("Handles concurrent mode features during navigations", () => { getComponents(); let { container } = render( - + } /> } /> @@ -356,10 +356,3 @@ describe("Handles concurrent mode features during navigations", () => { }); }); }); - -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/data-browser-router-legacy-formdata-test.tsx b/packages/react-router/__tests__/dom/data-browser-router-legacy-formdata-test.tsx index e1804680e9..8140fbbb3c 100644 --- a/packages/react-router/__tests__/dom/data-browser-router-legacy-formdata-test.tsx +++ b/packages/react-router/__tests__/dom/data-browser-router-legacy-formdata-test.tsx @@ -1,4 +1,3 @@ -import { JSDOM } from "jsdom"; // Drop support for the submitter parameter, as in a legacy browser. This // needs to be done before react-router-dom is required, since it does some // FormData detection. @@ -14,13 +13,14 @@ import { createHashRouter, createRoutesFromElements, } from "../../index"; +import getWindow from "../utils/getWindow"; testDomRouter("", createBrowserRouter, (url) => - getWindowImpl(url, false), + getWindow(url, false), ); testDomRouter("", createHashRouter, (url) => - getWindowImpl(url, true), + getWindow(url, true), ); function testDomRouter( @@ -120,10 +120,3 @@ function testDomRouter( }); }); } - -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/data-browser-router-test.tsx b/packages/react-router/__tests__/dom/data-browser-router-test.tsx index 58a4f8a659..ab9bf7ea35 100644 --- a/packages/react-router/__tests__/dom/data-browser-router-test.tsx +++ b/packages/react-router/__tests__/dom/data-browser-router-test.tsx @@ -5,7 +5,6 @@ import { screen, waitFor, } from "@testing-library/react"; -import { JSDOM } from "jsdom"; import * as React from "react"; import type { RouteObject, @@ -41,14 +40,15 @@ import { } from "../../index"; import getHtml from "../utils/getHtml"; +import getWindow from "../utils/getWindow"; import { createDeferred, tick } from "../router/utils/utils"; testDomRouter("", createBrowserRouter, (url) => - getWindowImpl(url, false), + getWindow(url, false), ); testDomRouter("", createHashRouter, (url) => - getWindowImpl(url, true), + getWindow(url, true), ); function testDomRouter( @@ -8002,10 +8002,3 @@ function testDomRouter( } }); } - -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/flush-sync-navigations-test.tsx b/packages/react-router/__tests__/dom/flush-sync-navigations-test.tsx index e4f5cee541..d545544fe1 100644 --- a/packages/react-router/__tests__/dom/flush-sync-navigations-test.tsx +++ b/packages/react-router/__tests__/dom/flush-sync-navigations-test.tsx @@ -1,6 +1,5 @@ import * as React from "react"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { JSDOM } from "jsdom"; import { createBrowserRouter, @@ -9,6 +8,7 @@ import { useFetcher, } from "../../index"; import { RouterProvider } from "../../lib/dom-export/dom-router-provider"; +import getWindow from "../utils/getWindow"; describe("flushSync", () => { it("wraps useNavigate updates in flushSync when specified", async () => { @@ -42,7 +42,7 @@ describe("flushSync", () => { }, ], { - window: getWindowImpl("/"), + window: getWindow("/"), }, ); render(); @@ -116,7 +116,7 @@ describe("flushSync", () => { }, ], { - window: getWindowImpl("/"), + window: getWindow("/"), }, ); render(); @@ -176,7 +176,7 @@ describe("flushSync", () => { }, ], { - window: getWindowImpl("/"), + window: getWindow("/"), }, ); render(); @@ -239,7 +239,7 @@ describe("flushSync", () => { }, ], { - window: getWindowImpl("/"), + window: getWindow("/"), }, ); render(); @@ -267,10 +267,3 @@ describe("flushSync", () => { router.dispose(); }); }); - -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/nav-link-active-test.tsx b/packages/react-router/__tests__/dom/nav-link-active-test.tsx index 4a88621cc2..11449c9c77 100644 --- a/packages/react-router/__tests__/dom/nav-link-active-test.tsx +++ b/packages/react-router/__tests__/dom/nav-link-active-test.tsx @@ -1,6 +1,5 @@ import { render, fireEvent, waitFor, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; -import { JSDOM } from "jsdom"; import * as React from "react"; import * as TestRenderer from "react-test-renderer"; import { @@ -14,6 +13,7 @@ import { createBrowserRouter, createRoutesFromElements, } from "../../index"; +import getWindow from "../utils/getWindow"; describe("NavLink", () => { describe("when it does not match", () => { @@ -1062,10 +1062,3 @@ function createDeferred() { reject, }; } - -function getWindow(initialUrl: string): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "https://remix.run/" }); - dom.window.history.replaceState(null, "", initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/scroll-restoration-test.tsx b/packages/react-router/__tests__/dom/scroll-restoration-test.tsx index 422e63cdaf..51e0ad438f 100644 --- a/packages/react-router/__tests__/dom/scroll-restoration-test.tsx +++ b/packages/react-router/__tests__/dom/scroll-restoration-test.tsx @@ -1,9 +1,9 @@ -import { JSDOM } from "jsdom"; import * as React from "react"; import { render, fireEvent, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; import getHtml from "../utils/getHtml"; +import getWindow from "../utils/getWindow"; import { Link, Outlet, @@ -22,7 +22,7 @@ describe(`ScrollRestoration`, () => { .spyOn(console, "warn") .mockImplementation(() => {}); - let testWindow = getWindowImpl("/base"); + let testWindow = getWindow("/base"); const mockScroll = jest.fn(); window.scrollTo = mockScroll; @@ -76,7 +76,7 @@ describe(`ScrollRestoration`, () => { it("removes the basename from the location provided to getKey", () => { let getKey = jest.fn(() => "mykey"); - let testWindow = getWindowImpl("/base"); + let testWindow = getWindow("/base"); window.scrollTo = () => {}; let router = createBrowserRouter( @@ -131,7 +131,7 @@ describe(`ScrollRestoration`, () => { .spyOn(console, "warn") .mockImplementation(() => {}); - let testWindow = getWindowImpl("/base"); + let testWindow = getWindow("/base"); const mockScroll = jest.fn(); window.scrollTo = mockScroll; @@ -346,10 +346,3 @@ const testPages = [ }, }, ]; - -function getWindowImpl(initialUrl: string): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/dom/special-characters-test.tsx b/packages/react-router/__tests__/dom/special-characters-test.tsx index 298d870986..48e9cd2669 100644 --- a/packages/react-router/__tests__/dom/special-characters-test.tsx +++ b/packages/react-router/__tests__/dom/special-characters-test.tsx @@ -1,6 +1,5 @@ /* eslint-disable jest/expect-expect */ -import { JSDOM } from "jsdom"; import * as React from "react"; import { cleanup, @@ -28,6 +27,7 @@ import { useNavigate, useParams, } from "../../index"; +import getWindow from "../utils/getWindow"; import getHtml from "../utils/getHtml"; /** @@ -160,12 +160,7 @@ describe("special character tests", () => { expectedLocation: Omit, expectedParams = {}, ) { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { - url: "https://remix.run/", - }); - let testWindow = dom.window as unknown as Window; - testWindow.history.replaceState(null, "", navigatePath); + let testWindow = getWindow(navigatePath); function Comp({ heading }) { return ( @@ -618,13 +613,7 @@ describe("special character tests", () => { expectedLocation: Omit, expectedParams = {}, ) { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { - url: "https://remix.run/", - }); - let testWindow = dom.window as unknown as Window; - testWindow.history.replaceState(null, "", path); - + let testWindow = getWindow(path); let renderedUseLocation: Omit | null = null; let renderedParams: Params | null = null; @@ -1112,13 +1101,3 @@ describe("special character tests", () => { }); }); }); - -function getWindow(initialPath) { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { - url: "https://remix.run/", - }); - let testWindow = dom.window as unknown as Window; - testWindow.history.pushState({}, "", initialPath); - return testWindow; -} diff --git a/packages/react-router/__tests__/dom/trailing-slashes-test.tsx b/packages/react-router/__tests__/dom/trailing-slashes-test.tsx index b820fcfe7c..f1258bfe69 100644 --- a/packages/react-router/__tests__/dom/trailing-slashes-test.tsx +++ b/packages/react-router/__tests__/dom/trailing-slashes-test.tsx @@ -1,4 +1,3 @@ -import { JSDOM } from "jsdom"; import * as React from "react"; import * as ReactDOM from "react-dom/client"; import { act } from "@testing-library/react"; @@ -13,6 +12,7 @@ import { useSearchParams, useNavigate, } from "../../index"; +import getWindow from "../utils/getWindow"; describe("trailing slashes", () => { let node: HTMLDivElement; @@ -29,7 +29,7 @@ describe("trailing slashes", () => { describe("in a element", () => { describe("with a basename that does not contain a trailing slash", () => { test("never includes trailing slashes on root links (index route)", () => { - let window = getWindowImpl("/app"); + let window = getWindow("/app"); act(() => { ReactDOM.createRoot(node).render( @@ -63,7 +63,7 @@ describe("trailing slashes", () => { }); test('never includes trailing slashes on root links (path="")', () => { - let window = getWindowImpl("/app"); + let window = getWindow("/app"); act(() => { ReactDOM.createRoot(node).render( @@ -97,7 +97,7 @@ describe("trailing slashes", () => { }); test("allows non-root links to control trailing slashes", () => { - let window = getWindowImpl("/app/parent/child"); + let window = getWindow("/app/parent/child"); act(() => { ReactDOM.createRoot(node).render( @@ -191,7 +191,7 @@ describe("trailing slashes", () => { describe("with a basename that contains a trailing slash", () => { test("always contains trailing slashes on root links (index route)", () => { - let window = getWindowImpl("/app/"); + let window = getWindow("/app/"); act(() => { ReactDOM.createRoot(node).render( @@ -225,7 +225,7 @@ describe("trailing slashes", () => { }); test('always contains trailing slashes on root links (path="" route)', () => { - let window = getWindowImpl("/app/"); + let window = getWindow("/app/"); act(() => { ReactDOM.createRoot(node).render( @@ -259,7 +259,7 @@ describe("trailing slashes", () => { }); test("allows non-root links to control trailing slashes", () => { - let window = getWindowImpl("/app/parent/child"); + let window = getWindow("/app/parent/child"); act(() => { ReactDOM.createRoot(node).render( @@ -355,9 +355,9 @@ describe("trailing slashes", () => { describe("in a element", () => { describe("with a basename that does not contain a trailing slash", () => { test("never includes trailing slashes on root links (/)", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -369,13 +369,13 @@ describe("trailing slashes", () => { , ); }); - expect(window.location.href).toBe("https://remix.run/foo"); + expect(window.location.href).toBe("http://localhost/foo"); }); test("never includes trailing slashes on root links (../)", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -387,14 +387,14 @@ describe("trailing slashes", () => { , ); }); - expect(window.location.href).toBe("https://remix.run/foo"); + expect(window.location.href).toBe("http://localhost/foo"); }); test("allows non-root links to leave off trailing slashes", () => { - let window = getWindowImpl("/foo"); + let window = getWindow("/foo"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo"); + expect(window.location.href).toBe("http://localhost/foo"); act(() => { ReactDOM.createRoot(node).render( @@ -407,14 +407,14 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); }); test("allows non-root links to include trailing slashes", () => { - let window = getWindowImpl("/foo"); + let window = getWindow("/foo"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo"); + expect(window.location.href).toBe("http://localhost/foo"); act(() => { ReactDOM.createRoot(node).render( @@ -427,15 +427,15 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); }); }); describe("with a basename that contains a trailing slash", () => { test("always includes trailing slashes on root links (/)", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -447,13 +447,13 @@ describe("trailing slashes", () => { , ); }); - expect(window.location.href).toBe("https://remix.run/foo/"); + expect(window.location.href).toBe("http://localhost/foo/"); }); test("always includes trailing slashes on root links (../)", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -465,14 +465,14 @@ describe("trailing slashes", () => { , ); }); - expect(window.location.href).toBe("https://remix.run/foo/"); + expect(window.location.href).toBe("http://localhost/foo/"); }); test("allows non-root links to leave off trailing slashes", () => { - let window = getWindowImpl("/foo/"); + let window = getWindow("/foo/"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/"); + expect(window.location.href).toBe("http://localhost/foo/"); act(() => { ReactDOM.createRoot(node).render( @@ -485,14 +485,14 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); }); test("allows non-root links to include trailing slashes", () => { - let window = getWindowImpl("/foo/"); + let window = getWindow("/foo/"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/"); + expect(window.location.href).toBe("http://localhost/foo/"); act(() => { ReactDOM.createRoot(node).render( @@ -505,16 +505,16 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); }); }); describe("empty string paths", () => { it("should not add trailing slashes", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -527,14 +527,14 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); }); it("should preserve trailing slash", () => { - let window = getWindowImpl("/foo/bar/"); + let window = getWindow("/foo/bar/"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); act(() => { ReactDOM.createRoot(node).render( @@ -547,16 +547,16 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); }); }); describe("current location '.' paths", () => { it("should not add trailing slash", () => { - let window = getWindowImpl("/foo/bar"); + let window = getWindow("/foo/bar"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); act(() => { ReactDOM.createRoot(node).render( @@ -569,14 +569,14 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar"); + expect(window.location.href).toBe("http://localhost/foo/bar"); }); it("should preserve trailing slash", () => { - let window = getWindowImpl("/foo/bar/"); + let window = getWindow("/foo/bar/"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); act(() => { ReactDOM.createRoot(node).render( @@ -589,17 +589,17 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/bar/"); + expect(window.location.href).toBe("http://localhost/foo/bar/"); }); }); }); describe("when using setSearchParams", () => { it("should not include trailing slash via useSearchParams", () => { - let window = getWindowImpl("/foo"); + let window = getWindow("/foo"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo"); + expect(window.location.href).toBe("http://localhost/foo"); function SetSearchParams() { let [, setSearchParams] = useSearchParams(); @@ -620,14 +620,14 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo?key=value"); + expect(window.location.href).toBe("http://localhost/foo?key=value"); }); it("should include trailing slash via useSearchParams when basename has one", () => { - let window = getWindowImpl("/foo/"); + let window = getWindow("/foo/"); jest.spyOn(window.history, "pushState"); - expect(window.location.href).toBe("https://remix.run/foo/"); + expect(window.location.href).toBe("http://localhost/foo/"); function SetSearchParams() { let [, setSearchParams] = useSearchParams(); @@ -648,18 +648,11 @@ describe("trailing slashes", () => { ); }); - expect(window.location.href).toBe("https://remix.run/foo/?key=value"); + expect(window.location.href).toBe("http://localhost/foo/?key=value"); }); }); }); -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "https://remix.run/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} - function SingleNavigate({ to }: { to: To }) { let navigate = useNavigate(); React.useEffect(() => { diff --git a/packages/react-router/__tests__/dom/use-prompt-test.tsx b/packages/react-router/__tests__/dom/use-prompt-test.tsx index a4841049b5..47f492c56f 100644 --- a/packages/react-router/__tests__/dom/use-prompt-test.tsx +++ b/packages/react-router/__tests__/dom/use-prompt-test.tsx @@ -1,4 +1,5 @@ import * as React from "react"; +import "@testing-library/jest-dom"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { Link, @@ -6,8 +7,7 @@ import { createBrowserRouter, unstable_usePrompt as usePrompt, } from "../../index"; -import "@testing-library/jest-dom"; -import { JSDOM } from "jsdom"; +import getWindow from "../utils/getWindow"; describe("usePrompt", () => { afterEach(() => { @@ -16,7 +16,7 @@ describe("usePrompt", () => { describe("when navigation is blocked", () => { it("shows window.confirm and blocks navigation when it returns false", async () => { - let testWindow = getWindowImpl("/"); + let testWindow = getWindow("/"); const windowConfirmMock = jest .spyOn(window, "confirm") .mockImplementationOnce(() => false); @@ -49,7 +49,7 @@ describe("usePrompt", () => { }); it("shows window.confirm and navigates when it returns true", async () => { - let testWindow = getWindowImpl("/"); + let testWindow = getWindow("/"); const windowConfirmMock = jest .spyOn(window, "confirm") .mockImplementationOnce(() => true); @@ -84,7 +84,7 @@ describe("usePrompt", () => { describe("when navigation is not blocked", () => { it("navigates without showing window.confirm", async () => { - let testWindow = getWindowImpl("/"); + let testWindow = getWindow("/"); const windowConfirmMock = jest .spyOn(window, "confirm") .mockImplementation(() => true); @@ -117,10 +117,3 @@ describe("usePrompt", () => { }); }); }); - -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} diff --git a/packages/react-router/__tests__/router/TestSequences/PushStateInvalid.ts b/packages/react-router/__tests__/router/TestSequences/PushStateInvalid.ts index 7756fdf64b..e5e6a9d3f2 100644 --- a/packages/react-router/__tests__/router/TestSequences/PushStateInvalid.ts +++ b/packages/react-router/__tests__/router/TestSequences/PushStateInvalid.ts @@ -1,7 +1,6 @@ -import type { DOMWindow } from "jsdom"; import type { History } from "../../../lib/router/history"; -export default function PushState(history: History, window: DOMWindow) { +export default function PushState(history: History, window: Window) { let err = new DOMException("ERROR", "DataCloneError"); jest.spyOn(window.history, "pushState").mockImplementation(() => { throw err; diff --git a/packages/react-router/__tests__/router/browser-test.ts b/packages/react-router/__tests__/router/browser-test.ts index cf3d969cb5..b994d0f1bf 100644 --- a/packages/react-router/__tests__/router/browser-test.ts +++ b/packages/react-router/__tests__/router/browser-test.ts @@ -1,7 +1,5 @@ /* eslint-disable jest/expect-expect */ -import { JSDOM } from "jsdom"; - import { type BrowserHistory, createBrowserHistory, @@ -22,18 +20,16 @@ import EncodedReservedCharacters from "./TestSequences/EncodedReservedCharacters import GoBack from "./TestSequences/GoBack"; import GoForward from "./TestSequences/GoForward"; import ListenPopOnly from "./TestSequences/ListenPopOnly"; +import getWindow from "../utils/getWindow"; describe("a browser history", () => { let history: BrowserHistory; - let dom: JSDOM; + let testWindow: Window; beforeEach(() => { // Need to use our own custom DOM in order to get a working history - dom = new JSDOM(`

History Example

`, { - url: "https://example.org/", - }); - dom.window.history.replaceState(null, "", "/"); - history = createBrowserHistory({ window: dom.window as unknown as Window }); + testWindow = getWindow("/"); + history = createBrowserHistory({ window: testWindow }); }); it("knows how to create hrefs from location objects", () => { @@ -97,7 +93,7 @@ describe("a browser history", () => { }); it("re-throws when using non-serializable state", () => { - PushStateInvalid(history, dom.window); + PushStateInvalid(history, testWindow); }); }); diff --git a/packages/react-router/__tests__/router/hash-base-test.ts b/packages/react-router/__tests__/router/hash-base-test.ts index 37fd21ef83..3a88571f98 100644 --- a/packages/react-router/__tests__/router/hash-base-test.ts +++ b/packages/react-router/__tests__/router/hash-base-test.ts @@ -1,41 +1,32 @@ -import { JSDOM } from "jsdom"; - import type { HashHistory } from "../../lib/router/history"; import { createHashHistory } from "../../lib/router/history"; +import getWindow from "../utils/getWindow"; describe("a hash history on a page with a tag", () => { let history: HashHistory; let base: HTMLBaseElement; - let window: Window; - let document: Document; + let testWindow: Window; beforeEach(() => { // Need to use our own custom DOM in order to get a working history - let dom = new JSDOM(`

History Example

`, { - url: "https://example.org/", - }); - window = dom.window as unknown as Window; - document = window.document; - - window.history.replaceState(null, "", "#/"); - - base = document.createElement("base"); + testWindow = getWindow("/", true); + base = testWindow.document.createElement("base"); base.setAttribute("href", "/prefix"); - document.head.appendChild(base); + testWindow.document.head.appendChild(base); - history = createHashHistory({ window: dom.window as unknown as Window }); + history = createHashHistory({ window: testWindow }); }); afterEach(() => { - document.head.removeChild(base); + testWindow.document.head.removeChild(base); }); it("knows how to create hrefs", () => { - const hashIndex = window.location.href.indexOf("#"); + const hashIndex = testWindow.location.href.indexOf("#"); const upToHash = hashIndex === -1 - ? window.location.href - : window.location.href.slice(0, hashIndex); + ? testWindow.location.href + : testWindow.location.href.slice(0, hashIndex); const href = history.createHref({ pathname: "/the/path", diff --git a/packages/react-router/__tests__/router/hash-test.ts b/packages/react-router/__tests__/router/hash-test.ts index 38a5afd41c..c215014cd7 100644 --- a/packages/react-router/__tests__/router/hash-test.ts +++ b/packages/react-router/__tests__/router/hash-test.ts @@ -1,7 +1,5 @@ /* eslint-disable jest/expect-expect */ -import { JSDOM } from "jsdom"; - import type { HashHistory } from "../../lib/router/history"; import { createHashHistory } from "../../lib/router/history"; @@ -20,6 +18,7 @@ import EncodedReservedCharacters from "./TestSequences/EncodedReservedCharacters import GoBack from "./TestSequences/GoBack"; import GoForward from "./TestSequences/GoForward"; import ListenPopOnly from "./TestSequences/ListenPopOnly"; +import getWindow from "../utils/getWindow"; // TODO: Do we still need this? // const canGoWithoutReload = window.navigator.userAgent.indexOf('Firefox') === -1; @@ -27,15 +26,12 @@ import ListenPopOnly from "./TestSequences/ListenPopOnly"; describe("a hash history", () => { let history: HashHistory; - let dom: JSDOM; + let testWindow: Window; beforeEach(() => { // Need to use our own custom DOM in order to get a working history - dom = new JSDOM(`

History Example

`, { - url: "https://example.org/", - }); - dom.window.history.replaceState(null, "", "#/"); - history = createHashHistory({ window: dom.window as unknown as Window }); + testWindow = getWindow("/"); + history = createHashHistory({ window: testWindow }); }); it("knows how to create hrefs from location objects", () => { @@ -68,8 +64,8 @@ describe("a hash history", () => { it("prefixes raw hash values with /", () => { let spy = jest.spyOn(console, "warn").mockImplementation(() => {}); - dom.window.history.replaceState(null, "", "#hello"); - history = createHashHistory({ window: dom.window as unknown as Window }); + testWindow.history.replaceState(null, "", "#hello"); + history = createHashHistory({ window: testWindow }); expect(history.location.pathname).toBe("/hello"); history.push("world"); @@ -119,7 +115,7 @@ describe("a hash history", () => { }); it("re-throws when using non-serializable state", () => { - PushStateInvalid(history, dom.window); + PushStateInvalid(history, testWindow); }); }); diff --git a/packages/react-router/__tests__/router/navigation-test.ts b/packages/react-router/__tests__/router/navigation-test.ts index 74a41c76d2..8d36d1a249 100644 --- a/packages/react-router/__tests__/router/navigation-test.ts +++ b/packages/react-router/__tests__/router/navigation-test.ts @@ -1,6 +1,6 @@ -import { JSDOM } from "jsdom"; import { createBrowserRouter } from "../../lib/dom/lib"; import type { HydrationState } from "../../lib/router/router"; +import getWindow from "../utils/getWindow"; import { cleanup, setup } from "./utils/data-router-setup"; import { createFormData } from "./utils/utils"; @@ -62,13 +62,6 @@ function initializeTest(init?: { }); } -function getWindowImpl(initialUrl: string, isHash = false): Window { - // Need to use our own custom DOM in order to get a working history - const dom = new JSDOM(``, { url: "http://localhost/" }); - dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); - return dom.window as unknown as Window; -} - describe("navigations", () => { afterEach(() => cleanup()); @@ -453,7 +446,7 @@ describe("navigations", () => { }, ], { - window: getWindowImpl("/"), + window: getWindow("/"), // This is what enables the partialMatches logic patchRoutesOnNavigation: () => {}, }, diff --git a/packages/react-router/__tests__/utils/getWindow.ts b/packages/react-router/__tests__/utils/getWindow.ts new file mode 100644 index 0000000000..78a3af7dd3 --- /dev/null +++ b/packages/react-router/__tests__/utils/getWindow.ts @@ -0,0 +1,8 @@ +import { JSDOM } from "jsdom"; + +export default function getWindow(initialUrl: string, isHash = false): Window { + // Need to use our own custom DOM in order to get a working history + const dom = new JSDOM(``, { url: "http://localhost/" }); + dom.window.history.replaceState(null, "", (isHash ? "#" : "") + initialUrl); + return dom.window as unknown as Window; +}