T
TanStack4mo ago
sensitive-blue

testing broken after update to 1.120.3

updating 1.115.2 -> 1.120.3 breaks the setup for unit tests described here error stack trace:
Warning: An update to Transitioner inside a test was not wrapped in act(...).

│ When testing, code that causes React state updates should be wrapped into act(...):

│ act(() => {
│ /* fire events that update state */
│ });
│ /* assert on the output */

│ This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reac
│ tjs.org/link/wrap-tests-with-act
│ at Transitioner
│ at Suspense
│ at Matches
│ at RouterContextProvider
│ at RouterProvider
│ at TestRouterProvider
Warning: An update to Transitioner inside a test was not wrapped in act(...).

│ When testing, code that causes React state updates should be wrapped into act(...):

│ act(() => {
│ /* fire events that update state */
│ });
│ /* assert on the output */

│ This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reac
│ tjs.org/link/wrap-tests-with-act
│ at Transitioner
│ at Suspense
│ at Matches
│ at RouterContextProvider
│ at RouterProvider
│ at TestRouterProvider
(omitted some parts to fit into message)
GitHub
Testing Component and Hook with Tanstack Router · TanStack router ...
Hi Team, I am trying to test my component and a hook with a vitest and RTL. How to pass the Tanstack Router as a wrapper? Can you please help me on this. I have got on idea on testing a Component f...
16 Replies
metropolitan-bronze
metropolitan-bronze4mo ago
if you can share a complete minimal example , we can have a look you might just be missing an await router.load()
sensitive-blue
sensitive-blueOP4mo ago
I placed it inside the useEffect inside the provider, doesn't seem to change anything:
export const TestRouterProvider: FunctionComponent<TestRouterProviderProps> = ({
children,
initialEntries = [{ path: "/" }],
}) => {
const memoryHistory = createMemoryHistory({
initialEntries: getRouterEntries(initialEntries),
});

const router = createRouter({
routeTree: buildTestRouteTree(),
defaultComponent: () => children,
defaultPendingComponent: DefaultPendingComponent,
defaultErrorComponent: ({ error }: { error: Error }) => <DefaultErrorComponent error={error} />,
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
history: memoryHistory,
});

useEffect(() => {
async function loadRouter() {
await router.load();
}

void loadRouter();
}, [router]);

return <RouterProvider router={router} />;
};
export const TestRouterProvider: FunctionComponent<TestRouterProviderProps> = ({
children,
initialEntries = [{ path: "/" }],
}) => {
const memoryHistory = createMemoryHistory({
initialEntries: getRouterEntries(initialEntries),
});

const router = createRouter({
routeTree: buildTestRouteTree(),
defaultComponent: () => children,
defaultPendingComponent: DefaultPendingComponent,
defaultErrorComponent: ({ error }: { error: Error }) => <DefaultErrorComponent error={error} />,
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
history: memoryHistory,
});

useEffect(() => {
async function loadRouter() {
await router.load();
}

void loadRouter();
}, [router]);

return <RouterProvider router={router} />;
};
i'll try making a repro soon (unless you meant to use the router load somewhere else)
metropolitan-bronze
metropolitan-bronze4mo ago
after rendering the routerprovider
sensitive-blue
sensitive-blueOP4mo ago
like this:
export const TestRouterProvider: FunctionComponent<TestRouterProviderProps> = ({
children,
initialEntries = [{ path: "/" }],
}) => {
const memoryHistory = createMemoryHistory({
initialEntries: getRouterEntries(initialEntries),
});

const router = createRouter({
routeTree: buildTestRouteTree(),
defaultComponent: () => children,
defaultPendingComponent: DefaultPendingComponent,
defaultErrorComponent: ({ error }: { error: Error }) => <DefaultErrorComponent error={error} />,
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
history: memoryHistory,
});

return <RouterProvider router={router} />;
};

export const TestRouterProviderLoaded = ({ children }: { children: ReactNode }) => {
const router = useRouter();

useEffect(() => {
async function loadRouter() {
await router.load();
}

void loadRouter();
}, [router]);

return children;
};
//............
const CustomWrapper = ({ children, initialEntries }: CustomWrapperProps) => {
return (
//.........
<TestRouterProvider initialEntries={initialEntries}>
<TestRouterProviderLoaded>{children}</TestRouterProviderLoaded>
</TestRouterProvider>
//..........
);
};
export const TestRouterProvider: FunctionComponent<TestRouterProviderProps> = ({
children,
initialEntries = [{ path: "/" }],
}) => {
const memoryHistory = createMemoryHistory({
initialEntries: getRouterEntries(initialEntries),
});

const router = createRouter({
routeTree: buildTestRouteTree(),
defaultComponent: () => children,
defaultPendingComponent: DefaultPendingComponent,
defaultErrorComponent: ({ error }: { error: Error }) => <DefaultErrorComponent error={error} />,
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
history: memoryHistory,
});

return <RouterProvider router={router} />;
};

export const TestRouterProviderLoaded = ({ children }: { children: ReactNode }) => {
const router = useRouter();

useEffect(() => {
async function loadRouter() {
await router.load();
}

void loadRouter();
}, [router]);

return children;
};
//............
const CustomWrapper = ({ children, initialEntries }: CustomWrapperProps) => {
return (
//.........
<TestRouterProvider initialEntries={initialEntries}>
<TestRouterProviderLoaded>{children}</TestRouterProviderLoaded>
</TestRouterProvider>
//..........
);
};
not sure if this is the correct way to access the router in tests (via useRouter hook), since the router instance is closed inside provider or did you mean to do the router loading inside every test in beforeEach
metropolitan-bronze
metropolitan-bronze4mo ago
rather this
sensitive-blue
sensitive-blueOP4mo ago
how would i access the test route instance there
metropolitan-bronze
metropolitan-bronze4mo ago
you would need to create it outside of react and pass it into your testing component
sensitive-blue
sensitive-blueOP4mo ago
what would i pass for children and history? since that depends on the things inside react
metropolitan-bronze
metropolitan-bronze4mo ago
would need to experiment with an example if you can provide it to me
sensitive-blue
sensitive-blueOP4mo ago
i tried making one with same code i have for ts router, cant reproduce it 😦 i found the commit after which I get the errors: https://github.com/TanStack/router/pull/4044/files
metropolitan-bronze
metropolitan-bronze4mo ago
yeah and you likely need to wait for the router to load
sensitive-blue
sensitive-blueOP4mo ago
is there a way I can do it inside the setup, instead of having to export it outside react and then load and set it up in each test?
metropolitan-bronze
metropolitan-bronze4mo ago
sensitive-blue
sensitive-blueOP4mo ago
for eg. could I make it so TestRouterProvider doesnt pass basic children, but children with an argument that is the router, and then use that in the child component which will be a wrapper that has a useEffect to load the router? you manually create the router each time?
metropolitan-bronze
metropolitan-bronze4mo ago
yes you can also do this in a beforeEach just think about how router is used typically you create a router and then pass it into react react does not create the router
sensitive-blue
sensitive-blueOP4mo ago
yeah true, i was just using it this way since it was the first thing I saw about setting it up in 1 place and not having to think about it in any other place in tests

Did you find this page helpful?