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

Link component used in Jest not working properly #43769

Open
1 task done
viktorrenkema opened this issue Dec 6, 2022 · 13 comments
Open
1 task done

Link component used in Jest not working properly #43769

viktorrenkema opened this issue Dec 6, 2022 · 13 comments
Labels
bug Issue was opened via the bug report template.

Comments

@viktorrenkema
Copy link

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 21.6.0: Thu Sep 29 20:13:56 PDT 2022; root:xnu-8020.240.7~1/RELEASE_ARM64_T6000
Binaries:
Node: 16.15.1
npm: 8.11.0
Yarn: 1.22.19
pnpm: N/A
Relevant packages:
next: 13.0.7-canary.1
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

https://github.com/viktorrenkema/testing-link

To Reproduce

  1. Run yarn test
  2. Notice Next informs us of below error. Next thinks we're using the /app directory, but we are not:
Dynamic href `/[foo]` found in <Link> while using the `/app` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href

Describe the Bug

  1. We found that this was introduced recently by App directory next/link dynamic href dev error #43074.
  2. That PR is supposed to fix another issue (next/link does not format the URL string properly with URL object for a dynamic route. #42715) that we also experienced ourselves.

We also went through all prior versions of Next 13 with the following results:
13.0.1 works
13.0.2-5 breaks, known issue #42715 looking something like:

Expected the element to have attribute: href=“/bar”
Received: href=“/[foo]?foo=bar”

13.0.6 breaks, with the error described in this issue. We get the error as introduced in the PR named in step 2.

Dynamic href `/[foo]` found in <Link> while using the `/app` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href

Expected Behavior

Jest test is supposed to know we are:

  1. Link component should return correct URL in Jest, not appending the link parameters to it
  2. not using the /app directory, when we are not

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@viktorrenkema viktorrenkema added the bug Issue was opened via the bug report template. label Dec 6, 2022
@ianizaguirre
Copy link

I confirm I am getting the same error even though I am not using the app directory.

@j-borg
Copy link

j-borg commented Dec 9, 2022

I get the same error for dynamic routes with version 13.0.6. But when I add the as prop, the error is fixed.

For compatibility with old dynamic routes in the pages directory, you can pass as prop to Link.

Legacy Props

@ianizaguirre
Copy link

I get the same error for dynamic routes with version 13.0.6. But when I add the as prop, the error is fixed.

For compatibility with old dynamic routes in the pages directory, you can pass as prop to Link.

Legacy Props

Right, but this is still a bug because we should not be needing to update our current code to pass an as prop since we are not using the new /app directory at all.

@m-rutter
Copy link

m-rutter commented Dec 12, 2022

It throws when this is evaluated to true:

https://github.com/hanneslund/next.js/blob/8ecbda4af7a72b24b375386b0e413ea1299fdcb0/packages/next/client/link.tsx#L398

This check relies on being able to get hold of the router context, however I don't see this router context being exported anywhere for public consumption, so its not easy to even mock or wrap tests in this router context.

@ianizaguirre
Copy link

any updates on this ?

@tatethurston
Copy link

tatethurston commented Jan 5, 2023

This is a pretty rough bug when adopting Next 13. In the meantime, here's a workaround for jest:

// setupTests.js

import { createContext } from 'react';

jest.mock('next/dist/shared/lib/router-context.js', () => ({
  RouterContext: createContext(true),
}));

I'm using this in a setupFile to only define it once (and hopefully remove it soon 🤞):

// jest.config.js

/** @type {import('jest').Config} */
const config = {
  setupFiles: ['./setupTests.js']
};

module.exports = config;

See @m-rutter's comment for more context.

@carlosrsabreu
Copy link

carlosrsabreu commented Jun 1, 2023

Any updates on this?

@ianizaguirre
Copy link

Any updates on this?

Hello, the comment above fixed it for me (#43769 (comment)), did it not resolve it for you?

@carlosrsabreu
Copy link

Any updates on this?

Hello, the comment above fixed it for me (#43769 (comment)), did it not resolve it for you?

Hello, yes. I was looking for a fix instead of a workaround.

@bazanowsky
Copy link

bazanowsky commented Sep 26, 2023

I don't know if this issue has already some better fix than the workaround.

But in my case i had error:

    The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: createContext

Which as i found in other issue - the solution is to replace jest.mock with jest.doMock 🤷

After this change i had another error:

Cannot find module 'next/dist/shared/lib/router-context.js' from 'jest.setup.js'

In this case - as for Next 13.5.1 (maybe earlier also) the filename is changed, and finally my working code was:

import { createContext } from 'react';

jest.doMock('next/dist/shared/lib/router-context.shared-runtime.js', () => ({
  RouterContext: createContext(true),
}));

👍 ✌️

@ianizaguirre
Copy link

ianizaguirre commented Sep 26, 2023

jest.doMock('next/dist/shared/lib/router-context.shared-runtime.js', () => ({

Thank you for the updated path, it works!

To help anyone who gets an error of:

"jest.mock() is not allowed to reference any out-of-scope variables."

The solution I found was to use a variable prefixed with the word "mock", so mockContext is what I named mine.

My working jest.setup.ts file looks like this

import 'whatwg-fetch';
import { createContext } from 'react';


const mockContext = createContext(true);

jest.mock('next/dist/shared/lib/router-context.shared-runtime.js', () => ({
  RouterContext: mockContext,
}));


@mateuszpigula
Copy link

mateuszpigula commented Oct 2, 2023

Because

vi.mock works only for modules that were imported with the import keyword. It doesn't work with require.

it does not work with vitest and next: 13.5.3. Neither vi.mock nor vi.doMock:

const mockContext = createContext(true);
vi.mock('next/dist/shared/lib/router-context.shared-runtime.js', () => ({
  RouterContext: mockContext,
}));

edit:
I ended up installing next-router-mock and wrapping the rendered component in MemoryRouterProvider:

const Wrapper = (props: ComponentProps<typeof MyComponent>) => {
  return (
    <MemoryRouterProvider>
      <MyComponent {...props} />
    </MemoryRouterProvider>
  );
};

Not really satisfied with this solution but the cleanest without any weird workarounds.

@mieradi
Copy link

mieradi commented Mar 24, 2024

Because

vi.mock works only for modules that were imported with the import keyword. It doesn't work with require.

it does not work with vitest and next: 13.5.3. Neither vi.mock nor vi.doMock:

const mockContext = createContext(true);
vi.mock('next/dist/shared/lib/router-context.shared-runtime.js', () => ({
  RouterContext: mockContext,
}));

edit: I ended up installing next-router-mock and wrapping the rendered component in MemoryRouterProvider:

const Wrapper = (props: ComponentProps<typeof MyComponent>) => {
  return (
    <MemoryRouterProvider>
      <MyComponent {...props} />
    </MemoryRouterProvider>
  );
};

Not really satisfied with this solution but the cleanest without any weird workarounds.

Using Vitest, and this also worked for me. At the time of writing, the import is

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests

9 participants