Testing for text contained in React component with Enzyme and Jest - reactjs

For my React component I have the following:
const ArchiveButton = ({collection, onClick}) => {
return (
<span>
{ collection.archived &&
<Button bsStyle="link" onClick={onClick}><i className="fa fa-rotate-left" /> Unarchive</Button>
}
{ !collection.archived &&
<Button bsStyle="link" onClick={onClick}><i className="fa fa-archive" /> Archive</Button>
}
</span>
);
};
I'm attempting to test by passing in different values for collection.archived and I want to check for the existence of the text "Unarchive" vs "Archive". When I do a wrapper.find('Button') and try and check against .text() it is just <Button /> and the only way I've figured out how to test it is:
const wrapper = shallow(<ArchiveButton onClick={onClick} {...props}/>);
let button = wrapper.find('Button').prop('children');
expect(button[1]).toMatch(/\sUnarchive/);
Seems a bit off though, not sure. Thanks!

It's because you're using shallow rendering, so your nested Button component doesn't get rendered. As you've seen, you can access the nested component props to test the values you pass to it.
If you prefer to have the button content rendered, use normal rendering instead.
Note that the nested Button is not a normal DOM element, which would be rendered anyway even if you use shallow rendering, but it's instead a component itself.
If you think about it, if you don't use shallow rendering in your case, you're not really unit-testing your component, as you're also asserting something about the Button component. If you use shallow rendering and access the props of the nested component, you're really just testing that your code is calling the Button component with the correct arguments, and you're not making any assumptions as how the Button component will render.
This is normally what you want to do in a unit test.

Related

How to get react grand-child node to render when child is out of my control

I have a react component which uses a 3rd party library Component as a Child node. The Grand children (or the children of the 3rd party libary) are under my control. When my component receives new props it re-renders, however the 3rd party component seems to stop my components grand-children from re-rendering also, even though the props my component received, are passed to the non-re-rendering components directly
If I remove the 3rd party component then my component re-renders as do the grand-children.
render() {
<div>
<ThirdPartyComponent props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
A concrete example can be found on this code sandbox: codesandbox.io/s/silly-grass-7frlx
I'd expect my MyGrandChildrenComponents component to get updated when this.props.products changes... Any hints?
This can happen if ThirdPartyComponent is a stateful component and is not handling its prop updates correctly. One way to force a re-render is to add a key prop to your ThirdPartyComponent and update its value when a re-render is needed.
render() {
<div>
<ThirdPartyComponent key={something-that-changes-when-rerender-needed} props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
If ThirdPartyComponent is a PureComponent it should re render (and so re render its children) when one of its props changes so you can try:
render() {
<div>
<ThirdPartyComponent props={blah} products={this.props.products}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
To trigger re renders when this.props.products changes.
But any prop should do.
If ThirdPartyComponent has a custom implementation of shouldComponentUpdate, then you will have to find the specific prop which triggers update if it exists.

React onClick on stateless component

I have been trying to fire an onClick on a react component. The event fires if I use
<button onClick={()=>this.goToPage('next')}>Next Page</button>
If I use the same method on a stateless component, it doesn't fire:
<PageButton onClick={()=>this.goToPage('next')}>Next Page</PageButton>
Why this is not possible?
Because what you are defining is a custom component. Note, that everything that you provide to the custom component is considered as props. So, your onClick method is also provided as props. Essentially you'll be required to do -
<PageButton onClick={()=>this.goToPage('next')}>Next Page</PageButton>
and in your <PageButton /> component -
<button onClick={this.props.onClick}>Next Page</button>
if you know what props you are providing to this component and not providing any unnecessary props, you can even spread the props object, like -
<button {...this.props}>Next Page</button>
Note - If you have other props to provide to this component as well, kindly refrain from using this method, as this will result in many unrecognized function warnings.
PS: Even if you write
<PageButton style={{backgroundColor: 'red}}>Next Page</PageButton>
it won't work because, this is treated as a prop. You'll need to handle the style prop in the render method of this <PageButton/> component
This does not work on a stateless component because the onClick is considered as a prop rather a event listener , you should implement.
For example inside PageButton you should implement something like this
render(){
return <div onClick={()=>this.props.onClick('next')}/>
}

React does not recognize the `anyTouched` prop on a DOM element

i have redux form inside my reactstrap modal.when my popup appear,console log show below warning.without Form it's working charm.
You have a component say it's name is CustomComponent that retuns something like this
<div>
<Form {...this.props}>
<p> this.props.pText </p>
<Button>I'm a button named {this.props.buttonName} </Button>
</Form>
</div>
and when you render it in another file you do this
<CustomComponent pText="hi" buttonName="randomName">
so what happens is you are passing the buttomName, pText to your Form which doesn't accept pText, buttonName (remember, Your button & accept those props but not the form itself, so the virtual react DOM guesses that the prop is not for react, so it must be a real DOM prop & doesn't know what to do with it, to solve the problem, simply remove the {...this.props}
this is a result of how React is handling custom vs predefined DOM attributes -> see here
https://reactjs.org/warnings/unknown-prop.html

REACT Warning Unknown props parsing to child component [duplicate]

I've built my own custom react-bootstrap Popover component:
export default class MyPopover extends Component {
// ...
render() {
return (
<Popover {...this.props} >
// ....
</Popover>
);
}
}
The component is rendered like so:
// ... my different class ...
render() {
const popoverExample = (
<MyPopover id="my-first-popover" title="My Title">
my text
</MyPopover >
);
return (
<OverlayTrigger trigger="click" placement="top" overlay={popoverExample}>
<Button>Click Me</Button>
</OverlayTrigger>
);
}
Now, I want to add custom props to MyPopover component like that:
my text
And to use the new props to set some things in the popover
for example -
<Popover {...this.props} className={this.getClassName()}>
{this.showTheRightText(this.props)}
</Popover>
but then I get this warning in the browser:
Warning: Unknown props popoverType on tag. Remove these props from the element.
Now, I guess that I can just remove the {...this.props} part and insert all the original props one by one without the custom props, but In this way I lose the "fade" effect and also it's an ugly way to handle this problem. Is there an easier way to do it?
Updated answer (React v16 and older):
As of React v16, you can pass custom DOM attributes to a React Component. The problem/warning generated is no longer relevant. More info.
Original answer (React v15):
The easiest solution here is to simply remove the extra prop before sending it to the Popover component, and there's a convenient solution for doing that.
export default class MyPopover extends Component {
// ...
render() {
let newProps = Object.assign({}, this.props); //shallow copy the props
delete newProps.popoverType; //remove the "illegal" prop from our copy.
return (
<Popover {...newProps} >
// ....
</Popover>
);
}
}
Obviously you can (and probably should) create that variable outside your render() function as well.
Basically you can send any props you want to your own component, but you'd have to "clean" it before passing it through. All react-bootstrap components are cleansed from "illegal" props before being passed as attributes to the DOM, however it doesn't handle any custom props that you may have provided, hence why you have to do your own bit of housekeeping.
React started throwing this warning as of version 15.2.0. Here's what the documentation says about this:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
[...]
To fix this, composite components should "consume" any prop that is intended for the composite component and not intended for the child component.
For further reading, check this page from the official react site.

Add custom props to a custom component

I've built my own custom react-bootstrap Popover component:
export default class MyPopover extends Component {
// ...
render() {
return (
<Popover {...this.props} >
// ....
</Popover>
);
}
}
The component is rendered like so:
// ... my different class ...
render() {
const popoverExample = (
<MyPopover id="my-first-popover" title="My Title">
my text
</MyPopover >
);
return (
<OverlayTrigger trigger="click" placement="top" overlay={popoverExample}>
<Button>Click Me</Button>
</OverlayTrigger>
);
}
Now, I want to add custom props to MyPopover component like that:
my text
And to use the new props to set some things in the popover
for example -
<Popover {...this.props} className={this.getClassName()}>
{this.showTheRightText(this.props)}
</Popover>
but then I get this warning in the browser:
Warning: Unknown props popoverType on tag. Remove these props from the element.
Now, I guess that I can just remove the {...this.props} part and insert all the original props one by one without the custom props, but In this way I lose the "fade" effect and also it's an ugly way to handle this problem. Is there an easier way to do it?
Updated answer (React v16 and older):
As of React v16, you can pass custom DOM attributes to a React Component. The problem/warning generated is no longer relevant. More info.
Original answer (React v15):
The easiest solution here is to simply remove the extra prop before sending it to the Popover component, and there's a convenient solution for doing that.
export default class MyPopover extends Component {
// ...
render() {
let newProps = Object.assign({}, this.props); //shallow copy the props
delete newProps.popoverType; //remove the "illegal" prop from our copy.
return (
<Popover {...newProps} >
// ....
</Popover>
);
}
}
Obviously you can (and probably should) create that variable outside your render() function as well.
Basically you can send any props you want to your own component, but you'd have to "clean" it before passing it through. All react-bootstrap components are cleansed from "illegal" props before being passed as attributes to the DOM, however it doesn't handle any custom props that you may have provided, hence why you have to do your own bit of housekeeping.
React started throwing this warning as of version 15.2.0. Here's what the documentation says about this:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
[...]
To fix this, composite components should "consume" any prop that is intended for the composite component and not intended for the child component.
For further reading, check this page from the official react site.

Resources