diff --git a/packages/react-router/__tests__/gh-issue-11300-test.tsx b/packages/react-router/__tests__/gh-issue-11300-test.tsx new file mode 100644 index 0000000000..d5fc25150b --- /dev/null +++ b/packages/react-router/__tests__/gh-issue-11300-test.tsx @@ -0,0 +1,137 @@ +import * as React from "react"; +import type { + Dispatch, + ReactNode, + SetStateAction} from "react"; +import { + createContext, + useContext, + useState, +} from "react"; +import { + createMemoryRouter, + createRoutesFromElements, + Route, + RouterProvider, + useLoaderData, +} from "react-router"; +import { + act, + fireEvent, + getByRole, + getByText, + render, + waitFor, +} from "@testing-library/react"; +import { createDeferred } from "../../router/__tests__/utils/utils"; + +type Environment = { + id: number; +}; + +type Query = { + environment: Environment; +}; + +type State = [A, Dispatch>]; + +describe("GH Issue #11300", () => { + it("works", async () => { + const EnvironmentContext = createContext | undefined>( + undefined + ); + + function useEnvironment(): Environment { + const environmentState = useContext(EnvironmentContext); + if (environmentState === undefined) + throw new Error("EnvironmentContext.Provider is missing"); + return environmentState[0]; + } + + let environmentId = 0; + + function createEnvironment(): Environment { + return { + id: ++environmentId, + }; + } + + function useCreateNewEnvironment(): () => void { + const environmentState = useContext(EnvironmentContext); + if (environmentState === undefined) + throw new Error("EnvironmentContext.Provider is missing"); + return () => environmentState[1](createEnvironment()); + } + + function MutableEnvironment({ children }: { children: ReactNode }) { + const environmentState = useState(createEnvironment); + return ( + + {children} + + ); + } + + function loadQuery(environment: Environment): Query { + return { environment }; + } + + function useQuery(query: Query): string { + const environment = useEnvironment(); + if (query.environment.id !== environment.id) + throw new Error( + "Query environment does not match environment from the context" + ); + return `Data... (environment.id = ${environment.id})`; + } + + function MyPage() { + const query = useLoaderData() as Query; + const data = useQuery(query); + const createNewEnvironment = useCreateNewEnvironment(); + return ( + <> +

{data}

+ + + ); + } + + function MyRouter() { + const environment = useEnvironment(); + let router = createMemoryRouter( + createRoutesFromElements( + loadQuery(environment)} + element={} + /> + ) + ); + return ; + } + + let { container } = render( + + + + ); + + // Somehow this avoids: + // Warning: An update to RouterProvider inside a test was not wrapped in act(...). + // I suspect that it has something to do with the way completeNavigation runs + // asynchronously and will yield to render. + let fooDefer = createDeferred(); + await act(() => fooDefer.resolve(123)); + + expect(getByText(container, "Data... (environment.id = 1)")).toBeDefined(); + + fireEvent.click(getByRole(container, "button")); + + await waitFor(() => { + expect( + getByText(container, "Data... (environment.id = 2)") + ).toBeDefined(); + }); + }); +}); diff --git a/packages/react-router/__tests__/tsconfig.json b/packages/react-router/__tests__/tsconfig.json new file mode 100644 index 0000000000..f0c7f5bd7f --- /dev/null +++ b/packages/react-router/__tests__/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["*"], +}