Apollo Compose and Enzyme mount - reactjs

I have an issue here. I'm using React Apollo as GraphQL client and compose from Apollo to create my component separated from my GraphQL rules.
Everything works fine but I need to write some tests. So here's my component:
export const NestedComponent(someProperty: MyProperty) => (
<div>
Some simple content...
{ someProperty.value }
Another content here...
</div>
);
class ParentComponent extends Component<Props, {
anotherProperty: string
}> {
constructor(props) {
super(props);
}
render() {
return (<NestedComponent someProperty={ value: anotherProperty } />);
}
}
const withSomeData = graphql(myQuery, {
props: ({ data }) => ({ someProperty: { value: data.value }),
});
export default compose(
withSomeData,
withApollo,
)(withTheme(WorkstreamSelector));
As you can see, it's a very simple component with a nested function component. No secrets. So, here's my problem, I'm using Airbnb Enzyme to test my components. For this, I use mount from Enzyme (this way I can mock Apollo layer and stuff the way I want) and then comes the problem.
When I simply try to mount a component that is out of Apollo's compose function, the mount function returns me the complete DOM of the object, but when I use compose, no matter what I do, I always get only the first level (note that I'm not using shallow, I'm using mount), and I can't test the nested components inside my parent's component.
Can someone help me?

Related

Difference Between Class.contextType and Context.Consumer with working example

I am trying to understand the React context API and was going through the official docs. I will appreciate if someone can throw some more light on the following points as the official doc does not address it clearly.
What is the difference in contextType and Consumer methods to
consume the values provided by Provider? In what situation we should
use which method?
Can the value exposed by Provider in a class based component, be
used by a react hook component using useContext? I had the same
setup and i ended up converting the useContext to Context.Consumer.
I have a very straightforward setup in which i have a Provider Class
based component which is exposing some state values. The Provider
has only one children component which is also a consumer. When i use
Context.Consumer in the children to fetch the values, everything
works as expected. But when i use contextType in the children
component, i see an empty object.
ContextProvider.js
import React from "react";
import {ContextConsumer} from "./ContextConsumer";
export const TestContext = React.createContext({
count: 1,
incrCount: (count)=>{
console.log(`count value :- ${count}`)
}
});
export class ContextProvider extends React.Component {
incrCount = () => {
this.setState({
count: this.state.count + 1,
});
};
state = {
count: 5,
incrCount: this.incrCount,
};
render() {
return (
<TestContext.Provider value={this.state}>
<ContextConsumer />
</TestContext.Provider>
);
}
}
ContextConsumer.js
import React from "react";
import { TestContext } from "./ContextProvider";
export class ContextConsumer extends React.Component {
static contextType=TestContext
componentDidMount() {
const {count,incrCount}= this.context;
console.log(`count:- ${(count)}`)
console.log(`incrCount:- ${incrCount}`)
}
render() {
return (
<div>
**// BELOW CODE IS WORKING AS EXPECTED**
<TestContext.Consumer>
{({ count, incrCount }) => (
<button onClick={incrCount}>Count is {count}</button>
)}
</TestContext.Consumer>
</div>
);
}
}
App.js
import {ContextProvider} from "../../playground/ContextProvider";
const output = (
<Provider store={reduxStore}>
<ContextProvider />
</Provider>
);
ReactDOM.render(output, document.getElementById("root"));
What is the difference in contextType and Consumer methods to consume the values provided by Provider? In what situation we should use which method?
The static contextType assignment was introduced in v16.6.0 as a way to use context outside of render method. The only difference between Consumer and static context is the fact that using contextType allows you use context outside of render method too
Can the value exposed by Provider in a class based component, be used by a react hook component using useContext?
Yes the context value from Provider can be used by useContext too. However you can only make use of useContext inside a functional component and not a class component and also after v16.8.0 or react which supports hooks
P.S. You must ensure one thing that you are not causing a circular dependency by importing provider in consumer component and also the other way around
Static contextType and class.contextType
useContext
context.Consumer
are almost same the difference between them is that (1) is used in class component and
useContext is a hook and the best thing is we can use this hook multiple times in one functional component.(3) can only be used in jsx or in render(return).(1)and(2) can be used outside return.
to put it more simply:
if (functional components) { useContext }
else {
if (multiple contexts) { Context.Consumer }
else { Class.contextType }
}
// "static contextType" is experimental lets avoid

React, Enzyme, Redux unit test connected-component in React 16+

I was trying to upgrade to react 16+ and my existing unit tests are failing. The application runs fine, only unit tests fails.
connected-component:
const componentsWrapper = (Components, selectData) => {
class BaseComponent extends Component {
constructor(props) {
super(props);
this.state = {
saveMode: false,
updateMode: false
};
this.foo = () => { };
}
render() {
return (
<Components
{...this.props}
/>
);
}
}
}
const mapDispatchToProps = dispatch => {
return selectData.getDispatch(dispatch);
};
const mapStateToProps = state => {
return selectData.getStore(state);
};
return connect(mapStateToProps, mapDispatchToProps)(BaseComponent);
};
export default componentsWrapper;
unit test:
class MockListComponent extends Component {
render() {
return (<div>Fake List</div>);
}
}
Components = componentsWrapper(MockListComponent, selectComponents);
wrapper = shallow(<Components store={store} />).dive();
instance = wrapper.instance()
// Here instance is null hence the rest will fail.
instance.foo = jest.fn();
instance is null because "Connect" component is functional or stateless. Source
NOTE: With React 16 and above, instance() returns null for stateless functional components.
I don't know how to get the instance to not be null or maybe refactor the code. I appreciate any help or hint.
These are the library versions I am trying to use:
react#16.2.9
enzyme#3.3.0
redux#4.0.5
I am not sure what is the exact behaviour you are trying to test, but in many cases, there is no need to explicitly 'test' the entire connected component unless you are planning to do integrated testing or functional testing.
To fix the issue you are facing, I would recommend you to export the child component (take note of export on BaseComponent):
const componentsWrapper = (Components, selectData) => {
export class BaseComponent extends Component {
// ... rest of the logic
}
}
Then, on your test file, we use enzyme's .find() to find the node that matches the selector. In this case, our selector is BaseComponent.
const wrapper = shallow(<Components store={store} />)
const wrapperInstance = wrapper.dive().find(BaseComponent).instance();
From there, you will have access to BaseComponent's class instance, and you can use it to call its methods, or spy on its methods.
The reason why you are facing the error stated on the question is explained on the enzyme documentation:
With React 16 and above, instance() returns null for stateless
functional components.
Therefore, you can only call .instance() on the component itself, rather than the entire connected component.

React design pattern for fetching items

I have a number of React components that need to fetch certain items to display. The components could be functional components, except for a very simple componentDidMount callback. Is there a good design pattern that would allow me to return to functional components?
class Things extends React.Component {
componentDidMount() {
this.props.fetchThings()
}
render() {
things = this.props.things
...
}
}
I'm using react-redux and I'm also using connect to connect my component to my reducers and actions. Here's that file:
import { connect } from 'react-redux'
import Things from './things'
import { fetchThings } from '../../actions/thing_actions'
const mapStateToProps = ({ things }) => ({
things: things,
})
const mapDispatchToProps = () => ({
fetchThings: () => fetchThings()
})
export default connect(mapStateToProps, mapDispatchToProps)(Things)
Would it make sense to fetch the things in this file instead? Maybe something like this:
import { connect } from 'react-redux'
import Things from './things'
import { fetchThings } from '../../actions/thing_actions'
const mapStateToProps = ({ things }) => ({
things: things,
})
class ThingsContainer extends React.Component {
componentDidMount() {
fetchThings()
}
render() {
return (
<Things things={this.props.things} />
)
}
}
export default connect(mapStateToProps)(ThingsContainer)
Functional components are meant to be components that don't do anything. You just give them props and they render. In fact, if your component needs to fetch anything at all, it's likely your component should be transformed into a container which fetches the data you need. You can then abstract the UI part of your component into one or more pure functional components which your container renders by passing the data it got as props.
I believe the presentational/container component split is the pattern you're looking for here.

Nested components testing with Enzyme inside of React & Redux

I have a component SampleComponent that mounts another "connected component" (i.e. container). When I try to test SampleComponent by mounting (since I need the componentDidMount), I get the error:
Invariant Violation: Could not find "store" in either the context or
props of "Connect(ContainerComponent)". Either wrap the root component
in a , or explicitly pass "store" as a prop to
"Connect(ContainerComponent)".
What's the best way of testing this?
Enzyme's mount takes optional parameters. The two that are necessary for what you need are
options.context: (Object [optional]): Context to be passed into the component
options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper
You would mount SampleComponent with an options object like so:
const store = {
subscribe: () => {},
dispatch: () => {},
getState: () => ({ ... whatever state you need to pass in ... })
}
const options = {
context: { store },
childContextTypes: { store: React.PropTypes.object.isRequired }
}
const _wrapper = mount(<SampleComponent {...defaultProps} />, options)
Now your SampleComponent will pass the context you provided down to the connected component.
What I essentially did was bring in my redux store (and Provider) and wrapped it in a utility component as follows:
export const CustomProvider = ({ children }) => {
return (
<Provider store={store}>
{children}
</Provider>
);
};
then, I mount the SampleComponent and run tests against it:
it('contains <ChildComponent/> Component', () => {
const wrapper = mount(
<CustomProvider>
<SampleComponent {...defaultProps} />
</CustomProvider>
);
expect(wrapper.find(ChildComponent)).to.have.length(1);
});
Option 1)
You can wrap the container component with React-Redux's Provider component within your test. So with this approach, you actually reference the store, pass it to the Provider, and compose your component under test inside. The advantage of this approach is you can actually create a custom store for the test. This approach is useful if you want to test the Redux-related portions of your component.
Option 2)
Maybe you don't care about testing the Redux-related pieces. If you're merely interested in testing the component's rendering and local state-related behaviors, you can simply add a named export for the unconnected plain version of your component. And just to clarify when you add the "export" keyword to your class basically you are saying that now the class could be imported in 2 ways either with curly braces {} or not. example:
export class MyComponent extends React.Component{ render(){ ... }}
...
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
later on your test file:
import MyComponent from 'your-path/MyComponent'; // it needs a store because you use "default export" with connect
import {MyComponent} from 'your-path/MyComponent'; // don't need store because you use "export" on top of your class.
I hope helps anyone out there.
There is also the option to use redux-mock-store.
A mock store for testing Redux async action creators and middleware. The mock store will create an array of dispatched actions which serve as an action log for tests.
The mock store provides the necessary methods on the store object which are required for Redux.
You can specify optional middlewares and your app specific initial state.
import configureStore from 'redux-mock-store'
const middlewares = []
const mockStore = configureStore(middlewares)
const initialState = {}
const store = mockStore(initialState)
const wrapper = mount(<SampleComponent store={store}/>)
You can use name export to solve this problem:
You should have:
class SampleComponent extends React.Component{
...
render(){
<div></div>
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SampleComponent)
You can add a export before class:
export class SampleComponent extends React.Component{
and import this component with no redux store:
import { SampleComponent } from 'your-path/SampleComponent';
With this solution you don't need to import store to your test files.
in an attempt to make the use of decorator syntax more testable I made this:
https://www.npmjs.com/package/babel-plugin-undecorate
input:
#anyOldClassDecorator
export class AnyOldClass {
#anyOldMethodDecorator
method() {
console.log('hello');
}
}
output:
#anyOldClassDecorator
export class AnyOldClass {
#anyOldMethodDecorator
method() {
console.log('hello');
}
}
export class __undecorated__AnyOldClass {
method() {
console.log('hello');
}
}
Hopefully this can provide a solid Option 3!

React-Redux - Reuseable Container/Connector

I am completely lost on the react-redux container (ie connector) concept as it is not doing what I anticipated. My issue is straight forward, and to me reasonable, yet I cannot find a well written example of how to accomplish it.
Let us say we have a react component that connects to a store that has product context, and we will call this component ProductContext.
Furthermore, let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions on every other component that may need products.
Illustratively this is what I mean:
from DiscountuedProducts:
<ProductContext >
// do something with props from container
</ProductContext >
from SeasonalProducts:
<ProductContext >
// do something with props from container
</ProductContext >
From the examples I see at react-redux, it appears to me that their containers lump both seasonal and discontinued products in the container itself. How is that reusable?
from the ProductContextComponent:
<section >
<DiscontinuedProducts />
<SeasonalProducts />
</section >
Complicating matters, while trying to keep a cool head about this most frustrating matter, "nebulous tersity" seems to be the only responses I receive.
So here is my ProductContext:
#connect(state => ({
products: state.productsReducer.products
}))
export default class ProductContext extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const { dispatch } = this.props;
const clientId = this.context.clientInfo._id;
dispatch(fetchProductsIfNeeded(clientId));
}
// get from parent
static contextTypes = {
clientInfo: PropTypes.object.isRequired
};
static propTypes = {
children: PropTypes.node.isRequired,
dispatch: PropTypes.func.isRequired,
products: PropTypes.array
};
render() {
if (!this.props.products) return <Plugins.Loading />;
.....
return (
// I want to put the products array here
);
}
};
Then my thinking is if I do this:
from DiscountuedProducts:
<ProductContext >
// do something with props from container
</ProductContext >
DiscontinuedProducts should have knowledge of those products and it can simply filter for what is discontinued.
Am I completely wrong on this? Is this not reasonable to desire?
If anyone knows of a exhaustive example on the Net demonstrating how this can be achieved, I would much appreciate it being pointed out to me. I have spent over a week on this issue and am about ready to give up on react-redux.
UPDATE: A very slick solution below with use of a HOC.
If anyone knows of a exhaustive example on the Net demonstrating how this can be achieved, I would much appreciate it being pointed out to me.
Look at the Shopping Cart example.
Examples code from: shopping-cart/containers/CartContainer.js
Let us say we have a react component that connects to a store that has product context,
There isn't a store for products and a store for users, etc. There is one store for everything. You should use reducers to take the full store and reduce to what your component needs.
From the example:
import { getTotal, getCartProducts } from '../reducers'
const mapStateToProps = (state) => {
return {
products: getCartProducts(state),
total: getTotal(state)
}
}
Here state is the entire store.
let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions
Components don't dispatch actions, they call functions passed to them. The functions live in an actions module, which you import and pass to container as props. In turn, the container passes those functions to component props.
From the example:
import { checkout } from '../actions'
CartContainer.propTypes = {
checkout: PropTypes.func.isRequired
}
connect(mapStateToProps,
{ checkout }
)(CartContainer)
What connect does, it subscribes to store changes, calls the map function, merges with constant props (here the action function), and assigns new props to the container.
DiscontinuedProducts should have knowledge of those products and it can simply filter for what is discontinued
This is actually spot on. The knowledge you mention is the one store, and it should absolutely filter in a reducer.
Hopefully this clears things up.
I have found a more practical way of utilizing repetitive data from redux than the docs. It makes no sense to me to repeat mapToProps and dispatch instantiation on every blessed component when it was already done once at a higher level, and there inlies the solution. Make sure your app is Babel 6 compliant as I used decorators.
1. I created a higher order component for the context ....
product-context.js:
import React, { Component, PropTypes } from 'react';
// redux
import { connect } from 'react-redux';
import { fetchProductsIfNeeded } from '../../redux/actions/products-actions';
// productsReducer is already in state from index.js w/configureStore
#connect(state => ({
products: state.productsReducer.products
}))
export default function ProductContext(Comp) {
return (
class extends Component {
static propTypes = {
children: PropTypes.node,
dispatch: PropTypes.func.isRequired,
products: PropTypes.array
};
static contextTypes = {
clientInfo: PropTypes.object.isRequired;
};
componentDidMount() {
const { dispatch } = this.props;
const clientId = this.context.clientInfo._id;
dispatch(fetchProductsIfNeeded(clientId));
}
render() {
if (!this.props.products) return (<div>Loading products ..</div>);
return (
<Comp products={ this.props.products }>
{ this.props.children }
</Comp>
)
}
}
)
}
2. component utilizing product-context.js
carousel-slider.js:
import React, { Component, PropTypes } from 'react';
......
import ProductContext from '../../../context/product-context';
#Radium
#ProductContext
export default class CarouselSlider extends Component {
constructor(props) {
super(props); }
......
static showSlideShow(carouselSlides) {
carouselSlides.map((slide, index) => {
......
results.push (
......
)
});
return results;
}
render() {
const carouselSlides = this.props.products;
const results = CarouselSlider.showSlideShow(carouselSlides);
return (
<div id="Carousel" className="animation" ref="Carousel">
{ results }
</div>
);
}
}
So there you go. All I needed was a decorator reference to product-context, and as a HOC, it returns the carousel component back with the products prop.
I saved myself at least 10 lines of repetitive code and I removed all related contextType from lower components as it is no longer needed with use of the decorator.
Hope this real world example helps as I detest todo examples.

Resources