I am very new to the enzyme/shallow render testing. I probably don't fully understand it yet.
Using this simplified component:
export const PlacementOption = (props) => <div/>
const UpdatingSelectField = (props) => <div/>
export class DevicePlatforms extends Component{
render(){
return <div>
<UpdatingSelectField/>
{this.props.value.device_platforms && this.props.children}
</div>
}
}
I am trying to tests DevicePlatforms. If this.props.value.device_platforms is not present children are not rendered and if it is they are rendered.
import React from 'react';
import { shallow, mount, render } from 'enzyme';
import { DevicePlatforms } from './Placement.js';
import { PlacementOption } from './Placement.js'
describe("<DevicePlatforms/>", function() {
it("with all devices selected renders all children", function() {
const value = {
device_platforms: "mobile/desktop",
}
const Component = <DevicePlatforms
value={value}
>
<PlacementOption/>
<PlacementOption/>
</DevicePlatforms>
const wrapper = shallow(Component)
expect(wrapper.find('PlacementOption')).toBe(2)
})
it("with no devices selected renders no children", function() {
const value = {}
const Component = <DevicePlatforms
value={value}
>
<PlacementOption/>
<PlacementOption/>
</DevicePlatforms>
const wrapper = shallow(Component)
expect(wrapper.find('PlacementOption')).toBe(0)
})
})
I have tried 'PlacementOption', PlacementOption in a find call.
All I get is a:
Expected ShallowWrapper({ many many lines of shallow wrapper content }) to be 3
Expected ShallowWrapper({ many many lines of shallow wrapper content }) to be 0
errors.
I can paste the "many many lines of shallow wrapper content" if needed but i don't think it is related and my problem is somewhere else - probably around somewhere around me not knowing how to use shallow render stuff.
You're asserting that an enzyme ShallowWrapper is equal to 3 or 0. This doesn't make sense.
Instead, the ShallowWrapper that is returned from .find() has a .length property. Try using that instead.
expect(wrapper.find('PlacementOption').length).toBe(2)
Related
In an app wrapped with withApp from this:
import { withApp } from "react-pixi-fiber";
And some code that looks a little like this:
class Foo extends React.Component {
// ...
eventHandler(evt) {
console.log("Event target =", evt.target);
}
render() {
let comp = (<Sprite interactive texture={...} pointerup={eventHandler} {/* ... */} />);
console.log("Component =", comp);
return (comp);
}
}
Doing this, the object that is logged as the "Event target" is a native PIXI Sprite object, which gives me access to methods like getBounds(). I'd like to be able to access this same sort of data from the comp variable (which I would then store somewhere), but when I log that, the object I get is something different. It has a $$typeof: Symbol(react.element), so I presume it's just a React object. I'd like to find a way to get access to the PIXI object associated with it so that I can do use that object later for doing things like bounds checking on an interactive setup with various other elements.
Is there a way to do this? Or: How can I do bounds checking on interactivity into an object that isn't the current target of an event from e.g. pointerup, pointermove, etc.?
It's been a while, but if you're still looking to solve this, you need to use a ref on your Sprite component. This isn't specific to react-pix-fiber, just standard React behavior. Using ReactDOM the ref would give you access to the html element, with PIXI and react-pixi-fiber, it gives you the PIXI display object.
import React, { createRef, Component } from "react";
class Foo extends Component {
constructor() {
super();
this.ref = createRef();
}
eventHandler() {
console.log(this.ref.current);
}
render() {
return (
<Sprite
interactive
texture={...}
pointerup={this.eventHandler.bind(this)}
/>
);
}
}
Besides the event handler, this.ref will be available in other lifecycle methods. Before render though, this.ref.current will be undefined.
Alternatively, you could use function components and hooks, either the useCallback hook or a combination for useRef and useEffect.
import React, { useCallback } from "react";
const Foo = () => {
const ref = useCallback(sprite => {
console.log(sprite);
}, []);
return (
<Sprite
interactive
ref={ref}
texture={...}
pointerup={this.eventHandler.bind(this)}
/>
);
}
import React, { useEffect, useRef } from "react";
const Foo = () => {
const ref = useRef();
useEffect(() => {
console.log(ref.current);
}, []);
return (
<Sprite
interactive
ref={ref}
texture={...}
pointerup={this.eventHandler.bind(this)}
/>
);
}
I created working examples of each of these methods here (see the BunnyClass, BunnyCallback and BunnyRef files):
https://codesandbox.io/s/hardcore-maxwell-pirh3
So, my goal is to reach the func cashedImageCheker which must return the string , based on recived from this.props -> imageFetchedURL value. And my problem is that I cannot understand how to put this value into the corresponding func cashedImageCheker of my component Banners.
I tried to use Enzyme .setProps({}) method to transfer mock props inside my testing Component, but for now I just gets undefined. Here is my test and component codes below.
Thank you for any help...
TEST FILE:
import React from 'react'
import { mount, shallow } from 'enzyme'
import Banners from '../'
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const { imageFetchedURL } = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
COMPONENT FILE:
import PropTypes from 'prop-types'
import React, { Component } from 'react'
class Banners extends Component {
cashedImageCheker = () => {
const { imageFetchedURL } = this.props
const imageFetchCached = imageFetchedURL && imageFetchedURL.length > 1
return imageFetchCached
}
render() {
const isLocalImgCached = this.cashedImageCheker()
return (
<div className='bannerWrap'>
{isLocalImgCached}
</div>
)
}
}
export default Banners
When testing your component, you can pass the props required by it while shallow mounting the component like
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL={'https://google.com/la-la-la/1.jpg'}/>)
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
Also the major problem in code test case is that cashedImageChecker doesn't return you an object but a value and hence you need to write
const imageFetchedURL = Component.instance().cashedImageCheker()
instead of
const { imageFetchedURL } = Component.instance().cashedImageCheker()
Doing the above change will allow you to setProps and test the component method too.
describe('<Banners />', () => {
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners />)
Component.setProps({
imageFetchedURL: 'https://google.com/la-la-la/1.jpg'
})
const imageFetchedURL = Component.instance().cashedImageCheker()
expect(imageFetchedURL.length).toBe(33)
})
})
So, my goal is to reach the func cashedImageCheker which must return
the string , based on recived from this.props -> imageFetchedURL
value.
Based on this one way is this
import React from 'react';
import { shallow } from 'enzyme';
import Banners from '../'
describe('<Banners />', () => {
const url = 'https://google.com/la-la-la/1.jpg';
it('imageFetchedURL must return true', () => {
const Component = shallow(<Banners imageFetchedURL= url />)
const imageFetchedURL = Component.instance().cashedImageCheker();
expect(imageFetchedURL.length).toBe(33)
})
})
And my problem is that I cannot understand how to put this value into
the corresponding func cashedImageCheker of my component Banners.
For this query you must know a thing when something is connected to props then during testing you can pass it on as actual props as I have done it
<Banners imageFetchedURL= url />
I tried to use Enzyme .setProps({}) method to transfer mock props
inside my testing Component
This is absolutely correct way too. It is another way of doing so.
But you were getting undefined because you were destructuring the return value which had nothing to do in your case.
I'm having a react component. Let's say Todo
import React, { Component } from 'react';
import injectSheet from 'react-jss';
class Todo extends Component {
// methods that incl. state manipulation
render() {
const { classes } = this.props;
return (
<div className={classes.container}>
<WhateverElse />
</div>
);
}
}
export default injectSheet(Todo);
I want to test it with enzyme. And there are two problems with it.
1. Access to the state
(and other component specific features)
When I shallow or mount that composer in the suite I can't get access to its state of course because it's not my component anymore but something new around it.
E.g. this code will give me an error:
it('should have state updated on handleAddTodo', () => {
const todo = shallow(<Todo />);
const length = todo.state('todos').length;
});
It says of course TypeError: Cannot read property 'length' of undefined because the state is not what I expect but this: { theme: {}, dynamicSheet: undefined }
This won't also give me access to props, refs etc.
2. Problems with theme provider
To provide some default colouring to the project like this:
import React, { Component } from 'react';
import Colors from './whatever/Colors';
class App extends Component {
render() {
return (
<ThemeProvider theme={Colors}>
<WhateverInside />
</ThemeProvider>
);
}
}
And of course when running tests it gives me an error [undefined] Please use ThemeProvider to be able to use WithTheme.
So my question is the following. Is there a way to solve this problem in “one single place”. How can I make enzyme agnostic of what is my component wrapped with?
If not, then how do I solve the problem if passing the ThemeProvider features down to the component that I'm testing?
And how can I access the state, ref, props and other things of the wrapped component?
Thank you!
here's what I'd do to test the component,
import React, { Component } from 'react';
import injectSheet from 'react-jss';
const styles = {};
class Todo extends Component {
// methods that incl. state manipulation
render() {
const { classes } = this.props;
return (
<div className={classes.container}>
<WhateverElse />
</div>
);
}
}
export { styles, Todo as TodoCmp }
export default injectSheet(styles)(Todo);
and in the test file, I'd add the following:
import { theme } from 'your-theme-source';
const mockReducer = (prev, curr) => Object.assign({}, prev, { [curr]: curr });
const coerceStyles = styles =>
typeof styles === 'function' ? styles(theme) : styles;
const mockClasses = styles =>
Object.keys(coerceStyles(styles)).reduce(mockReducer, {});
import {TodoCmp, styles} from 'your-js-file';
// then test as you'd normally.
it('should blah blah', () => {
const classes = mockClasses(styles);
const todo = shallow(<Todo classes={classes} />);
const length = todo.state('todos').length;
})
Please read more about it in the nordnet-ui-kit library specifically in the test directory. Here's a quick example
It is not related to JSS specifically. Any HOC wraps your component. Ideally you don't test any internals of a component directly.
Components public api is props, use them to render your component with a specific state and verify the rendered output with shallow renderer.
For some edge cases if first and preferred way is impossible, you can access the inner component directly and access whatever you need directly. You will have to mock the props the HOC would pass otherwise for you.
const StyledComponent = injectSheet(styles)(InnerComponent)
console.log(StyledComponent.InnerComponent)
If your component relies on theming, you have to provide a theme provider, always.
I've just started using jest and enzyme.
I'm having a problem to make my unit test work.
Im using redux-mock-store to mock store object.
it('shows an li for each comment', () => {
expect(container.find('li').length).toBe(2);
});
I'm expecting two li elements but I got 0 li elements.
I've got stuck in this error for a long time.
Could anyone please help me to figure out how to make this test pass, please!?
test result from jest test runner
Error: expect(received).toBe(expected)
Expected value to be (using ===):
2
Received:
0
Expected :2
Actual :0
CommentList.test.js
import React, { Component } from 'react';
import { shallow, mount, render } from 'enzyme';
import configureStore from 'redux-mock-store';
import CommentList from '../../components/CommentList';
jest.unmock('../../components/CommentList');
describe('CommentList', () => {
const initialState = {comments: ['New Comment', 'Other New Comment']};
const mockStore = configureStore();
let store;
let container;
beforeEach(() => {
store = mockStore(initialState);
container = shallow(<CommentList store={store} />);
});
//This passes.
it('renders the connected component', () => {
expect(container.length).toBe(1);
});
//This fails.
it('shows an li for each comment', () => {
expect(container.find('li').length).toBe(2);
});
});
CommentList.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
const propTypes = {};
const defaultProps = {};
const CommentList = (props) => {
const list = props.comments.map((comment) => {
<li key={comment}>{comment}</li>
});
return (
<ul className="comment-list">
{list}
</ul>
)
};
function mapStateToProps(state) {
return {
comments: state.comments
}
}
CommentList.propTypes = propTypes;
CommentList.defaultProps = defaultProps;
export default connect(mapStateToProps)(CommentList);
I think it should work like that if you render your component using mount instead of shallow inside your beforeEach().
With shallow rendering the renderer is not going as deep as to show your li components, because the tree will be connect(CommentList) -> CommentList -> ul -> li
You can also use dive to go one level deeper if needed, in case you want to stay shallow. See in the docs:
http://airbnb.io/enzyme/docs/api/ShallowWrapper/dive.html
You can either export not decorated CommentList component and test in by just passing comments props or you can try to wrap the CommentList component with the Provider and pass the store to it.
<Provider store={store}>
<CommentList />
</Provider>
More information you can find here:
http://redux.js.org/docs/recipes/WritingTests.html#connected-components
In order to make your example work you have to change list to:
const list = props.comments.map(comment => (
<li key={comment}>{comment}</li>
));
I am trying to use Enzyme's shallow wrapper to get the instance of my component and calling my class function over it. It shows me this error:
TypeError: tree.instance(...).onCampaignSelected is not a function
class ToolbarPage extends Component {
constructor(){
super();
this.onCampaignSelected = this.onCampaignSelected.bind(this);
this.state = {
item: null
}
}
onCampaignSelected (item) {
this.setState({item})
}
render () {
return (
<MyComponent onItemSelected={this.onCampaignSelected} />
)
}
}
function mapStateToProps(state){
buttons: state.toolbar.buttons
}
export default connect(mapStateToProps)(ToolbarPage);
My test case looks like this
import { shallow, mount } from 'enzyme';
import ToolbarPage from './ToolbarPage';
import configureStore from 'configureStore';
const store = configureStore();
const props = {
store,
isLoggedIn: false,
messageCounter: 0
}
describe('<ToolbarPage />', () => {
it('allows to select campaign', () => {
const tree = shallow(<ToolbarPage {...props}/>);
tree.instance().onCampaignSelected();
})
})
I also figured out that it is a wrapped component, so I won't get this function on the wrapped component. How do I access this function?
shallow does not render the full set of components with all of their properties & methods. It is intended for basic "did this thing render what I expected?" testing.
mount will give you everything and should allow you to test whatever you need. It is very useful for testing event handling & manipulating the state of components to test the interactions between components.