Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Next 13 'next/navigation' router #67

Open
vitharanagedonjani-i2e opened this issue Nov 9, 2022 · 19 comments
Open

Support Next 13 'next/navigation' router #67

vitharanagedonjani-i2e opened this issue Nov 9, 2022 · 19 comments
Labels
enhancement New feature or request

Comments

@vitharanagedonjani-i2e
Copy link

vitharanagedonjani-i2e commented Nov 9, 2022

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

@scottrippey
Copy link
Owner

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.

@vitharanagedonjani-i2e
Copy link
Author

@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. :/

@Adriangar99
Copy link

I have the same problem but without the new app infra

@Pikachews
Copy link

Pikachews commented Nov 12, 2022

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.

@e-roy
Copy link

e-roy commented Nov 15, 2022

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

@scottrippey
Copy link
Owner

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?)

@with-heart
Copy link

with-heart commented Dec 6, 2022

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.

@geryit
Copy link

geryit commented Dec 21, 2022

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

@vitharanagedonjani-i2e
Copy link
Author

vitharanagedonjani-i2e commented Dec 21, 2022

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.

@scottrippey scottrippey changed the title Next 13 support? Support Next 13 'next/navigation' router Feb 20, 2023
@scottrippey scottrippey added the enhancement New feature or request label Feb 20, 2023
@scottrippey scottrippey pinned this issue Feb 20, 2023
@scottrippey
Copy link
Owner

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.

@mxswat
Copy link

mxswat commented May 24, 2023

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);
  },
}));

@scottrippey
Copy link
Owner

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.

@mikekellyio
Copy link

mikekellyio commented May 26, 2023

@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!

@mxswat
Copy link

mxswat commented May 26, 2023

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('/');

@scottrippey
Copy link
Owner

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
Copy link
Owner

Reopened for tracking purposes.

@SalahAdDin
Copy link

SalahAdDin commented Sep 27, 2023

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
Copy link

@scottrippey any review here?

@philwolstenholme
Copy link

@scottrippey any review here?

#103 (comment)

PRs are welcome :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests