How to snapshot test a component with nested component? - javascript

I'm testing a component with a nested component inside which use redux. I'm using shallow test for the component.
This is my test:
describe("Header", () =>
void it("renders correctly", () => {
const renderer = new ShallowRenderer()
const tree = renderer.render(<Header />)
expect(tree).toMatchSnapshot();
})
The snapshot output is:
exports[`Header renders correctly 1`] = `
<mockConstructor
render={[Function]}
/>
`;
Is this correct? Shouldn't a snapshot show my component?

Try using shallow from the enzyme package:
import { shallow } from 'enzyme';
import Header from './Header';
describe('Header', () => {
it('should render', () => {
const wrapper = shallow(<Header />);
expect(wrapper).toMatchSnapshot();
});
});

I changed my code as you say and the snapshot output for your snippet is:
exports[`Header renders correctly 1`] = `ShallowWrapper {}`;
Looking information about this output i found Jest/Enzyme ShallowWrapper is empty when creating Snapshot
Basically i have to use enzyme-to-json, so i changed my code to:
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
...
describe("Header", () =>
void it("renders correctly", () => {
const tree = shallow(<Header />)
expect(toJson(tree)).toMatchSnapshot()
})
)
In the github site for enzyme-to-json there is a example that show as my test
import React, {Component} from 'react';
import {shallow} from 'enzyme';
import toJson from 'enzyme-to-json';
it('renders correctly', () => {
const wrapper = shallow(
<MyComponent className="my-component">
<strong>Hello World!</strong>
</MyComponent>
);
expect(toJson(wrapper)).toMatchSnapshot();
});
But the snapshot is:
exports[`Header renders correctly 1`] = `
<mockConstructor
render={[Function]}
/>
`;

Related

How to test mapStateToProps using React Redux and Jest?

When I create a test for my connected React component where I want to test the mapStateToProps logic I run into a problem which I'm not sure how to solve.
Error message
Expected: 1
Received: undefined
24 | it('should show previously rolled value', () => {
25 | // test that the state values were correctly passed as props
> 26 | expect(wrapper.props().lastRolledNumber).toBe(1);
When I check the wrapper.props() it only returns the store object and no properties.
To make sure it's not my code I have found an example that should work to simplify it, but I get the same problem when using that exact version in my app (option #2, https://jsramblings.com/2018/01/15/3-ways-to-test-mapStateToProps-and-mapDispatchToProps.html)
I think it might have to do with the React 16+ version, which I found mentioned here: https://airbnb.io/enzyme/docs/api/ReactWrapper/props.html
.props() => Object
Returns the props object for the root node of the wrapper. It must be
a single-node wrapper. This method is a reliable way of accessing the
props of a node; wrapper.instance().props will work as well, but in
React 16+, stateless functional components do not have an instance.
See .instance() => ReactComponent
Does anyone know how to test this in a good way to see that the logic is working as expected without exporting the private mapStateToProps function directly?
Component
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
// Component 1 - "Base component"
// Exporting it is a good practice for testing its own logic
export const Dice = ({ lastRolledNumber, onRollDice }) => (
<div>
<p>The last rolled number was {lastRolledNumber}.</p>
<button onClick={onRollDice}>Roll dice</button>
</div>
);
Dice.propTypes = {
lastRolledNumber: PropTypes.number.isRequired,
onRollDice: PropTypes.func.isRequired
}
const mapStateToProps = (state) => ({
lastRolledNumber: state.lastRolledNumber
});
const mapDispatchToProps = (dispatch) => ({
onRollDice: () => dispatch({ type: 'ROLL_DICE' })
});
// Component 2 - Container component
// Export it as a default export
export default connect(mapStateToProps, mapDispatchToProps)(Dice);
Test
import React from 'react';
import { shallow } from 'enzyme';
import '../test-config'; // Setup Enzyme & Adapter
import DiceContainer from './Dice';
// Create the mock store
import configureMockStore from 'redux-mock-store';
const mockStore = configureMockStore();
describe('Dice', () => {
let wrapper, store;
beforeEach(() => {
const initialState = {
lastRolledNumber: 1
};
store = mockStore(initialState);
// Shallow render the container passing in the mock store
wrapper = shallow(
<DiceContainer store={store} />
);
});
it('should show previously rolled value', () => {
// test that the state values were correctly passed as props
expect(wrapper.props().lastRolledNumber).toBe(1);
});
it('should roll the dice again when button is clicked', () => {
// test that the component events dispatch the expected actions
wrapper.simulate('rollDice');
const actions = store.getActions();
expect(actions).toEqual([ { type: 'ROLL_DICE' } ]);
});
});
You are almost there. You need to call .dive() method so that you can get the Dice component rather than the DiceContainer component which is wrapped by the connect HOC of react-redux module.
Short answer:
wrapper = shallow(<DiceContainer store={store} />).dive();
A Completed working example:
index.jsx:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
export const Dice = ({ lastRolledNumber, onRollDice }) => (
<div>
<p>The last rolled number was {lastRolledNumber}.</p>
<button onClick={onRollDice}>Roll dice</button>
</div>
);
Dice.propTypes = {
lastRolledNumber: PropTypes.number.isRequired,
onRollDice: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
lastRolledNumber: state.lastRolledNumber,
});
const mapDispatchToProps = (dispatch) => ({
onRollDice: () => dispatch({ type: 'ROLL_DICE' }),
});
export default connect(mapStateToProps, mapDispatchToProps)(Dice);
index.test.jsx:
import React from 'react';
import { shallow } from 'enzyme';
import configureMockStore from 'redux-mock-store';
import DiceContainer from '.';
const mockStore = configureMockStore();
describe('Dice', () => {
let wrapper;
let store;
beforeEach(() => {
const initialState = {
lastRolledNumber: 1,
};
store = mockStore(initialState);
wrapper = shallow(<DiceContainer store={store} />).dive();
});
it('should show previously rolled value', () => {
expect(wrapper.props().lastRolledNumber).toBe(1);
});
it('should roll the dice again when button is clicked', () => {
wrapper.simulate('rollDice');
const actions = store.getActions();
expect(actions).toEqual([{ type: 'ROLL_DICE' }]);
});
});
Unit test results:
PASS src/stackoverflow/59771991/index.test.jsx (9.645s)
Dice
✓ should show previously rolled value (19ms)
✓ should roll the dice again when button is clicked (2ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 11.505s
Test coverage html report:
In my case, I was able to write the test with render by this -
import React from 'react';
import '#testing-library/jest-dom';
import {
screen, cleanup, render,
} from '#testing-library/react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
const store = configureMockStore()({
headerState: {
showBack: false,
},
});
const mockProps = { ... };
describe('Component', () => {
beforeAll(() => {
render(
<Provider store={store}>
<Component {...mockProps} />
</Provider>,
);
});
afterAll(cleanup);
test('Test', () => {
screen.debug();
});
});

How to unit test React components with 'react-testing-library'

I'm using react-testing-library for unit testing React components.
import React from 'react'
import 'react-testing-library/cleanup-after-each'
import {fireEvent, render, cleanup} from 'react-testing-library'
import MyComponent from '../MyComponent';
const translations = require('../../translations');
afterEach(cleanup);
describe('test MyComponent', () => {
it('render functional component', () => {
// extract container which is 'HTMLDivElement'
const {container} = render(<MyComponent translations={translations} />)
})
How do I manipulate the container element as a DOM element? E.g.
console.log(container.classList, container.id, container.innerHTML);
using the props and methods listed at: https://developer.mozilla.org/en-US/docs/Web/API/Element. I can't get any values so far.

Jest/Enzyme Class Component testing with React Suspense and React.lazy child component

So I converted an import used in a class component to React.lazy import api and wrapped it in a Suspense tag. When I test that class component, enzyme throws an error "Enzyme Internal Error: unknown node with tag 13". Is there a way to render and test the mount of the lazy loaded component rather than using shallow render?
I've already tried async await to wait until the promise of the lazy load resolved but that didn't work neither, like so:
it('async await mount', () async () => {
const wrapper = await mount(<Component />)
}
here's example code:
Component.js
import React, { PureComponent, Suspense } from 'react'
const ChildComponent = React.lazy(() => import('../ChildComponent'))
export default class Component extends PureComponent {
constructor() {
super()
this.state = {
example: null
}
}
doSomething = () => this.setState({
example: 'example'
})
render() {
return (
<div>
<p>Example</p>
<Suspense fallback={<div>...loading</div>}>
<ChildComponent
example={this.state.example}
doSomething={this.doSomething}
/>
</Suspense>
</div>
)
}
}
Component.test.js
import React from 'react'
import renderer from 'react-test-renderer'
import { mount } from 'enzyme'
import Component from '../../Component'
describe('Component', () => {
// snapshot renders loading and not children
it('example snapshot of tree', () => {
const tree = renderer.create(<Component />).toJSON()
expect(tree).toMatchSnapshot()
})
it('example mount test', () => {
// this test fails and throws error I state above
const wrapper = mount(<Component />)
wrapper.setState({ example: 'example' })
expect(wrapper.state.example).toBe('example')
})
})
I read that Enzyme does not support React 16.6 Suspense API yet but I wanted to know if there was still a way to test the mounted so I can use things like simulate and find API from Enzyme.
Solution
The GitHub issue linked by ChuckJHardy has been resolved merged and released. You are now able to use the mount API in enzyme as of 1.14.0.
References
https://github.com/airbnb/enzyme/pull/1975
I needed to test my lazy component using Enzyme. Following approach worked for me to test on component loading completion:
const myComponent = React.lazy(() =>
import('#material-ui/icons')
.then(module => ({
default: module.KeyboardArrowRight
})
)
);
Test Code ->
//mock actual component inside suspense
jest.mock("#material-ui/icons", () => {
return {
KeyboardArrowRight: () => "KeyboardArrowRight",
}
});
const lazyComponent = mount(<Suspense fallback={<div>Loading...</div>}>
{<myComponent>}
</Suspense>);
const componentToTestLoaded = await componentToTest.type._result; // to get actual component in suspense
expect(componentToTestLoaded.text())`.toEqual("KeyboardArrowRight");
This is hacky but working well for Enzyme library.

shallow rendering could not find component

hi everyone I am testing my react application using jest. While testing a component I found that a test breaks unexpectedly throwing error as
Method “props” is only meant to be run on a single node. 0 found instead.
test file
import React from 'react';
import {shallow} from 'enzyme';
import {AddLibraryItem} from '../../components/AddLibraryItem';
import libraryItems from '../fixtures/libraryItems';
let addLibraryItem, history, wrapper;
beforeEach(() => {
addLibraryItem = jest.fn();
history = {push: jest.fn()};
wrapper = shallow(<AddLibraryItem addLibraryItem={addLibraryItem} history={history}/>);
})
test('should execute on submit button successfully', () => {
console.log(wrapper);
wrapper.find('LibraryItemForm').prop('onSubmit')(libraryItems[0]);
expect(addLibraryItem).toHaveBeenLastCalledWith(libraryItems[0]);
expect(history.push).toHaveBeenLastCalledWith("/");
});
Component
import React from 'react';
import {connect} from 'react-redux';
import LibraryItemForm from './LibraryItemForm';
import {addLibraryItem} from '../actions/libraryA';
export class AddLibraryItem extends React.Component {
onSubmit = (libraryItem) => {
this.props.addLibraryItem(libraryItem);
this.props.history.push('/');
}
render () {
return (
<div>
<LibraryItemForm onSubmit={this.onSubmit} />
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
addLibraryItem: (libraryItem) => dispatch(addLibraryItem(libraryItem))
}
}
const ConnectedAddLibraryItem = connect(undefined, mapDispatchToProps)(AddLibraryItem);
export default ConnectedAddLibraryItem;
The piece of test was earlier working very fine and test of 'LibraryItemForm' is also working fine and also rendering perfectly.
I am not getting what is wrong with it.
Is there any fix of it?
You probably forgot to dive():
wrapper.find(LibraryItemForm).dive().prop('onSubmit')(libraryItems[0]);
Enzyme documentation here.

Jest: Add component children elements to snapshot

When trying to run Sample.test.jsx with jest, below snapshot is created.
Sample.jsx
class Sample extends React.Component {
render() {
return (
<Link to={'/xyz'}>
<div className={cx('l-tab')}>
Click Me
</div>
</Link>
);
}
}
Sample.test.jsx
import React from 'react';
import { mount } from 'enzyme';
test('testing Component', () => {
const component = mount(
<Sample />
);
expect(component).toMatchSnapshot();
});
Snapshot
exports[`testing Component 1`] = `
<Link to="/xyz" />
`
Question- How can I get the child elements of Link in the snapshot?
Snapshot expected:
exports[`testing Component 1`] = `
<Link to="/xyz" >
<div className='l-tab'>
Click Me
</div>
</Link>
`
If you don't want to do shallow rendering, you could just try the react-test-renderer instead of enzyme:
import React from 'react';
import renderer from 'react-test-renderer';
import Sample from './Sample';
test('testing Component', () => {
const component = renderer.create(<Sample />).toJSON();
expect(component).toMatchSnapshot();
});
enzyme mount children method..
.children([selector]) => ReactWrapper
from enzyme mount docs :
const wrapper = mount(<ToDoList items={items} />);
expect(wrapper.find('ul').children()).to.have.length(items.length);
ref: http://airbnb.io/enzyme/docs/api/ReactWrapper/children.html
you can also find the element by className :
const wrapper = mount(<MyComponent />);
expect(wrapper.find('.my-button').hasClass('disabled')).to.equal(true);
ref: http://airbnb.io/enzyme/docs/api/ReactWrapper/hasClass.html

Categories

Resources