Search code examples
reactjstestingcypress

cypress: how to mock useTranslation in test?


I want to ask that I have a component EmailSignUpView that use react-intl to translate text content.

import { useTranslation as t } from '@utils/hooks';
<div className={styles.notifyContainer}>
      <div className={styles.notifyTextContainer}>
        <h2
          className={styles.signUpheading}
          data-test-id="email-signup-heading"
        >
          {t('email_stay_in_touch')}
        </h2>
      </div>
</div>

and here's how we implement useTranslation

import { useIntl } from 'react-intl';

const useTranslation = (
  id: string,
  values?: Record<string, string | number>
): string => {
  const { formatMessage } = useIntl();

  return formatMessage({ id }, values);
};

export default useTranslation;

and here's my test

import { IntlProvider } from 'react-intl';
import EmailSignUpView from '@components/EmailSignUpView/EmailSignUpView';
import MockRouter from '../../cypress/support/utils';
import { enTransMessages } from '../../cypress/support/testData/testData';
import * as hooks from '@utils/hooks';

describe('Email Signup Section tests', () => {
  before(() => {
    // Stub the useTranslation hook
    cy.stub(hooks, 'useTranslation').callsFake(() => ({
      t: (key) => {
        const translations = {
          'email_stay_in_touch': 'The text that I want to test!', // Add other key-value pairs as needed
        };
        return translations[key] || key;
      },
    }));
  });

  it('validate default Heading', () => {
    cy.mount(
      <MockRouter>
        <IntlProvider locale="en" messages={enTransMessages}>
          <EmailSignUpView />
        </IntlProvider>
      </MockRouter>
    );
    cy.getByDataID(selectors.heading)
      .should('have.text', 'The text that I want to test!')
      .and('be.visible');
  });
});

but the result is that the text is not as I expected.

Does anyone know where I may be wrong? Thank you!

Update: Post the log here

Type    Function    Alias(es)   # Calls
stub-1  useTranslation      -
spy-1       push    -
spy-2       replace -
spy-3       reload  -
spy-4       back    -
spy-5       forward -
stub-2      prefetch    2
spy-6       beforePopState  -
spy-7       emit    -
spy-8       off -
spy-9       on  -


Solution

  • The stub().callFake() should just provide the same result type as the hook itself (type string according to the real hook).

    Instead you are returning an object, perhaps trying to implement an equivalent code to the hook?

    Try returning a string, the simplest is this:

    cy.stub(hooks, 'useTranslation').callsFake(() => {
      return 'The text that I want to test!';
    })
    
    ...
    
    cy.getByDataID(selectors.heading)
      .should('have.text', 'The text that I want to test!')
    

    If you want to use multiple translations, using the real hook's parameter, perhaps this - assuming id is the language key:

    cy.stub(hooks, 'useTranslation').callsFake((id, values) => {
      const translations = {
        'en': 'The text that I want to test!', 
        'zh': '我想要测试的文本', 
        'es': 'El texto que quiero probar.', 
        'ar': 'النص الذي أريد اختباره'
      };
      return translations[id] || 'not found'
    })