I'm trying to mock react-bootstrap <Modal> component with jest. <Modal> contains some "sub-components" as properties, for example <Modal.Header>. I'm trying to find out the correct way to mock this kind of components using Jest.
Here's a simple component using <Modal>:
// mymodal.js
import React from 'react'
import {Modal, Button} from 'react-bootstrap'
const MyModal = ({ visible, hide, title, onOk }) =>
<Modal show={visible} onHide={hide}>
<div className='simple-modal'>
<Modal.Header closeButton>{title}</Modal.Header>
<Modal.Body>
<div>I'm body</div>
</Modal.Body>
<Modal.Footer>
<Button className='invert-primary' onClick={hide}>
Cancel
</Button>
<Button bsStyle='primary' onClick={onOk}>
Ok
</Button>
</Modal.Footer>
</div>
</Modal>
export default MyModal
And here's basic snapshot test for it:
// mymodal.test.js
import renderer from 'react-test-renderer'
import * as React from 'react'
import MyModal from './mymodal'
jest.mock('react-bootstrap', () => {
function Modal(props) {
return <div>{props.children}</div>
}
Modal.Header = 'Modal.Header'
Modal.Body = 'Modal.Body'
Modal.Footer = 'Modal.Footer'
return({
Modal: Modal,
Button: 'Button',
})
})
describe('MyModal component', () => {
test('should render a modal', () => {
const modal = renderer.create(<MyModal
visible={true}
hide={() => ''}
onOk={() => ''}
title='Title' />)
expect(modal.toJSON()).toMatchSnapshot()
})
})
And here's snapshot:
// Jest Snapshot v1
exports[`MyModal component should render a modal 1`] = `
<div>
<div
className="simple-modal"
>
<Modal.Header
closeButton={true}
>
Title
</Modal.Header>
<Modal.Body>
<div>
I'm body
</div>
</Modal.Body>
<Modal.Footer>
<Button
className="invert-primary"
onClick={[Function]}
>
Cancel
</Button>
<Button
bsStyle="primary"
onClick={[Function]}
>
Ok
</Button>
</Modal.Footer>
</div>
</div>
`;
I'm quite happy with the snapshot result, but I'd like to get better output for the <Modal> component itself so that the snapshot would contain also component's name (currenlty <div>) and props (currently no props shown).
How should the mocking be done to achieve this?
I couldn't find way to achieve this with jest mocking. Finally I went with enzyme shallow rendering, which handles the basic mocking out of box. To do spanshot matching, I serialized the enzyme wrappers using enzyme-to-json npm package.
I cant seem to find a way to do a really simple thing like the below:
render() {
return (
<div className="messageDetail">
<div className="messageForm" >
Name: <input id="senderMsgName" value={this.props.nameValue} onChange={this.props.handleNameChange}/>
<br />
Body: <input id="senderMsgBody" value={this.props.bodyValue} onChange={this.props.handleBodyChange}/>
<br />
</div>
</div>
);
}
}
All I want to test is that the onChange function is called. how can I mock this out using sinon (bearing in mind it called on props)? I then will simulate it being called to test it.
I started with this: const handleBodyChangeSpy = sinon.spy();
and will expect this: expect(handleBodyChangeSpy).to.have.not.been.called();
just need some guidance on how to do that
I assume you are using ES6 and class Component extends React.Component. In that case you can create a spy by writing var spy = sinon.spy(Component.prototype, "handleBodyChange"). You can test your assertions on that spy.
More about creating spies here: http://sinonjs.org/releases/v1.17.7/spies/ (check under Creating spies: sinon.spy() Method Signatures).
Is there a better solution to dive into props.text in Enzyme?
Component:
export function TitleText ({ text, info, required }) {
return (
<div className={style.titleText}>
<div className={style.titleText} style={{ margin: 0 }} required={required}>{text}</div>
{info ? <InfoIcon className={style.infoIcon} /> : ''}
</div>
)
}
Test:
it('renders text from its props', () => {
const wrapper = setupTitleText('Test')
expect(wrapper.find(`.${style.titleText}`).node.props.children[0].props.children).toEqual('Test')
})
You can access the props using props(). Should at least work on shallow and mounted components.
wrapper.props().text
However, I would add a data attribute for testing.
Doing so would
let anyone working on your code would know that this element is being tested
let anyone working on your code would know that this element is being tested
prevent multiple elements being returned when you're looking to test something specific
prevent others breaking your tests if they decide to change the class.
I have created a react application that has all the logic (like onchange functions) in the parent and all the html rendering in the children components.
In order to test if the right state changes are happening i have to enter text to the input fields and enter values but the only problem is I dont know how to access the children elements when i mount the parent in js dom.
Should i move logic into the child components or should i only unit test the functions of the parent component?
This is from the parent
render() {
if (!this.state.accessTokenEntered) {
return <AccessTokenPage _onChange={this._onChange}
accessToken={this.state.inputs.accessToken}
env={this.state.inputs.env}
_onFirstClick={this._onFirstClick}/>;
and this is the child
const AccessToken = props =>(
<Layout>
<Input name={"accessToken"} displayName={"Access Token"} _onChange={props._onChange}
value={props.accessToken}/>
<DropDown name={"env"} displayName={"Environment"} _onChange={props._onChange}
data={['ppe', 'prod']} multiple={false}
value={props.env}/>
<br/>
<div style={{"textAlign": "center"}}>
<input type="button" onClick={props._onFirstClick} className="btn btn-primary" value="Submit"/>
</div>
</Layout>
);
and this is the childs child
const Input = props => (
<div className="form-group row">
<label className="col-xs-2 col-form-label">{props.displayName}</label>
<div className="col-xs-10">
<input name={props.name} className="form-control" value={props.value}
onChange={props._onChange}/></div>
</div>
);
You should be testing your child component. When the onChange event of the textbox is simulated, test if the onChange prop is called. This can be done by creating a mock or spy for the onChange prop.
An example test is shown below:
Mocking a prop.
beforeEach(() => {
onAdd = jest.fn();
add = TestUtils.renderIntoDocument(<Add onAdd={onAdd} />);
});
Test if the mock method is called:
it('Button click calls onAdd', () => {
const button = TestUtils.findRenderedDOMComponentWithTag(add, 'button');
const input = TestUtils.findRenderedDOMComponentWithTag(add, 'input');
input.value = 'Name 4';
TestUtils.Simulate.change(input);
TestUtils.Simulate.click(button);
expect(onAdd).toBeCalledWith(input.value);
});
I am using Jest and React TestUtils. Similar code is available for enzyme in my github project.
Is there a way to set css properties by using emberjs properties' auto binding ?
Something like:
<div {{bindAttr style="background-color: divColor;"}}>
...
</div>
Please note that as of ember 1.13. binding attributes (bind-attr) is deprecated. You would need to use code similar to this to bind to the class:
<div class={{myClass}}></div>
Furthermore, style binding in this fashion is not recommended because it can introduce XSS vulnerabilities. The HTML templating automatically escapes HTML to prevent XSS when using {{...}}, but style attributes have additional vulnerabilities outside of the scope of the built-in escaping.
The recommended approach is to escape the CSS yourself (i.e. creating the escapeCSS function that would escape the specific CSS appropriately to prevent XSS - this is not a built-in function. You could start with Ember.Handlebars.Utils.escapeExpression and add any additional checking from that base.) More information can be found here:
https://guides.emberjs.com/v2.2.0/templates/writing-helpers/#toc_escaping-html-content
Then you tell Ember that the string is "safe" by using Ember.String.htmlSafe, and Ember will not try to escape that content.
controller:
myStyle: Ember.computed('color', function() {
var color = escapeCSS(this.get('color'));
return new Ember.String.htmlSafe("color: " + color);
})
template:
<div style={{myStyle}}></div>
Reference: http://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes
Another simple way to do this is to add a computed property to your model.
Model ----
App.Photo = Em.Object.extend(
objectId: null
url: ""
style: (->
"background-image:url('" + #get("url") + "')"
).property("url")
)
Template -----
{{#each item in App.photoController}}
<div {{bindAttr style="item.style"}}></div>
{{/each}}
I got this working, and seems to be the simplest way to go about it.
Not exactly like that but close. You'll have to build the style string yourself. Look at this jsFiddle.
App = Ember.Application.create();
/**************************
* Models
**************************/
/**************************
* Views
**************************/
App.View = Ember.View.extend({
style: function() {
return "background-color:" + this.get('color');
}.property('color').cacheable()
});
/**************************
* Controllers
**************************/
App.set('controller', Ember.Object.create({
color: "transparent",
red: function() {
this.set('color', 'red');
},
blue: function() {
this.set('color', 'blue');
},
style: function() {
return "background-color:" + this.get('color');
}.property('color').cacheable()
}));
/**************************
* App Logic
**************************/
$(function() {
template:
{{#view Ember.Button target="App.controller" action="blue"}}BLUE{{/view}}
{{#view Ember.Button target="App.controller" action="red"}}RED{{/view}}
{{#view App.View colorBinding="App.controller.color" attributeBindings="style"}}
Color is {{App.controller.color}}
{{/view}}
<hr>
<div {{bindAttr style="App.controller.style"}}>And another way...</div>
Recent Ember version (2.3.0 as of this writing) allows straight-forward embedding of computed style.
// bar-graph.js
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['bar-graph'],
inlineStyle: Ember.computed('attrs.min', 'attrs.max', 'attrs.value', function() {
let min = this.get('attrs.min');
let max = this.get('attrs.max');
let value = this.get('attrs.value')
let percentage = Math.round(value / (max - min) * 100);
return new Ember.Handlebars.SafeString(`width: ${percentage}%`);
})
});
<!-- bar-graph.hbs -->
<div class="bar" style={{inlineStyle}}>{{value}}</div>
Live example
I have tried using answer provided by #WallMobile, but it has some syntax issues. So this is the correct syntax to be used.
App.Photo = Em.Object.extend({
objectId: null,
url: "",
style: function() {
return "background-image:url('" + this.get("url") + "')"
}.property("url")
})
HTMLBars now lets you do this - upgrade to the latest ember/ember-cli to take advantage of the new stuff.
There's a new addon which allows you to declare styles as JS objects and bind them to your component's style attribute. Check out ember-computed-style
import computedStyle from 'ember-computed-style';
export default Ember.Component.extend({
style: computedStyle('backgroundStyle'),
attributeBindings: ['style'],
backgroundStyle: function(){
return {
backgroundColor: this.get('divColor')
};
}.property('divColor'),
divColor: 'red'
});
This will produce:
<div style="background-color:red;"></div>
Another approach you could use is CSS custom properties.
ember-cli-custom-properties is an Ember add-on that binds component properties to CSS custom properties (variables). Its fairly simple to use. Once you install the add-on, the add-on makes the customProperties and customPropertyBindings property available on the #ember/component class.
For example, you could turn the raw HTML above into a Ember component, and give it a class name.
import Component from '#ember/component';
export default Component.extend ({
classNames: ['my-component'],
// Map the backgroundColor attribute to a CSS custom property
customProperties: ['backgroundColor']
});
You can then reference this class name in the styles in for your application.
.my-component {
background-color: var(--background-color);
}
Lastly, just set the backgroundColor attribute on the component to your desired color.
{{my-component backgroundColor="red"}}