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

language is not set during tests with vitest #1724

Open
phun-ky opened this issue Feb 19, 2024 · 6 comments
Open

language is not set during tests with vitest #1724

phun-ky opened this issue Feb 19, 2024 · 6 comments

Comments

@phun-ky
Copy link

phun-ky commented Feb 19, 2024

🐛 Bug Report

Note

I do not think this is a bug, more of a configuration issue/documentation issue, because clearly, I am doing something wrong

Language is not set during tests with vitest. In a dependency I import, this code exists:

function useI18N() {
  var _useTranslation = reactI18next.useTranslation(),
      t = _useTranslation.t,
      language = _useTranslation.i18n.language;

  if (language !== 'nb' && language !== 'en') {
    throw Error('Language must be either nb or en.');
  }

  return t;
}

function withI18n(Component) {
  return function WrappedComponent(props) {
    var t = useI18N();
    return /*#__PURE__*/React__default["default"].createElement(Component, _extends({}, props, {
      i18n: t
    }));
  };
}

This code throws throw Error('Language must be either nb or en.'); when testing using vitest. It does not throw for building with vite nor with the previous test runner jest (with practically same config).

That code is wrapping a component that is consumed by several components up until the file I am testing, which is using this:

export const renderWithI18NContext = (
  component: ReactNode,
  organisation?: Organisation,
  locale = 'nb'
) => {
  return render(componentWithI18NContext(component, organisation, locale));
};

const componentWithI18NContext = (
  
) => {
  store.dispatch();

  i18n.changeLanguage(locale);

  return (
    <Provider store={store}>
      {/* @ts-ignore */}
      <MemoryRouter>
        <SWRConfig value={{ dedupingInterval: 0 }}>
          <Some.Provider value={something}>
            {component}
          </Some.Provider>
        </SWRConfig>
      </MemoryRouter>
    </Provider>
  );
};

With an import i18n that looks like this:

use(initReactI18next).init({
  lng: 'nb',
  fallbackLng: 'nb',
  resources: {
    nb: {
      translation: {}
    },
    en: {
      translation: {}
    }
  },
  interpolation: {
    escapeValue: false // not needed for react as it escapes by default
  }
});

export default i18n;

Which is consumed like this:

it('should pass', async () => {
  renderWithI18NContext(
    <ComponentTestContainer>
      …
      component={<ComponentToTest {...props} />}
    />
  );

  expect();
});

I've narrowed it down to react-i18next is not picking up language, i.e., that use(initReactI18next).init({…}) is not called, or something..

To Reproduce

I cannot produce a reproduction case due to the complexity of the internal (non public) dependencies, which I also think has something to do with this. As stated, I think this is a misconfiguration on my part, not a bug itself.

Expected behavior

That language is set.

Your Environment

  • runtime version: node 20
  • i18next version: "react-i18next": "14.0.5", "i18next": "23.7.16"
  • os: Linux
  • any other relevant information
@adrai
Copy link
Member

adrai commented Feb 19, 2024

make sure the file that contains the i18next.init call is included/required during your tests

@phun-ky
Copy link
Author

phun-ky commented Feb 20, 2024

@adrai thanks for the suggestion. I've tried that with no avail sadly :/ Tried to include it in the test, in the test setup file, the renderWithI18NContext wrapper and in the component I am testing.

@adrai
Copy link
Member

adrai commented Feb 20, 2024

hard to help, sorry

@geritol
Copy link

geritol commented May 21, 2024

@phun-ky this might be due to something called "dual package hazard"
vitest-dev/vitest#3287 (comment)

Had a similar issue (main code was using different build of the package than the library
adding the following to vite.config.js solved the issue for me:

resolve: {
  alias: {
     'react-i18next': path.resolve(__dirname, './node_modules/react-i18next/dist/commonjs/index.js'), // https://github.com/vitest-dev/vitest/issues/3287#issuecomment-1534159966
  },
},

@RomRom1
Copy link

RomRom1 commented Jun 24, 2024

Hi @phun-ky ,

I had the same problem and I solved it by using i18next-fs-backend in my component wrapper.
Also, to make it works, i18n instance needs to be created and initialized synchronously.
Please note the await on init call and the option initImmediate: false.

import {render as rtlRender} from '@testing-library/react'
import {I18nextProvider, initReactI18next} from "react-i18next";
import {createInstance} from "i18next";
import Backend from "i18next-fs-backend";
import translation from "../public/locales/en/translation.json"

const instance = createInstance()
await instance
    .use(Backend)
    .use(initReactI18next)
    .init({
        fallbackLng: "en",
        debug: false,
        interpolation: {
            escapeValue: false
        },
        defaultNS: "translation",
        initImmediate: false,
        resources: {
            en: {
                translation
            }
        }
    });

export const render = (ui: JSX.Element) => {

    function Wrapper({children}: any): any {

        return (
            <I18nextProvider i18n={instance}>
                {children}
            </I18nextProvider>
        )
    }

    rtlRender(ui, {wrapper: Wrapper})
}

Hope that can help

@tomtom94
Copy link

tomtom94 commented Sep 9, 2024

Hi there,

I just found an alternative for me, which is pretty well working.

In my case I need to use the var env import.meta.env.MODE === 'test'

import frAdmin from '../public/locales/fr/admin.json'
import frErrorBoundary from '../public/locales/fr/errorBoundary.json'
import frMenu from '../public/locales/fr/menu.json'
import frNewStudy from '../public/locales/fr/studyResults.json'
import frStudyResults from '../public/locales/fr/studyResults.json'

import enAdmin from '../public/locales/en/admin.json'
import enErrorBoundary from '../public/locales/en/errorBoundary.json'
import enMenu from '../public/locales/en/menu.json'
import enNewStudy from '../public/locales/en/studyResults.json'
import enStudyResults from '../public/locales/en/studyResults.json'

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: ['fr'],
    ...(import.meta.env.MODE === 'test' && {
      resources: {
        fr: { admin: frAdmin, errorBoundary: frErrorBoundary, menu: frMenu, newStudy: frNewStudy, studyResults: frStudyResults },
        en: { admin: enAdmin, errorBoundary: enErrorBoundary, menu: enMenu, newStudy: enNewStudy, studyResults: enStudyResults }
      }
    }),
    interpolation: {
      escapeValue: false // not needed for react as it escapes by default
    },
    ...(import.meta.env.MODE !== 'test' && { ns: ['menu', 'studyResults', 'newStudy', 'admin'] })
  })
import { render, screen } from '@testing-library/react'
import { MyComponent } from './MyComponent'
import { I18nextProvider } from 'react-i18next'
import { setupStore } from '../../modules/store'
import { Provider } from 'react-redux'
import i18n from '../../i18n'

const store = setupStore()

test("renders all the stuff you want", () => {
  render(
    <Provider store={store}>
      <I18nextProvider i18n={i18n}>
        <MyComponent />
      </I18nextProvider>
    </Provider>
  )

  expect(screen.getByText('I am looking for my sentence here')).toBeDefined()
})

Actually by just defining the namespaces, with this line ns: ['menu', 'studyResults', 'newStudy', 'admin'] the testing library didn't open the json files, so I had to import them one by one and put them in the classical resources: { fr:{}, en:{} } style attributes.

I have been trying your asynchronous stuff @RomRom1 but not working.

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

No branches or pull requests

5 participants