T
TanStack2y ago
exotic-emerald

Outlet does not render when using @testing-library/react.

Hey there, I'm moving to using @tanstack/react-location with an existing project that was previously using react-router-dom. Version in package.json is ^3.4.4 - this is because I am implementing a Module Federation solution that already exists in another project, and wanted to keep the versions in sync for the time being. All is working well, and I hasn't had any issues with dev. However, my integration tests appear to be failing due to the <Outlet /> component not rendering the element associated with the route I'm feeding it. For some context, I have a reusable function that accepts some UI as an argument. This function then creates history with createMemoryHistory, allowing me to set initial entries to render the view I want. It then wraps the UI in the router, a mock GraphQL provider and a context provider, akin to the below (pared down to remove error handling and whatnot):
const renderTestUI = ({ ui, ctx, initialPath = '/test' }) => {
RTLCleanup();
const history = createMemoryHistory({
initialEntries: [initialPath]
});
const location = new ReactLocation();
return RTLRender(
<Router
location={location}
routes={[
{
path: '/test',
element: <p>This does not render</p>
}
]}
>
<MockGraphQLProvider>
<AppContext.Provider value={ctx}>
{ui}
</AppContext.Provider>
</MockGraphQLProvider>
</Router>
);
};
const renderTestUI = ({ ui, ctx, initialPath = '/test' }) => {
RTLCleanup();
const history = createMemoryHistory({
initialEntries: [initialPath]
});
const location = new ReactLocation();
return RTLRender(
<Router
location={location}
routes={[
{
path: '/test',
element: <p>This does not render</p>
}
]}
>
<MockGraphQLProvider>
<AppContext.Provider value={ctx}>
{ui}
</AppContext.Provider>
</MockGraphQLProvider>
</Router>
);
};
What I'm seeing is that even when I can see that router.state.location.pathname matches the route in question, the <Outlet /> that is rendered as part of the component passed as ui simply does not render. Is there some consideration when testing that I should know about? Anything more I can do to debug this thoroughly? If it would help I can do my best to throw together an actual working example rather than this more meager one. Thank you so much for your time, and I hope you have a great rest of your week.
2 Replies
exotic-emerald
exotic-emeraldOP2y ago
I did go ahead and throw together an example - I hope this clarifies what boneheaded mistake I'm making: https://codesandbox.io/s/priceless-mendeleev-6qkyj4 I do see an error about updates to Router not being wrapped in act(). I went ahead and tried wrapping the return in render in act, but that didn't seem to do anything other than remove the error.
edreeseg
CodeSandbox
priceless-mendeleev-6qkyj4 - CodeSandbox
priceless-mendeleev-6qkyj4 by edreeseg using @tanstack/react-location, @testing-library/react, loader-utils, react, react-dom, react-scripts, regenerator-runtime
exotic-emerald
exotic-emeraldOP2y ago
For anyone that runs into this in the future, it appears the reason this wasn't working is routing through react-location is async, so for accurate results you must use something like waitFor for accurate results. The fixed version of the tests would look something like:
describe("Attempting Outlet render", () => {
it("Displays text above outlet", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/Attempting Outlet render/));
});
it("Should display element associated with path with Outlet.", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/This should render in Outlet/));
});
it("Displays text below outlet", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/Below Outlet/));
});
});
describe("Attempting Outlet render", () => {
it("Displays text above outlet", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/Attempting Outlet render/));
});
it("Should display element associated with path with Outlet.", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/This should render in Outlet/));
});
it("Displays text below outlet", async () => {
await act(() => render(<OutletContainer />));
waitFor(() => screen.getByText(/Below Outlet/));
});
});

Did you find this page helpful?