reactjs - examples - Inyectar el objeto react-intl en componentes de Enzyme montados para realizar pruebas
react intl examples (1)
He creado funciones de ayuda para parchear las funciones existentes de Enzyme mount()
y shallow()
. Ahora estamos usando estos métodos de ayuda en todas nuestras pruebas donde usamos los componentes React Intl.
Puede encontrar la información esencial aquí: https://gist.github.com/mirague/c05f4da0d781a9b339b501f1d5d33c37
Por el bien de mantener los datos accesibles, aquí está el código en pocas palabras:
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
EDITAR: ¡Resuelto! Desplácese hacia abajo para la respuesta
En nuestras pruebas de componentes, necesitamos que tengan acceso al contexto react-intl
. El problema es que estamos montando componentes individuales (con Enzyme''s mount()
) sin su <IntlProvider />
parent wrapper. Esto se resuelve envolviendo al proveedor, pero luego la root
apunta a la instancia de IntlProvider
y no a CustomComponent
.
Las pruebas con React-Intl: documentos de enzimas todavía están vacías.
<CustomComponent />
class CustomComponent extends Component {
state = {
foo: ''bar''
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
Caso de prueba estándar (deseado) (Enzyme + Mocha + Chai)
// This is how we mount components normally with Enzyme
const wrapper = mount(
<CustomComponent
params={params}
/>
);
expect( wrapper.state(''foo'') ).to.equal(''bar'');
Sin embargo, dado que nuestro componente utiliza FormattedMessage
como parte de la biblioteca react-intl
, obtenemos este error cuando react-intl
el código anterior:
Uncaught Invariant Violation: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
Envolviéndolo con IntlProvider
const wrapper = mount(
<IntlProvider locale="en">
<CustomComponent
params={params}
/>
</IntlProvider>
);
Esto proporciona a CustomComponent
el contexto intl
que solicita. Sin embargo, al tratar de hacer afirmaciones de prueba como estas:
expect( wrapper.state(''foo'') ).to.equal(''bar'');
plantea la siguiente excepción:
AssertionError: expected undefined to equal ''''
Esto, por supuesto, porque intenta leer el estado de IntlProvider
y no nuestro CustomComponent
.
Intentos de acceder a CustomComponent
He intentado lo siguiente en vano:
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
La pregunta es: ¿Cómo podemos montar CustomComponent
con el contexto intl
mientras todavía podemos realizar operaciones "root" en nuestro CustomComponent
?