next-router-mock icon indicating copy to clipboard operation
next-router-mock copied to clipboard

Support Next 13 'next/navigation' router

Open vitharanagedonjani-i2e opened this issue 2 years ago • 19 comments

Module not found: Error: Can't resolve 'next/dist/next-server/lib/router-context'

Must be nice to have an implementation to mock useRouter of next/navigation

vitharanagedonjani-i2e avatar Nov 09 '22 22:11 vitharanagedonjani-i2e

Where do you get this error? Storybook? Jest? I've done some tests with next 13, and so far things looked good. But I haven't looked at the new app infrastructure and whether next-router-mock will work with it.

scottrippey avatar Nov 10 '22 01:11 scottrippey

@scottrippey I got the error in storybook and jest both once I moved to new app infrastructure. useRouter which was previously imported from next/router doesn't work inside app. They have introduced a useRouter which can be imported from next/navigation and it works on client side pages. Check this url

Although I managed to fix it in jest by mocking the useRouter (from navigation). I'm still trying to find a way to mock it in storybook. :/

vitharanagedonjani-i2e avatar Nov 10 '22 08:11 vitharanagedonjani-i2e

I have the same problem but without the new app infra

Adriangar99 avatar Nov 11 '22 13:11 Adriangar99

I get the same thing when trying to add MemoryRouterProvider as a storybook decorator, using nextjs 13.0.2, not using the new app infrastructure.

Pikachews avatar Nov 12 '22 20:11 Pikachews

receiving an error in storybook when using the MemoryRouterProvider ERROR in ../../node_modules/next-router-mock/dist/MemoryRouterProvider/next-10.js 6:25-76 Module not found: Error: Can't resolve 'next/dist/next-server/lib/router-context' in ...

using nextjs 13.0.3, and NOT using the new app infrastructure. my build is also using turborepo, so I'm not sure if this adds to issue

*** update *** it does seem to work if I continue using import { useRouter } from "next/router"; instead of import { useRouter } from "next/navigation";

and I also hard code the next/dist/next-server/lib/router-context location into the next-10.js file this might be a turborepo thing, not really sure, but hope it helps someone

e-roy avatar Nov 15 '22 13:11 e-roy

Regarding MemoryRouterProvider issues: In Next 13, they did not change any of the internal paths. So, I'm having trouble reproducing the issue. I created #66 to test the Next 13 integration, and tests are passing so far.

Can anyone create a CodeSandbox to demonstrate this issue? Did anyone upgrade from Next 12, where it was working, and then Next 13 broke? (just to verify that it's a Next 13 issue?)

scottrippey avatar Nov 15 '22 17:11 scottrippey

I was able to resolve the issue @e-roy reported above in https://github.com/Soil-labs/eden-app-2-FrontEnd/pull/688#pullrequestreview-1207242476.

We had to move our decorators from decorator.tsx to the decorators export of preview.tsx.

We also needed to change the import to next-router-mock/MemoryRouterProvider/next-12 because it defaults to trying to load next-10. No idea why that is.

with-heart avatar Dec 06 '22 20:12 with-heart

Using next-router-mock/MemoryRouterProvider/next-12 for now but new <Link/> component isnt being rendered

geryit avatar Dec 21 '22 09:12 geryit

import { addDecorator } from '@storybook/react'; import { AppRouterContext } from 'next/dist/shared/lib/app-router-context';

addDecorator((Story) => (
  <AppRouterContext.Provider>
    <RootLayoutRegistry>
      <Story />
    </RootLayoutRegistry>
  </AppRouterContext.Provider>
));

This will fix it.

vitharanagedonjani-i2e avatar Dec 21 '22 15:12 vitharanagedonjani-i2e

So there are 2 separate issues here, hopefully this message helps people

Module not found: Error: Can't resolve 'next/dist/next-server/lib/router-context'

is solved by importing from this path instead:

import { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider/next-13';

I will update the documentation soon!

The problem with the next-router-mock/MemoryRouterProvider path is that it tries to load from ./next-13 and also does ./next-10 as a backup. This approach works fine in Jest, but Storybook, using Webpack, tries to compile both.

Supporting next/navigation

I will add support for this very soon too. I believe the API is very similar, but the Context Provider is different, so I will have to do some work to test this out. I'm leaving this issue open to track this feature.

scottrippey avatar Mar 20 '23 14:03 scottrippey

I found a way to make next-router-mock work with next/navigation, including useSearchParams

Maybe this can help other people.

vi.mock('next/router', () => require('next-router-mock'));
vi.mock('next/navigation', () => ({
  ...require('next-router-mock'),
  useSearchParams: () => {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const router = require('next-router-mock').useRouter();
    const path = router.asPath.split('?')?.[1] ?? '';
    return new URLSearchParams(path);
  },
}));

mxswat avatar May 24 '23 13:05 mxswat

This is a great start, thank you!
I've been playing with Next 13 lately, and it seems like the next/navigation implementation will be mostly easy, since the underlying URL logic is pretty similar.   But as I've been strapped for time, I haven't been able to implement the extra API hooks, and it might be a while before I can support the full suite of next/navigation features.
I'd be happy to accept PRs, even if they only offer "partial" navigation support for now.

scottrippey avatar May 26 '23 16:05 scottrippey

@mxswat your solution is super helpful! I'm using usePathname so using your strategy I was at least able to get my tests running again with a hardcoded path.

vi.mock('next/navigation', () => ({
  ...require('next-router-mock'),
  useSearchParams: () => {
   ...
  },
  usePathname: () => '/'
}));

I'm still looking for a solution so that I can define the path the test should see, but very happy to be moving again!

mikekellyio avatar May 26 '23 17:05 mikekellyio

I also made a new version that supports usePathname and other


import * as mockRouter from 'next-router-mock';

const useRouter = mockRouter.useRouter;

export const MockNextNavigation = {
  ...mockRouter,
  notFound: vi.fn(),
  redirect: vi.fn().mockImplementation((url: string) => {
    mockRouter.memoryRouter.setCurrentUrl(url);
  }),
  usePathname: () => {
    const router = useRouter();
    return router.asPath;
  },
  useSearchParams: () => {
    const router = useRouter();
    const path = router.query;
    return new URLSearchParams(path as any);
  },
};

And I use it like this,

import mockRouter from 'next-router-mock';
import { MockNextNavigation } from '@/test-mocks/next-navigation';

vi.mock('next/navigation', () => MockNextNavigation);

And very important, reset the router URL in the beforeEach

beforeEach(() => {
  mockRouter.setCurrentUrl('/');

mxswat avatar May 26 '23 20:05 mxswat

I've started a branch to implement these next/navigation hooks! I'm going to move this discussion to #103, since this thread has too many side-tracks.

scottrippey avatar Aug 15 '23 22:08 scottrippey

Reopened for tracking purposes.

scottrippey avatar Sep 20 '23 16:09 scottrippey

I also made a new version that supports usePathname and other

import * as mockRouter from 'next-router-mock';

const useRouter = mockRouter.useRouter;

export const MockNextNavigation = {
  ...mockRouter,
  notFound: vi.fn(),
  redirect: vi.fn().mockImplementation((url: string) => {
    mockRouter.memoryRouter.setCurrentUrl(url);
  }),
  usePathname: () => {
    const router = useRouter();
    return router.asPath;
  },
  useSearchParams: () => {
    const router = useRouter();
    const path = router.query;
    return new URLSearchParams(path as any);
  },
};

And I use it like this,

import mockRouter from 'next-router-mock';
import { MockNextNavigation } from '@/test-mocks/next-navigation';

vi.mock('next/navigation', () => MockNextNavigation);

And very important, reset the router URL in the beforeEach

beforeEach(() => {
  mockRouter.setCurrentUrl('/');

How can we extend the MemoryRouterProvider to include usePathName? I'm using the path on my Navbar and the value always comes null.

SalahAdDin avatar Sep 27 '23 15:09 SalahAdDin

@scottrippey any review here?

SalahAdDin avatar Jun 28 '24 03:06 SalahAdDin

@scottrippey any review here?

https://github.com/scottrippey/next-router-mock/pull/103#issuecomment-2192407604

PRs are welcome :)

philwolstenholme avatar Jun 28 '24 07:06 philwolstenholme