Open sshmaxime opened 1 year ago
It seems related to https://github.com/vercel/next.js/discussions/49304, can you try the examples?
I tried what's suggested but it doesn't work. And ideally, I don't wanna use canaries releases as we will use this in production.
Can you share a minimal reproduction? We could maybe do the same as suggested here, but it feels a bit hacky: https://github.com/vercel/next.js/discussions/49304#discussioncomment-6910332
Please note that the Next.js App Router uses a canary version of React too.
I've been trying to get a minimal reproduction on Codesandbox
since this morning but I'm getting additional weird errors, could you try on that repo and branch directly https://github.com/LedgerHQ/cex-deposit-live-app/tree/support/i18n ? You should only have to do yarn
and yarn dev
. And then run yarn test
.
Thanks for the reproduction repo. Using jsdom seems to be wrong for testing server components, but using the node environment triggers this error:
I've also tried to remove completely the usage of cache()
but Next.js breaks in useCurrentLocale()
when using useParams()
from next/navigation
- I have no idea why.
Installing react@canary
gives the same error, whereas react@rc
gives our first error with cache is not a function
. We'll probably have to ask the Next.js team at some point.
Hey @QuiiBz, thanks for taking the time to check out our code. It means a lot.
While this issue is getting fixed I changed our components as Clients but I'm now getting Error: Uncaught [TypeError: Cannot read properties of null (reading 'locale')]
. Everything is mocked properly as per the docs, did i miss anything ?
Is this what you meant when saying Next.js breaks in useCurrentLocale() when using useParams() from next/navigation
? Because it seems to be related to that after following the stack trace:
Line 214-215 in next-international/dist/app/client
const params = (0, import_navigation2.useParams)();
const segment = params[(_a = config.segmentName) != null ? _a : DEFAULT_SEGMENT_NAME];
Is this what you meant when saying
Next.js breaks in useCurrentLocale() when using useParams() from next/navigation
Yeah, exactly. I've been able to go further by adding the following mocks:
jest.mock('next/navigation', () => ({
...jest.requireActual('next/navigation'),
useParams: jest.fn().mockReturnValue({
locale: 'en',
}),
}))
jest.mock('next/headers', () => ({
headers: jest.fn().mockReturnValue(new Headers({
'x-next-locale': 'en'
})),
}))
jest.mock('react', () => ({
...jest.requireActual('react'),
cache: jest.fn().mockImplementation(fn => fn)
}))
...but I'm now getting the following render error:
...which makes me believe that testing Server Components isn't really ready (no documentation about this on the Next.js docs, even though there is one for the Pages Router): https://github.com/testing-library/react-testing-library/issues/1209
https://github.com/vercel/next.js/issues/53065 suggests using testEnvironment: "node"
, which only needs the React's cache()
mock, but we get again the same error as my first comment (https://github.com/QuiiBz/next-international/issues/178#issuecomment-1724216504)
There seems to be a fix on canary Next.js, but we might want to wait for an official release: https://github.com/vercel/next.js/issues/47448#issuecomment-1705573492
@QuiiBz The weird thing is that the issue comes from useCurrentLocale
which is actually executed in a Client component. I don't quite follow why it has anything to do with Server Components ? Is it because it's embedded in one ?
ps: I tried the Next.js canary version mentionned in the issue and it didn't work. Even the latest one v13.4.20-canary.40
.
Is it because it's embedded in one
That's my assumption. Either way, Next.js doesn't have any documentation yet for testing Server Components or Client Components, and next-international doesn't seem to be the issue here (you could use cache()
, useParams()
... in application code too, and you'll get the same errors even if you're not using next-international).
I guess we'll have to wait for examples and documentation from Next.js, there is sadly nothing much that I can do.
I am getting the same error while running Jest for Server Components:
I am using Next version 13.5.1
. Any idea how to solve this problem?
Please read the above discussion for an answer. We don't know yet how to properly test new Next.js features.
For anyone out there struggling with testing for this I was able to get this working with all the following mocks
import { screen } from '@testing-library/react';
import { renderWithStoreI18n } from '@/utils/testing/testingLibraryHelper';
import Home from './page';
const testCache = (func) => func;
jest.mock('react', () => {
const originalModule = jest.requireActual('react');
return {
...originalModule,
cache: testCache
};
});
jest.mock('next/router', () => ({
useRouter: jest.fn().mockImplementation(() => ({
locale: 'en',
defaultLocale: 'en',
locales: ['en', 'ca', 'gb']
}))
}));
jest.mock('next/navigation', () => ({
...jest.requireActual('next/navigation'),
useParams: jest.fn().mockReturnValue({
locale: 'en'
})
}));
jest.mock('next/headers', () => ({
headers: jest.fn().mockReturnValue(new Headers({
'x-next-locale': 'en'
}))
}));
jest.mock('../../i18n/server', () => ({
getI18n: jest.fn().mockImplementation(() => jest.fn())
}));
jest.mock('../../i18n/client', () => ({
useI18n: jest.fn().mockImplementation(() => jest.fn())
}));
describe('Page', () => {
test('Page renders', async () => {
const Result = await Home();
renderWithStoreI18n(Result);
const homeEl = screen.getByTestId('home-test');
expect(homeEl).toBeDefined();
});
});
wforte4 can I see your component, I can get my component but the translation doesn't work, the example is far too limited in the documentation.
@QuiiBz Still no way to test with app router?
Yes this works with app router and no the translations themselves don't work. But it's a way at least for now to be able to do testing and have translations in your app. At my company we also do testing to use the application which is where we would test translations so it didn't matter for us. I also moved these mocks to the setup file for jest so that you don't have to put that on each test. Hope that helps!
I am using this library for client components and I have customRender below
import { render } from '@testing-library/react'; import { I18nProviderClient } from '@/locales/client';
jest.mock('next/router', () => require('next-router-mock'));
jest.mock('next/router', () => ({ useRouter: jest.fn().mockImplementation(() => ({ locale: 'en', defaultLocale: 'en', locales: ['en', 'fr'], })), }));
const Providers = ({ children }: { children: React.ReactElement }) => {
return
const customRender = (ui: React.ReactElement, options = {}) => render(ui, { wrapper: Providers, ...options, });
export * from '@testing-library/react'; export { default as userEvent } from '@testing-library/user-event'; export { customRender as render };
But when i try to run the test I get these error
Error: Uncaught [TypeError: (0 , import_react.use) is not a function]
18 | const customRender = (ui: React.ReactElement, options = {}) =>
> 19 | render(ui, {
| ^
20 | wrapper: Providers,
21 | ...options,
22 | });
I have added all the mocks mentioned by [wforte4] but nothing is working. Would really appreciate some help.
I am with the same problem as @kpaccess. The solution of @wforte4 doesn't work for me.
customRender didn't work for me. I just started mocking for whatever files that I was using.
like jest.mock('./src/locales/client', () => ({ useI18n: jest.fn(), useScopedI18n: jest.fn(), useChangeLocale: jest.fn(), useCurrentLocale: jest.fn(() => 'en'), }));
Are you using next/jest.js
inside your Jest configuration?
yes const nextJest = require('next/jest');
customRender didn't work for me. I just started mocking for whatever files that I was using.
like jest.mock('./src/locales/client', () => ({ useI18n: jest.fn(), useScopedI18n: jest.fn(), useChangeLocale: jest.fn(), useCurrentLocale: jest.fn(() => 'en'), }));
Are you mocking the client I18NProvider for your tests? Could I see an example of this? I am running into the same issue
Hey, is there any news? I tried to set the same mocking
jest.mock('../../locales/client', () => ({
useI18n: jest.fn().mockImplementation(() => jest.fn())
}));
This mock causes more issues, namely
console.error
Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. Check the render method of 'wrapper'.
I found one way to test. Here is my test version:
Test.tsx.ts
import { cultures } from '@autoscout24/culture'
import { I18nProviderClient, useScopedI18n } from '../../locales/client'
import { renderWithI18N, screen } from '../../tests/utils/renderWithI18N'
import PriceEstimationStart from './PriceEstimationStart'
jest.mock('../../locales/client')
describe('Test translation in all countries', () => {
let mockedUseScopedI18n: jest.MockWithArgs<typeof useScopedI18n>
beforeEach(() => {
mockedUseScopedI18n = jest.mocked(useScopedI18n)
const mockedI18nProviderClient = jest.mocked(I18nProviderClient)
mockedI18nProviderClient.mockImplementation(({ children }) => (
<>{children}</>
))
})
test.each([
[cultures.de_DE.iso, 'Hallo Welt!'],
[cultures.de_AT.iso, 'Hallo Welt!'],
[cultures.en_GB.iso, 'Hello World!'],
[cultures.fr_BE.iso, 'Bonjour le monde!'],
[cultures.fr_FR.iso, 'Bonjour le monde!'],
[cultures.it_IT.iso, 'Ciao mondo!'],
[cultures.nl_BE.iso, 'Hallo wereld!'],
[cultures.nl_NL.iso, 'Hallo wereld!'],
])('should resolve %s locale with text: %s', async (locale, expectedText) => {
mockedUseScopedI18n.mockImplementation(() => () => {
const translations: Record<string, string> = {
[cultures.de_AT.iso]: 'Hallo Welt!',
[cultures.de_DE.iso]: 'Hallo Welt!',
[cultures.en_GB.iso]: 'Hello World!',
[cultures.fr_BE.iso]: 'Bonjour le monde!',
[cultures.fr_FR.iso]: 'Bonjour le monde!',
[cultures.it_IT.iso]: 'Ciao mondo!',
[cultures.nl_BE.iso]: 'Hallo wereld!',
[cultures.nl_NL.iso]: 'Hallo wereld!',
}
return translations[locale] ?? 'Unknown locale'
})
renderWithI18N(<PriceEstimationStart />, { locale })
expect(screen.queryByText(expectedText)).toBeInTheDocument()
})
})
Custom renderer
import React, { ReactElement } from 'react'
import { cleanup, render } from '@testing-library/react'
import { I18nProviderClient } from '../../locales/client'
afterEach(() => {
cleanup()
})
const renderWithI18N = (ui: ReactElement, options: { locale: string }) =>
render(ui, {
// wrap provider(s) here if needed
wrapper: ({ children }) => (
<I18nProviderClient locale={options.locale}>
{children}
</I18nProviderClient>
),
...options,
})
export * from '@testing-library/react'
export { default as userEvent } from '@testing-library/user-event'
export { renderWithI18N }
Hello,
I'm getting an
TypeError: (0 , import_react2.cache) is not a function
error while setting up the tests withnext-international
. Have you already see those types of error ?