How can I enable react-i18n translation file to be used in the unit tests done with react-testing-library and jest?

I am making unit tests with jest and react-testing-library for my frontend application which is done with React. My unit tests worked nicely before I added the internationalization with react-i18next -library. Now when I run the tests, it seems that it doesn’t find/use the translation files and all places where there should read something, are left empty. I’m using the newest react version with hooks and instead of React.Component I am using this kind of “const-components”:

    const ComponentName = ({t}) => {
        return(
          <p>{t('example')}</p>
        )}
      export default ComponentName;

The internationalization works perfectly in the actual page but just that the unit tests fail due to not using the translation-file so I think the problem is with correctly mocking the translation files. I am only finding some suggestion solutions for the older react using this.variableName -type of solutions, which however doesn’t help me much.

I have tried to mock it with jest.fn(), but I am not sure which function is the one, which I should mock and how to utilize the useTranslation() -function correctly from the tests.

    import React from 'react';
    import { useTranslation, Trans } from 'react-i18next';
    import { render } from '@testing-library/react';
    import ComponentName from './ComponentName';

    import '../locales/i18n';

    test('renders all documents in the list', () => {
      const mockUseTranslation = jest.fn();

      const { t, i18n } = mockUseTranslation();

      // const t = jest.fn();
      const c = render(<ComponentName t={t} />);
      expect(c.getByText('Translation File Title')).toBeDefined();
      expect(
        c.getAllByText(
          'Lorem ipsum'
        ).length
      ).toBe(3);
    });

Error message: Unable to find an element with the text: Translation File Title. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

So in short: the place, which should contain certain text is now totally empty.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You should not mock the translation, instead render the component with translation library as Higher Order Component, for example;

import React from 'react';
import i18n from '../../../i18n' // your i18n config file
import { render } from '@testing-library/react';
import ComponentName from './ComponentName';
import { I18nextProvider } from 'react-i18next'

test('renders all documents in the list', () => {
    const c = render(
      <I18nextProvider i18n={i18n}> // actually give translation to your component
         <ComponentName />
      </I18nextProvider>
    );
    // example if you have a key called example
    expect(c.getByText(i18n.getDataByLanguage('en').translation.example)).toBeDefined(); 
});

Instead of calling your translation texts with i18n.getDataByLanguage(‘en’) , you can give the default translation of your project, if it is French call it by i18n.getDataByLanguage(‘fr’).

Also change your component like this, instead of taking useTranslation hook from props, take it inside the component with hooks

ComponentName.jsx

import { useTranslation } from 'react-i18next'

const ComponentName = () => {
  const { t } = useTranslation()

  return(
    <p>{t('example')}</p>
  )}

export default ComponentName;

Method 2

Eventually I got the mock working like this (in App.js):

jest.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: key => key,
    i18n: { changeLanguage: jest.fn() }
  })
}));

In case somebody needs this.

Additionally inside components I was just using t={key=>key}, which enabled queries like this: expect(c.getByText('json.field.in.translation')).toBeDefined();

Method 3

I did that this way:

  1. create the configuration for i18n in a separate file:
     const DEFAULT_LANGUAGE = "en";
     const DEFAULT_NAMESPACE = "translations";
     const enTranslationJson={ //bring that json from your real translation file!
      "nav": {
         "home": "Home",
         "example": "Example"
       },
      "page-title": "{{pageName}} Page",
     };
    
     i18n.use(initReactI18next).init({
      lng: DEFAULT_LANGUAGE,
      fallbackLng: DEFAULT_LANGUAGE,
      ns: [DEFAULT_NAMESPACE],
      defaultNS: DEFAULT_NAMESPACE,
      debug: false,
      interpolation: {
       escapeValue: false,
      },
      resources: { [DEFAULT_LANGUAGE]: { [DEFAULT_NAMESPACE]: 
       enTranslationJson } },
      });
    
      export default i18n;
  2. override render method with the I18nextProvider:
    import { render as rtlRender } from "@testing-library/react";
    import { I18nextProvider } from "react-i18next";
    
    const render = (ui: React.ReactElement) => {
       return rtlRender(<I18nextProvider i18n={i18n}>{ui}</I18nextProvider>);
    }
  3. In the tests I am using the render func and looking for the real translation values:
    test("home component render correctly", async () => {
      render(<Home />);
      const item = screen.getByText("Template Page");
      expect(item).toBeInTheDocument();
    });


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x