Hello,
I am trying to shallow render a component and test it using enzyme. My component looks like this:
class LogIn extends Component {
static propTypes = {
login: PropTypes.func
};
handleSubmit = () => {
const email = this.refs.email;
this.props.login(email.value);
};
render() {
return (
<div>
<input type="text" ref="email" />
<button onClick={this.handleSubmit}>Click me!</button>
</div>
);
}
}
Inside one of my tests with karma and enzyme, I run the following line:
buttons.at(0).simulate('click'); // This is the button element
This results in this error:
TypeError: Cannot read property 'value' of undefined
Basically the this.refs are undefined. I can't seem to get the refs to work inside the component that I'm testing. Is this even possible with enzyme?
You might have noticed the ShallowRenderer doesn't have docs for a ref() mehthod, while the MounedRenderer does. If you want to test refs you have to mount.
I believe the reason is that shallow rendering does not maintain an internal instance and therefore it can't hold a ref. That is the purpose of shallow rendering. Even FB ReactTestUtils shallowRendering doesn't work with refs.
@blainekasten Thank you! That cleared it up for me. I'm using mount() now. I have one other unrelated question, is it possible to simulate the entry of text on an uncontrolled input with enzyme? For example:
component.find('.email').at(0).simulate('keypress', { which: 'a' })
When I do this on an input like this:
<input type="text" ref="email" placeholder="Email" className="email" />
I am unable to successfully simulate the typing. Is this even possible with an uncontrolled input? Let me know if I should create a new issue for this. Thank you!
I haven't looked into the simulate code in a while. But I'm pretty sure that just triggers the internal React event system. Since that component is uncontrolled it wouldn't be registered in reacts event system.
I think you would want to do a full fledge Event in this case. Something like:
const e = new Event('keyboard');
// setup the event
input.dispatchEvent(e);
@blainekasten Thanks for the response! I've been trying to implement this, here's what I've got so far:
const input = inputs.at(0);
const evt = new Event('keyboard');
evt.initKeyEvent ('keypress', true, true, (new Object()),
0, 0, 0, 0,
0, 'e'.charCodeAt(0));
const canceled = !input.dispatchEvent(evt);
if (canceled) {
console.log('does not work');
} else {
console.log('works');
}
inputs comes from component.find('input') using enzyme, and inputs.at(0) maps to a text input. I am getting this error: evt.initKeyEvent is not a function.
I just can't seem to get it to work.
I think you are nearly there but not quite. The problem is with:
const input = inputs.at(0)
You are wanting to trigger the event on the actual DOM node. at() returns an EnzymeWrapper. So two things you'll have to do.
mount and not shallow for this test.Untested, But I think this is the code change you want.
const wrapper = shallow(<MyElement />);
const input = wrapper.find('input').at(0).node; // node is the actual DOM node
// event stuff....
input.dispatchEvent(evt);
@blainekasten I ended up using redux-form to make the inputs controlled and then just simulated the form submit, but I will certainly be trying this on my tests in the future. Thank you!
@blainekasten: Would be much needed to be able to INJECT refs in case of shallow rendering.
This would enable to make all these test possible that reference refs.
I understand why refs are not available in shallow (non-DOM rendering) but why should it be prohibited to test such code? I was trying to inject refs manually without luck so far.
@blainekasten Hi blainekasten , Am trying to render my component using enzyme and mocha . While rendering the child component into parent component using mount(node[, options]) It could not attach the component. Could you please help me on this ? PFA
my component looks like this parent.js
render() {
return (
<div id="parent">
<div id="child"></div>
<div id="childs"></div>
</div>
);
}
and test file is parent.spec.js
parentobj = mount(<parentcomp/>);
var parentfindobj = parentobj.find("#child");
mount(<childcomp/>,{options: { attachTo: parentfindobj }});
@blainekasten I am trying to test this
bodymovin.loadAnimation({
container: this.refs.bodyMovinRef, // the dom element that will contain the animation
renderer: 'svg',
loop: true,
autoplay: true,
animationData: this.props.animationData // the animation json object
});
render() {
return (
<div className = {`${this.props.className} bodyMovin`} ref="bodyMovinRef" />
);
}
How do I test the ref is being set correctly?
My test looks like this:
describe('BodyMovin', () => {
let bodyMovinMock;
beforeEach(() => {
jasmineEnzyme();
bodyMovinMock = {
loadAnimation: jasmine.createSpy('loadAnimation'),
destroy: jasmine.createSpy('destroy')
};
BodyMovinRewireAPI.__Rewire__('bodymovin', bodyMovinMock);
});
afterEach(() => {
BodyMovinRewireAPI.__ResetDependency__('bodymovin');
});
fit('loads the animation when the component mounts', () => {
let component = mount(<BodyMovin animationData="mockAnimationData"/>);
let bodyMovinClassName = component.find('ref.bodyMovinRef');
let mockAnimationData = { container: bodyMovinClassName, renderer: 'svg', loop: true, autoplay: true, animationData: 'mockAnimationData' };
expect(bodyMovinMock.loadAnimation).toHaveBeenCalledWith(mockAnimationData);
expect(bodyMovinMock.loadAnimation.getDOMNode()).to.have.property('ref');
});
});
Most helpful comment
You might have noticed the
ShallowRendererdoesn't have docs for aref()mehthod, while theMounedRendererdoes. If you want to testrefsyou have to mount.I believe the reason is that shallow rendering does not maintain an internal instance and therefore it can't hold a ref. That is the purpose of shallow rendering. Even FB ReactTestUtils shallowRendering doesn't work with refs.