Set material-ui TextField value for testing purposes
You can also be interested in:
Recently I began my trip in the world of react and redux. Everyone is hot with these "new" technologies, and after my previous experiences with angular I decided to give it a try.
In this entry I just want to share a tip about how to set the value of a material-ui TextField for testing purposes.
Scenario
I have a simple login form, with username and password fields, and a submit button. When the submit button is clicked, a function is called, dispatching an action which gets as arguments the username and password inputs values. Such values are computed using this.refs.FIELD_NAME.getValue()
inside the parent component.
What follows is my code, the bold lines are the one of interest for this entry:
import React from 'react' import { connect } from 'react-redux' import { push } from 'react-router-redux' import { async } from 'redux-api' import TextField from 'material-ui/lib/text-field' import RaisedButton from 'material-ui/lib/raised-button' import AuthApi from 'api/Auth' type Props = { auth: PropTypes.object.isRequired, onSubmit: PropTypes.func.isRequired, } export class Login extends React.Component { props: Props; login () { return () => { this.props.onSubmit(this.refs.user.getValue(), this.refs.password.getValue()) } } render () { let error if (this.props.auth.error || this.props.auth.data.error) { let message = this.props.auth.error ? this.props.auth.error.message : this.props.auth.data.message error = <p className='error alert alert-danger'>{message}</p> } else { error = '' } return ( <section className='login'> <h1>Login</h1> {error} <div className='form-group'> <TextField ref='user' hintText='Username' /> </div> <div className='form-group'> <TextField type='password' ref='password' hintText='Password' /> </div> <RaisedButton label='send' primary onMouseDown={this.login()} /> </section> ) } } const mapStateToProps = (state) => { return { auth: state.auth } } const mapDispatchToProps = (dispatch) => { return { onSubmit: (user, password) => { let checkRedirect = (data) => { if (!data.error) { // must redirect console.log('login success -> redirect') dispatch(push('/')) } console.log('login error') } async( dispatch, (cb) => AuthApi.actions.login({user: user, password: password}, cb) ).then((data) => checkRedirect(data)) } } } export default connect( mapStateToProps, mapDispatchToProps )(Login)
There are some test to write here:
- check that error messages are displayed when the API returns some sort of error
- check that input fields are rendered
- check that a submit button is rendered
- check that submit button calls the onSubmit function
- check that the onSubmit function is called with the right params
We'll discuss here about the last point, so we need a way to check if the onSubmit function was called with the right parameters. In order to do this, in our test we need to set the TextField values to some text.
I wonder you might be tempted to use the setValue method (there's always a setValue when a getValue method exists, don't you think?), well such method does not exist! At least in the last realeses. So how to set this value?
// assuming _rendered is the rendered parent component (using TestUtils) // THIS WON'T WORK! _rendered.refs.user.setValue('MYVALUE') // THIS NEITHER! ReactDOM.findDOMNode(_rendered.refs.user).value = 'MYVALUE'
The second attempt is near to the solution, the problem is that the TextField component contains many dom elements, not just an input, so we need to find this input first:
// find the input element inside TextField let input_user = TestUtils.findRenderedDOMComponentWithTag(_rendered.refs.user, 'input') // set its value ReactDOM.findDOMNode(input_user).value = 'MYVALUE'
That's it! Now we can use sinon spies to check the arguments that the function received, and check them against the values we just set. Here comes the complete testing code:
/* eslint-disable no-unused-vars */ import React from 'react' import ReactDOM from 'react-dom' import { bindActionCreators } from 'redux' import TestUtils from 'react-addons-test-utils' import {Login} from 'containers/Login' import RaisedButton from 'material-ui/lib/raised-button' function shallowRender (component) { const renderer = TestUtils.createRenderer() renderer.render(component) return renderer.getRenderOutput() } function renderWithProps (props = {}) { return TestUtils.renderIntoDocument( <Login {...props} /> ) } function shallowRenderWithProps (props = {}) { return shallowRender(<Login {...props} />) } describe('(Component) Login', () => { let _component, _rendered, _props, _spies beforeEach(function () { _spies = {} }) describe('Submit Button', () => { let _button beforeEach(function () { _props = { auth: { data: { } }, onSubmit: (_spies.onSubmit = sinon.spy()) } _rendered = renderWithProps(_props) _button = TestUtils.findRenderedComponentWithType(_rendered, RaisedButton) }) it('Should exist.', function () { expect(_button).exist }) it('Should have onMouseDown property.', function () { expect(_button.props.onMouseDown).exist }) it('Should call login action.', function () { _spies.onSubmit.should.have.not.been.called _button.props.onMouseDown() _spies.onSubmit.should.have.been.called }) it('Should call login action with right params.', function () { let input_user = TestUtils.findRenderedDOMComponentWithTag(_rendered.refs.user, 'input') let input_password = TestUtils.findRenderedDOMComponentWithTag(_rendered.refs.password, 'input') ReactDOM.findDOMNode(input_user).value = 'usr' ReactDOM.findDOMNode(input_password).value = 'pwd' _spies.onSubmit.should.have.not.been.called _button.props.onMouseDown() _spies.onSubmit.should.have.been.called // use eql instead of equal in order to check values and not reference! expect(_spies.onSubmit.getCalls()[0].args).eql(['usr', 'pwd']) }) }) // ... })
That's quite easy indeed, but it cost me some time to get it working, that's why I share it with you. Have a good day!
Your Smartwatch Loves Tasker!
Your Smartwatch Loves Tasker!
Featured
Archive
- 2021
- 2020
- 2019
- 2018
- 2017
- Nov
- Oct
- Aug
- Jun
- Mar
- Feb
- 2016
- Oct
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2015
- Nov
- Oct
- Aug
- Apr
- Mar
- Feb
- Jan
- 2014
- Sep
- Jul
- May
- Apr
- Mar
- Feb
- Jan
- 2013
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2012
- Dec
- Nov
- Oct
- Aug
- Jul
- Jun
- May
- Apr
- Jan
- 2011
- Dec
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May