web-dev-qa-db-ja.com

テスト用にマウントされたEnzymeコンポーネントにreact-intlオブジェクトを注入する

EDIT:解決しました!下にスクロールして回答を探します


コンポーネントテストでは、 _react-intl_ コンテキストにアクセスできるようにする必要があります。問題は、_<IntlProvider />_親ラッパーなしで(Enzymeの mount() で)単一のコンポーネントをマウントしていることです。これは、プロバイダーをラップすることで解決されますが、rootIntlProviderではなくCustomComponentインスタンスを指します。

React-Intlを使用したテスト:酵素 ドキュメントはまだ空です。

<CustomComponent />

_class CustomComponent extends Component {
  state = {
    foo: 'bar'
  }

  render() {
    return (
      <div>
        <FormattedMessage id="world.hello" defaultMessage="Hello World!" />
      </div>
    );
  }
}
_

標準テストケース(希望)(酵素+モカ+チャイ)

_// This is how we mount components normally with Enzyme
const wrapper = mount(
  <CustomComponent
    params={params}
  />
);

expect( wrapper.state('foo') ).to.equal('bar');
_

ただし、このコンポーネントでは_react-intl_ライブラリの一部としてFormattedMessageを使用しているため、上記のコードを実行するとこのエラーが発生します。

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


IntlProviderでラップする

_const wrapper = mount(
  <IntlProvider locale="en">
    <CustomComponent
      params={params}
    />
  </IntlProvider>
);
_

これにより、CustomComponentが要求するintlコンテキストが提供されます。ただし、次のようなアサーションをテストしようとすると、

_expect( wrapper.state('foo') ).to.equal('bar');
_

次の例外が発生します。

_AssertionError: expected undefined to equal ''_

これは、IntlProviderではなくCustomComponentの状態を読み取ろうとするためです。


CustomComponentへのアクセスの試み

私は役に立たないように以下を試しました:

_const wrapper = mount(
  <IntlProvider locale="en">
    <CustomComponent
      params={params}
    />
  </IntlProvider>
);


// Below cases have all individually been tried to call `.state('foo')` on:
// expect( component.state('foo') ).to.equal('bar');

const component = wrapper.childAt(0); 
> Error: ReactWrapper::state() can only be called on the root

const component = wrapper.children();
> Error: ReactWrapper::state() can only be called on the root

const component = wrapper.children();
component.root = component;
> TypeError: Cannot read property 'getInstance' of null
_

質問は:CustomComponentintlコンテキストでマウントしながら、CustomComponentに対して「ルート」操作を実行するにはどうすればよいですか?

20
Mirage

既存のEnzyme mount()およびshallow()関数にパッチを適用するヘルパー関数を作成しました。 React Intlコンポーネントを使用するすべてのテストで、これらのヘルパーメソッドを使用しています。

Gistはここにあります: https://Gist.github.com/mirague/c05f4da0d781a9b339b501f1d5d33c37


データへのアクセスを維持するために、コードを簡単に説明します。

helpers/intl-test.js

/**
 * 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';

const messages = require('../locales/en'); // en.json
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 default {
  shallowWithIntl(node) {
    return shallow(nodeWithIntlProp(node), { context: { intl } });
  },

  mountWithIntl(node) {
    return mount(nodeWithIntlProp(node), {
      context: { intl },
      childContextTypes: { intl: intlShape }
    });
  }
};

CustomComponent

class CustomComponent extends Component {
  state = {
    foo: 'bar'
  }

  render() {
    return (
      <div>
        <FormattedMessage id="world.hello" defaultMessage="Hello World!" />
      </div>
    );
  }
}

CustomComponentTest.js

import { mountWithIntl } from 'helpers/intl-test';

const wrapper = mountWithIntl(
  <CustomComponent />
);

expect(wrapper.state('foo')).to.equal('bar'); // OK
expect(wrapper.text()).to.equal('Hello World!'); // OK
26
Mirage