Skip to content

Instantly share code, notes, and snippets.

@mirague
Last active November 9, 2021 09:32
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mirague/c05f4da0d781a9b339b501f1d5d33c37 to your computer and use it in GitHub Desktop.
Save mirague/c05f4da0d781a9b339b501f1d5d33c37 to your computer and use it in GitHub Desktop.
Testing React-Intl components with Enzyme's mount() and shallow() methods. This is a helper function which wraps the `intl` context around your component tests in an easy and efficient way.

When using Enzyme and React-Intl you are likely run into the issues when testing components that use some of React-Intl's components or injected formatting functions. These helper functions (mountWithIntl and shallowWithIntl) aim to address some of the below errors:

Uncaught Invariant Violation: [React Intl] Could not find required ``intl`` object. <IntlProvider> needs to exist in the component ancestry.

TypeError: Cannot read property 'getInstance' of null

Error: ReactWrapper::state() can only be called on the root

Checkout this question at StackOverflow for a complete overview of the issues I ran into:

Injecting react-intl object into mounted Enzyme components for testing

import { mountWithIntl } from 'helpers/intl-enzyme-test-helper.js';
const wrapper = mountWithIntl(
<CustomComponent />
);
expect(wrapper.state('foo')).to.equal('bar'); // OK
expect(wrapper.text()).to.equal('Hello World!'); // OK
import React from 'react';
import { FormattedMessage } from 'react-intl';
class CustomComponent extends React.Component {
state = {
foo: 'bar'
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
/**
* Components using the react-intl module require access to the intl context.
* This is not available when mounting single components in Enzyme.
* These helper functions aim to address that and wrap a valid,
* English-locale intl context around them.
*/
import React from 'react';
import { IntlProvider, intlShape } from 'react-intl';
import { mount, shallow } from 'enzyme';
// You can pass your messages to the IntlProvider. Optional: remove if unneeded.
const messages = require('../locales/en'); // en.json
// Create the IntlProvider to retrieve context for wrapping around.
const intlProvider = new IntlProvider({ locale: 'en', messages }, {});
const { intl } = intlProvider.getChildContext();
/**
* When using React-Intl `injectIntl` on components, props.intl is required.
*/
function nodeWithIntlProp(node) {
return React.cloneElement(node, { intl });
}
/**
* Export these methods.
*/
export function shallowWithIntl(node) {
return shallow(nodeWithIntlProp(node), { context: { intl } });
}
export function mountWithIntl(node) {
return mount(nodeWithIntlProp(node), {
context: { intl },
childContextTypes: { intl: intlShape }
});
}
@AlexKund
Copy link

AlexKund commented Dec 6, 2019

Thats how I achieve the things:

import React from 'react';
import StandardFilterIntl, {StandardFilter} from 'bundles/components/Filter/StandardFilter';
import {mountWithIntl} from 'enzyme-react-intl';

const FilterComponent = mountWithIntl(<StandardFilterIntl {...standardFilterProps} />);
FilterComponent.find(StandardFilter).state()

@linzhaoqiang
Copy link

@mirague I use your method but cannot render the component properly. Can you help me with this problem?

import React from 'react';
import {injectIntl, IntlProvider} from 'react-intl'

import { mount, shallow } from 'enzyme';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
const defaultLocale = 'en'
const locale = defaultLocale

function mountWithIntl(node) {
  return mount(node, {
    wrappingComponent: IntlProvider,
    wrappingComponentProps: {
      locale,
      defaultLocale,
      messages: {},
    },
  })
}

function shallowWithIntl(node) {
  return shallow(node, {
    wrappingComponent: IntlProvider,
    wrappingComponentProps: {
      locale,
      defaultLocale,
      messages: {},
    },
  })
}

Enzyme.configure({
  adapter: new Adapter()
});

class Func extends React.Component {
  render() {
    return <div>{int.formatMessage({id:'xxx'})}</div>
  }
}

const Component = injectIntl(Func);

describe('Name of the group', () => {
  it('should ', () => {
    const component = shallowWithIntl(<Component/>);
    expect(component.find('div').length).toBe(1);
    console.log(component);
  });
});

`

 FAIL  src/App.test.js
  Name of the group
    × should  (15ms)

   Name of the group  should 

    expect(received).toBe(expected) // Object.is equality

    Expected: 1
    Received: 0

      45 |   it('should ', () => {
      46 |     const component = shallowWithIntl(<Component/>);
    > 47 |     expect(component.find('div').length).toBe(1);
         |                                          ^
      48 |     console.log(component);
      49 |   });
      50 | });

      at Object.<anonymous> (src/App.test.js:47:42)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        4.275s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment