Find component by display name when the component is stateless functional, with Enzyme - unit-testing

I have the following components:
// Hello.js
export default (React) => ({name}) => {
return (
<div>
Hello {name ? name : 'Stranger'}!
</div>
)
}
// App.js
import createHello from './Hello'
export default (React) => () => {
const Hello = createHello(React)
const helloProps = {
name: 'Jane'
}
return (
<Hello { ...helloProps } />
)
}
// index.js
import React from 'react'
import { render } from 'react-dom'
import createApp from './App'
const App = createApp(React)
render(
<App />,
document.getElementById('app')
)
And I want to set up a test to see if the App component contains one Hello component. I tried the following, using Tape and Enzyme:
import createApp from './App'
import React from 'react'
import test from 'tape'
import { shallow } from 'enzyme'
test('App component test', (assert) => {
const App = createApp(React)
const wrapper = shallow(<App />)
assert.equal(wrapper.find('Hello').length === 1, true)
})
But the result was that the length property of the find result was equal to 0, when I was expecting it to be equal to 1. So, how do I find my Hello component?

There are a couple of things you can do in this case. Enzyme can match component constructors based on the constructor's static .displayName or .name properties, or by referential equality. As a result, the following approaches should all work:
Direct Reference
you can import the actual components in your tests and find them using direct references to the component:
// NavBar-test.js
import NavBar from './path/to/NavBar';
...
wrapper.find(NavBar).length)
Named Function Expressions
If you use named function expressions to create your stateless functional components, the names should still work.
// NavBar.js
module.exports = function NavBar(props) { ... }
Static .displayName property
You can add a static .displayName property on the components:
// NavBar.js
const NavBar = (props) => { ... };
NavBar.displayName = 'NavBar';

Try to import the Hello component in the top of your file and then update your assertion to find the actual component and not the name of it. Like below:
import createApp from './App'
import Hello from './Hello'
import React from 'react'
import test from 'tape'
import { shallow } from 'enzyme'
test('App component test', (assert) => {
const App = createApp(React)
const wrapper = shallow(<App />)
assert.equal(wrapper.find(Hello).length === 1, true)
})
Btw for all the enzyme users out there the assertion would be something like:
expect(wrapper.find(Hello)).toHaveLength(1);

Related

Cannot read property 'getters' of undefined - VueJS unit testing with jest

I am writing unit tests for VueJS components and have consulted the "Applying Global Plugins and Mixins" section of Vue Test Utils Common Tips. I have a component that depends on the Vuex store so it makes sense that I would transpose the example under that section for my purposes.
Here is my code for that component's specific .spec.js file:
import { createLocalVue, mount } from '#vue/test-utils'
import AppFooter from '#/components/AppFooter/AppFooter'
import store from '#/store'
describe('AppFooter component', () => {
const localVue = createLocalVue()
localVue.use(store)
it('AppFooter should have header slot', () => {
const AppFooterComponent = mount(AppFooter, {
localVue
})
/* TODO: Replace with a more appropriate assertion */
expect(true).toEqual(true)
})
})
This is pretty faithful to the example provided in the link above. However, the error I receive when I run the test suite is as follows:
Should I be installing the Vue store differently?
To elaborate on my comment, I believe it should look like the following, where you pass in the store on the mount() call.
import { createLocalVue, mount } from '#vue/test-utils'
import AppFooter from '#/components/AppFooter/AppFooter'
import Vuex from 'vuex'
import store from '#/store' //you could also mock this out.
describe('AppFooter component', () => {
const localVue = createLocalVue()
localVue.use(Vuex)
it('AppFooter should have header slot', () => {
const AppFooterComponent = mount(AppFooter, {
store,
localVue
})
/* TODO: Replace with a more appropriate assertion */
expect(true).toEqual(true)
})
})
I believe that you have something like this.$store.getters[someBeautifulGetterName] in you component. To make your tests mount the component your need to initialise store and pass it into your testing component. Just keep in mind that this would be a brand new instance of Vuex. Here is the code
import { shallowMount } from '#vue/test-utils'
import Vue from 'vue'
import Vuex from 'vuex'
import Tags from '#/components/Tags'
Vue.use(Vuex)
Vue.prototype.$store = new Vuex.Store()
const factory = (propsData) => {
return shallowMount(Tags, {
propsData: {
...propsData
}
})
}
describe('Tags', () => {
it("render tags with passed data", () => {
const wrapper = factory({ loading: true })
// TODO:
})
})

React-Enzyme testing- DropDown not rendering properly

I have a React DropDown
import React from 'react'
import PropTypes from 'prop-types'
import {FormGroup, ControlLabel, FormControl} from 'react-bootstrap'
class dropDown extends React.Component {
static defaultProps = {
inputValue: '',
dropdownText: 'Dropdown'
};
static propTypes = {
dropdownText: PropTypes.string.isRequired
};
constructor (props) {
super(props)
this.state = {inputValue: ''}
}
render () {
return <div>
<FormGroup controlId="locale-value">
<ControlLabel>{this.props.dropdownText}</ControlLabel>
<FormControl componentClass="select" value={this.state.inputValue}>
<option value="1">1</option>
<option value="2">2</option>
</FormControl>
</FormGroup>
</div>
}
}
export default dropDown
I am now trying to test it using Enzyme
import React from 'react'
import ReactDOM from 'react-dom'
import LocaleDropDown from './LocaleDropDown'
import ReactTestUtils from 'react-dom/test-utils'
import {FormGroup, ControlLabel, FormControl} from 'eui-components'
import { shallow, mount } from 'enzyme'
import renderer from 'react-test-renderer'
/*
* Verify that the app can be rendered without any fatal errors.
*/
describe('<dropDown/> component', () => {
it('renders the Select component', () => {
const div = document.createElement('div')
const wrapper = ReactDOM.render(<LocaleDropDown />, div)
console.log(wrapper.find('select').length.to.equal(1));
});
});
It says wrapper.find is not a function.
Since I am rendering, I was expecting the dropdown to be rendered and able to access all the options inside.
This could be a naming issue. You don't show where you've defined the LocaleDropDown component. Your first file only exports a component called dropDown.
Assuming that the first file is named LocaleDropDown, you should change the import statement at the top of the test file.
import dropDown from './LocaleDropDown'
For what it's worth, I would use the enzyme shallow function to mount the component, seeing as how you're already importing it.
Your test could look something like:
/*
* Verify that the app can be rendered without any fatal errors.
*/
describe('<dropDown/> component', () => {
const dropDown = () => {
return shallow(<dropDown />);
}
it('renders the Select component', () => {
const select = dropDown().find('select');
expect(select.length).toEqual(1);
});
});

Why is my immutable Redux store not the same in UI and test?

I'm building up an AppHeader component that connects to a Redux store to get its props. Ideally, I'd probably do it different, but decided to use as an exercise in testing a connected component (I'm new to React).
I'm trying to test this component by using redux-mock-store, but when I get it working in tests, the UI fails. When I get it working in the UI, the tests fail.
The PROBLEM_LINE in AppHeader.component.js below, is where the symptom originates.
When set to appHeader: state.get('AppHeader'), the tests pass successfully, but the console shows:
Uncaught TypeError: state.get is not a function at
Function.mapStateToProps [as mapToProps]`
When set to appHeader: state.AppHeader, the UI correctly displays "Real App Title" inside of an but the test now throws:
TypeError: Cannot read property 'toJS' of undefined
This seems to be an issue with using Immutable.js structures. If I change both initialState variables using plain JS objects, the tests pass and the correct value is displayed. I feel like I must be using immutable incorrectly, or am not getting stores set up correctly with it.
I've read nearly all the posts returned by Google re: testing connected components/containers but most were using state.AppHeader or state.get('AppHeader') in mapStateToProps, and most except a precious few were more on how to hook up redux and react but not so much testing it.
I tried to forego using redux-mock-store, and creating my own store (i.e. a function with dispatch, subscribe, etc) but that didn't solve any problems and only created new ones. Maybe I need to reinvestigate that if that's a better way in the end.
src/index.js
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux'
import {Router, browserHistory} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';
import configureStore from './store/configureStore';
import routes from './routes';
const store = configureStore;
const history = syncHistoryWithStore(browserHistory, store);
render(
<Provider store={store}>
<Router history={history} routes={routes} />
</Provider>,
document.getElementById('root')
);
src/store/configureStore.js
import {createStore, combineReducers} from 'redux';
import {routerReducer} from 'react-router-redux';
import * as reducers from '../ducks';
const rootReducer = combineReducers({
routing: routerReducer,
...reducers
});
export default createStore(
rootReducer
);
src/ducks/index.js
import AppHeader from './AppHeader.duck';
export {
AppHeader
};
src/ducks/AppHeader.duck.js
import {fromJS} from 'immutable';
////////////
/// Actions
const SET_APP_TITLE = 'new-react/AppHeader/SET_APP_TITLE';
////////////
/// Reducer
const initialState = fromJS({ <-- USING IMMUTABLE HERE
title: 'Real App Title'
});
export default function reducer(state = initialState, action){
switch(action.type){
case SET_APP_TITLE:
return state.set('title', action.payload.title);
default:
return state;
}
}
////////////
/// Action Creators
export function setAppTitle(title){
return {
type: SET_APP_TITLE,
payload: {title}
};
}
module.exports = reducer;
src/components/AppHeader.component.js
import React from 'react';
import {connect} from 'react-redux';
export class AppHeader extends React.PureComponent {
render() {
const appHeader = this.props.appHeader;
return (
<div className="appHeader">
<h1 className="appHeader_title">
{appHeader.title}
</h1>
</div>
);
}
}
AppHeader.propTypes = {
appHeader: React.PropTypes.object
};
const mapStateToProps = state => {
return {
appHeader: state.AppHeader.toJS() <-- PROBLEM LINE
};
}
export default connect(
mapStateToProps
)(AppHeader);
src/components/AppHeader.component.spec.js
import React from 'react';
import ReactDOM from 'react-dom';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import configureMockStore from 'redux-mock-store'
import {fromJS} from 'immutable';
import AppHeader from './AppHeader';
let initialState = fromJS({ <-- USING IMMUTABLE AGAIN
AppHeader: {
title: 'Mock App Title'
}
});
const mockStore = configureMockStore([])(initialState);
describe('AppHeader', () => {
let component;
beforeEach(() => {
const wrapper = mount(
<Provider store={mockStore}>
<AppHeader />
</Provider>
);
component = wrapper.find('AppHeader');
});
it('renders without crashing', () => {
expect(component).toBeDefined();
});
it('shows the app title', () => {
expect(component.find('.appHeader_title').text())
.toBe('App Header Title');
});
});
All dependencies were installed in the last day or so, so are the latest version from npm install. The app itself was created with create-react-app.

How to unit test a method of react component?

I am trying to unit test my reactjs component:
import React from 'react';
import Modal from 'react-modal';
import store from '../../../store'
import lodash from 'lodash'
export class AddToOrder extends React.Component {
constructor(props) {
super(props);
this.state = {checked: false}
//debugger
}
checkBoxChecked() {
return true
}
render() {
console.log('testing=this.props.id',this.props.id )
return (
<div className="order">
<label>
<input
id={this.props.parent}
checked={this.checkBoxChecked()}
onChange={this.addToOrder.bind(this, this.props)}
type="checkbox"/>
Add to order
</label>
</div>
)
}
}
export default AddToOrder;
Just to get started I am already struggling to assert the checkBoxChecked method:
import React from 'react-native';
import {shallow} from 'enzyme';
import {AddToOrder} from '../app/components/buttons/addtoorder/addtoorder';
import {expect} from 'chai';
import {mount} from 'enzyme';
import jsdom from 'jsdom';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc
global.window = doc.defaultView
let props;
beforeEach(() => {
props = {
cart: {
items: [{
id: 100,
price: 2000,
name:'Docs'
}]
}
};
});
describe('AddToOrder component', () => {
it('should be handling checkboxChecked', () => {
const wrapper = shallow(<AddToOrder {...props.cart} />);
expect(wrapper.checkBoxChecked()).equals(true); //error appears here
});
});
```
How can I unit test a method on the component? This is the error I am getting:
TypeError: Cannot read property 'checked' of undefined
You are almost there. Just change your expect to this:
expect(wrapper.instance().checkBoxChecked()).equals(true);
You can go through this link to know more about testing component methods using enzyme
For those who find the accepted answer as not working, try using .dive() on your shallow wrapper before using .instance():
expect(wrapper.dive().instance().somePrivateMethod()).toEqual(true);
Reference: Testing component methods with enzyme
Extend of previous answer.
If you have connected component (Redux) , try next code :
const store=configureStore();
const context = { store };
const wrapper = shallow(
<MyComponent,
{ context },
);
const inst = wrapper.dive().instance();
inst.myCustomMethod('hello');

Enzyme object has no useful methods .to .length

I have the following test
import test from 'ava';
import React from 'react';
import { shallow } from 'enzyme';
import A from '../../../src/components/A/index';
import B from '../../../src/components/B/index';
console.log('loaded component test');
test('shallow', t => {
const wrapper = shallow(<A />);
t.is(wrapper.find(B).length, 38);
});
Component A is a listing of several component Bs. What could I be doing wrong? I'm using Enzyme and AVA.
Warning: A: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://facebook/react-special-props)
Warning: A: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://facebook/react-special-props)
t.is(wrapper.find(B).length, 38)
|
0
1 test failed [16:55:47]
1 uncaught exception
1. components › A › index › shallow
AssertionError: 0 === 38
Test.fn (index.js:11:5)
The problem was that I needed a full DOM in order test these nested components. I added
/** setup.js
* provides a document for enzyme mount tests
* simply require this file to make the environment available
*/
import test from 'ava';
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
test(t => {
t.pass('DOM setup'); // silences ava warning
});
and then I utilized mount
import test from 'ava';
import React from 'react';
import { mount, shallow } from 'enzyme';
import setup from '../../setup';
import A from '../../../src/components/A/index';
import B from '../../../src/components/B/index';
test('should display no B if A given no props', t => {
const wrapper = mount(<A />);
t.is(wrapper.find(B).length, 0);
});
test('should display two Bs if A given two B via props', t => {
const testBs = [
{ id: 1},
{ id: 2},
];
const wrapper = mount(<A Bees={testBs} />);
t.is(wrapper.find(B).length, 2);
});
Note I did not use the jsx syntax when passing B into find(). It also helped to upgrade ava, babel-core versions and add the es2017 preset to get async/await support.