FullOnFlatWhite
FullOnFlatWhite

Reputation: 3672

Stubbing I18next useTranslation hook in Jest doesn't trigger toHaveBeenCalled

I'm trying to stub/spy the translation, not just mock it, and I can't seem to get it to trigger even in this most base case.

/**
 * ComponentName.jsx
 */

import { useTranslation } from "react-i18next";

export default function ComponentName() {
  const { t } = useTranslation();

  return <div>t(`index:path`)</div>
}

/**
 * ComponentName.test.jsx
 */

import { shallow } from "enzyme";
import ComponentName from "./ComponentName";
import { useTranslation } from "react-i18next";
jest.mock("react-i18next", () => ({
  useTranslation: () => ({ t: jest.fn(key => key) })
}));

it("calls the translation function", () => {
  const wrapper = shallow(<ComponentName />);

  expect(useTranslation().t).toHaveBeenCalled();
});

When I drop a console.log(t) in the ComponentName.jsx file, it correctly displays that it's a mocked function. If I put t() in the ComponentName.test.jsx file, it passes.

Is there a way to stub this so that I can eventually tag it with toHaveBeenCalledWith? Or am I relegated to doing contains("index:path") on the component?


Edit: So, when I updated on @felixmosh's answer

/**
 * ComponentName.test.jsx
 */


import { mount } from 'enzyme';
import { I18nextProvider } from 'react-i18next';

describe('<SomeComponent />', () => {
  it('dispatches SORT_TABLE', () => {
    const i18nextMock = {
      t: jest.fn(key => key),
    };
    const enzymeWrapper = mount(
      <I18nextProvider i18n={i18nextMock}>
        <SomeComponent />
      </I18nextProvider>
    );

    expect(i18nextmock.t).toHaveBeenCalled()
  });
});

/**
 * ComponentName.jsx
 */

import { useTranslation } from "react-i18next";

export default function ComponentName() {
  const { t } = useTranslation();

  return <div>t(`index:path`)</div>
}

It's the same issue. If t was "a string" instead of jest.fn(), when i console.log t in ComponentName.jsx, I correctly get "a string", when I console.log t as jest.fn(key => key), I correctly get a function.

But when I call it, I don't get it.

Is it possible that it's not the same instance that's being sent to I18nextProvider?

Upvotes: 4

Views: 6616

Answers (1)

felixmosh
felixmosh

Reputation: 35553

2 Things that you should improve in your code:

  1. useTransaltion is a hook which requires context, make sure you wrap you component with i18nextProvider.
  2. As of the fact you will have nested components switch shallow with mount.
  3. There is no need for mocking anything, i18next has a built-it support of tests. In order to enable it use cimode as the lng when configuring your i18next for tests.
// i18nForTests.js

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
  lng: 'cimode',
  // ----- ^
  // have a common namespace used around the full app
  ns: ['translations'],
  defaultNS: 'translations',

  interpolation: {
    escapeValue: false, // not needed for react!!
  },

  resources: { en: { translations: {} } },
});

export default i18n;
// SomeComponent.test.js
import { mount } from 'enzyme';
import { I18nextProvider } from 'react-i18next';
import i18n from '../i18nForTests';

describe('<SomeComponent />', () => {
  it('dispatches SORT_TABLE', () => {
    const enzymeWrapper = mount(
        <I18nextProvider i18n={i18n}>
          <SomeComponent />
        </I18nextProvider>
    );
    enzymeWrapper.find('.sort').simulate('click');

    expect(enzymeWrapper.find('#some-text').text()).toEqual('MY_TRANS_KEY');
  });
});

Edit: Version with mocked I18next

// i18nextMock.js
export const i18nextMock = {
  t: jest.fn(),
  // Do the same for other i18next fields
};

// SomeComponent.test.js
import { mount } from 'enzyme';
import { I18nextProvider } from 'react-i18next';
import i18nextMock from '../i18nextMock';

describe('<SomeComponent />', () => {
  it('dispatches SORT_TABLE', () => {
    const enzymeWrapper = mount(
        <I18nextProvider i18n={i18nextMock}>
          <SomeComponent />
        </I18nextProvider>
    );
    enzymeWrapper.find('.sort').simulate('click');

    expect(enzymeWrapper.find('#some-text').text()).toEqual('MY_TRANS_KEY');
  });
});

Upvotes: 4

Related Questions