enzymejs / enzyme Goto Github PK
View Code? Open in Web Editor NEWJavaScript Testing utilities for React
Home Page: https://enzymejs.github.io/enzyme/
License: MIT License
JavaScript Testing utilities for React
Home Page: https://enzymejs.github.io/enzyme/
License: MIT License
Why are Sinon methods being exported from this package (for example in https://github.com/airbnb/reagent/blob/master/src/index.js#L20)? This poses a problem for us, as Sinon is throwing an exception on our codebase and we don't necessarily want to use Sinon and would rather have the flexibility to choose our own mocking/stubbing library. I understand that it's perhaps a convenience to be able to useSinon
, but It seems outside of the scope of what Reagent is trying to accomplish, and it is trivial to set up Sinon ourselves. Is there a chance you'll rethink this approach? I'd really like to be able to use Reagent for testing, but right now this is preventing us from doing so.
The idea is we want .get(index)
to have the same semantics as jQuery, where it returns the actual node at the index rather than the wrapped node. The current functionality that .get(index)
has can be moved to an .at(index)
method.
The enzyme
npm package includes a compiled _book
directory that occupies a lot of space:
~/src/app/node_modules/enzyme〉du -sh *
4.0K CHANGELOG.md
4.0K CONTRIBUTING.md
4.0K LICENSE.md
8.0K README.md
6.3M _book
4.0K book.json
236K build
356K docs
4.0K install-relevant-react.sh
2.7M node_modules
8.0K package.json
1.7M packages
172K src
Could this be included in npmignore so that it doesn't slow down installs?
When using shallow
, the .setProps()
method doesn't trigger the componentDidUpdate lifecycle method. However, .setState()
does trigger it. It seems like they both should (or both shouldn't).
describe('MyComponent', () => {
let spy;
before(function() {
spy = sinon.spy(MyComponent.prototype, 'componentDidUpdate');
});
after(function() {
spy.restore();
});
// fails
it('calls componentDidUpdate when setting props', () => {
const wrapper = shallow(<MyComponent color="yellow" />);
wrapper.setProps({ 'color': 'green' });
expect(spy.calledOnce).to.be.true;
});
// passes
it('calls componentDidUpdate when setting state', () => {
const wrapper = shallow(<MyComponent color="yellow" />);
wrapper.setState({ foo: 'bar' });
expect(spy.calledOnce).to.be.true;
});
});
When refactoring my tests to use enzyme, I wanted to find some elements based on their ref attribute. Is there a way to achieve that?
Lets say I have this component (it's just a made-up example):
export default class Example extends React.Component {
render () {
return (
<form>
<button ref="first-button">First button</button>
<button ref="second-button">Second button</button>
</form>
)
}
}
How do I find the second-button
using its ref attribute?
it('should find an element based on its ref attribute', () => {
const component = shallow(<Example />)
// This does not work yet, complex CSS selectors are not supported
const button = component.find('button[ref="second-button"]');
// This works, but I don't like depending on the JSX structure.
// My tests would fail if I added a new button or changed the button to a link, for example.
const button = component.find('button').get(1);
});
Is implementing a ref([key])
method a good/viable idea?
const button = component.ref('second-button');
I could help if you think this is something worth to have.
It would be really nice if the ReactWrapper
would expose a .unmount()
function that would behind the scenes perform something similar to doing mountedComponent.getInstance().componentWillUnmount();
for those components rendered with mount()
.
It would be also OK if the docs would mention what is the recommended way of doing this, too.
A use case for this is to prevent leaking listeners bound during componentWillMount
calls.
Potential API:
chai.use(require('reagent/chai'));
expect(wrapper).to.have.props({ foo: "bar" });
expect(wrapper).to.have.children();
expect(wrapper).to.not.have.children();
expect(wrapper).to.match('.foo.bar');
expect(wrapper).to.be.empty;
expect(wrapper).to.not.be.empty;
I'm trying to get enzyme working using a pretty standard webpack/karma setup. Webpack throws a bunch of errors related to the dependencies enzyme imports, including sinon, cheerio, and jsdom. Sinon gives webpack heartburn because it uses it's own require
(which seems to be documented here: webpack/webpack#304), and then there are a bunch of "Module not found" errors for jsdom and cheerio.
I'm importing enzyme as described in the docs and can't really think of anything else I might be missing, so I'm curious if anyone else is trying to use webpack and running into a similar issue, or if there's just something weird with my configuration. I'd expect to be able to just import it and have it work, but maybe I need to do something with those external libraries?
Here's my test file for reference:
import expect from 'expect';
import { shallow } from 'enzyme';
import React from 'react';
import Button from '../Button';
describe('Button', () => {
it('should render children passed in', () => {
const wrapper = shallow(
<Button><div className="foo" /></Button>
);
expect(wrapper.contains(<div className="foo" />)).toBe(true);
});
});
I won't paste the entire error log from webpack because it's pretty long, but here's a sample:
WARNING in ./~/enzyme/~/jsdom/~/acorn/dist/acorn.js
Critical dependencies:
1:478-485 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
@ ./~/enzyme/~/jsdom/~/acorn/dist/acorn.js 1:478-485
WARNING in ./~/enzyme/~/jsdom/~/acorn/dist/walk.js
Critical dependencies:
1:503-510 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
@ ./~/enzyme/~/jsdom/~/acorn/dist/walk.js 1:503-510
ERROR in ./~/enzyme/build/react-compat.js
Module not found: Error: Cannot resolve module 'react/lib/ExecutionEnvironment' in /Users/agoggin/www/vhosts/hammerhead/node_modules/enzyme/build
@ ./~/enzyme/build/react-compat.js 22:2-43
ERROR in ./~/enzyme/~/cheerio/index.js
Module not found: Error: Cannot resolve module 'json' in /Users/agoggin/www/vhosts/hammerhead/node_modules/enzyme/node_modules/cheerio
@ ./~/enzyme/~/cheerio/index.js 11:18-38
ERROR in ./~/enzyme/~/jsdom/lib/jsdom.js
Module not found: Error: Cannot resolve module 'fs' in /Users/agoggin/www/vhosts/hammerhead/node_modules/enzyme/node_modules/jsdom/lib
@ ./~/enzyme/~/jsdom/lib/jsdom.js 6:9-22
My webpack/karma config is pretty vanilla, but it'd help I can post that, too.
I'm aware of #27, mine happens on React v0.14 and it crushes my soul as well 😞
As far as I've tested (AFAIT?), this only happens when the state alters markup:
import React from 'react';
import { mount, describeWithDOM } from 'enzyme';
const Component = React.createClass({
getInitialState() {
return { flag: false };
},
render() {
return this.state.flag ? <a /> : <div />
}
});
describeWithDOM('<Component />', () => {
it('turns on the flag', () => {
const wrapper = mount(<Component />);
wrapper.setState({ flag: true });
});
});
Error: Invariant Violation: dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use ReactDOMServer.renderToString() for server rendering.
This does not happen if both elements are the same (e.g. this.state.flag ? <div /> : <div />
).
Jest seemed to have solved this issue, it doesn't happen there, so it might be worth looking how they did it. I can take a look later, but I was wondering if you knew what's causing this.
This SO answer might help, though I'm not sure if it's related.
I have a component in my app that has a router passed to it via context:
const contextTypes = { router: React.PropTypes.func.isRequired, };
Reagent tests currently do not support passing in a context and will throw this error:
Error: expected zero console.warn calls, got 1:
warn(Warning: Failed Context Types: Required context `router` was not specified in `MyComponent`.) at warning (node_modules/react/lib/warning.js:48:15)
at Context.<anonymous> (spec/javascripts/_failTestsOnErrors.js:21:23)
It would be great to be able to pass context into shallow
.
setProps(newProps)
on ReactWrapper replaces all props with a passed new one.
setProps(newProps)
on ShallowWrapper merges it with exsisting.
I think that ReactWrapper should merge props as well because it is more useful and similar how React's setState
works.
There's an example of simulating click events using the shallow renderer:
it('simulates click events', () => {
const onButtonClick = sinon.spy();
const wrapper = shallow(
<Foo onButtonClick={onButtonClick} />
);
wrapper.find('button').click();
expect(onButtonClick.calledOnce).to.be.true;
});
But when I try to use it, I get this:
$.find('.' + helloClass).click();
^
TypeError: $.find(...).click is not a function
I didn't search meticulously, but I don't see a .click()
method in the source... where is it?
given a simple test case
import {mount} from 'enzyme';
it('should update content', () => {
function Foo({foo, id}) {
const content = foo ? <span className="message">{id}</span> : id;
return <div>{content}</div>;
}
const wrapper = mount(<Foo id="1" />);
wrapper.setProps({ id: '1', foo: true});
expect(wrapper.find('.message').length).toBe(1);
});
fails on the latest assertion with the error
Error: Invariant Violation: findComponentRoot(..., .7ng446bthc.0): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a <tbody> when using tables, nesting tags like <form>, <p>, or <a>, or using non-SVG elements in an <svg> parent. Try inspecting the child nodes of the element with React ID ``.
at invariant (../project/node_modules/fbjs/lib/invariant.js:39:15)
at Object.ReactMount.findComponentRoot (../project/node_modules/react/lib/ReactMount.js:764:54)
at Object.ReactMount.findReactNodeByID (../project/node_modules/react/lib/ReactMount.js:679:23)
at Object.getNode (../project/node_modules/react/lib/ReactMount.js:162:32)
at ReactDOMComponent.Mixin.getPublicInstance (../project/node_modules/react/lib/ReactDOMComponent.js:925:29)
at findAllInRenderedTreeInternal (../project/node_modules/enzyme/build/MountedTraversal.js:233:27)
at findAllInRenderedTreeInternal (../project/node_modules/enzyme/build/MountedTraversal.js:246:26)
at findAllInRenderedTreeInternal (../project/node_modules/enzyme/build/MountedTraversal.js:249:24)
at treeFilter (../project/node_modules/enzyme/build/MountedTraversal.js:261:10)
at ReactWrapper.<anonymous> (../project/node_modules/enzyme/build/ReactWrapper.js:37:45)
It happened when I conditionally render some elements. With shallow render it works fine.
I have a component that looks like this:
class MySharona extends React.Component {
constructor(props) {
super(props);
this.state = {mounted: false};
}
componentDidMount() {
this.setState({mounted: true});
}
render() {
return <div>{this.state.mounted}</div>;
}
}
I've got a test that looks like this:
import { expect } from 'chai';
import { mount, describeWithDom } from 'reagent';
import React from 'react';
import MySharona from './MySharona.js';
describeWithDom('MySharona', () => {
it('passes my test', () => {
const wrapper = mount(<MySharona />);
expect(wrapper.find('div').text).to.be('true');
});
});
Instead of getting a passing test, I get a horrible, horrible error, that crushes my soul and makes me sad:
Error: Invariant Violation: dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure
windowand
documentare available globally before requiring React when unit testing or use React.renderToString for server rendering.
For parity with jQuery
As it follows from the docs
Reagent is unopinionated regarding which test runner or assertion library you use, and should be compatible with all major test runners and assertion libraries out there.
I tried to use it with Jasmine and it worked fine until I tried to use mount
. I got the error.
TypeError: before is not a function
before
hook is the Mocha-specific thing, Jasmine has the similar thing named beforeAll
.
Thanks for the awesome tool! It is really missing piece to my tests, but I can't use it due to lack of Jasmine support.
This would be a breaking change, but I think it provides a much cleaner experience.
For example:
wrapper.find('.foo .bar')
@hkjels, @Kauko and others have raised concern in #43 regarding the name conflict of "reagent" with the ClojureScript library.
This new issue is meant for discussion regarding new name ideas ONLY. If you have greater comments to add to the overall discussion/issue of renaming the library, please do so in the aforementioned thread.
Please do NOT suggest the name unless it has its corresponding package name available in npm.
Please do NOT comment with "+1" comments
http://airbnb.io/enzyme/docs/api/ShallowWrapper/simulate.html
I'm trying to get a grip on how to test my React components. Specifically, I feel like a proper test would be to assert some state, call a function that calls setState
, and re-assert the state. The below example uses an inline function, which confuses me more.. is there no such possibility to directly call the function without "simulating" an event? Furthermore, the simulate example in the docs doesn't seem to make sense:
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
const { count } = this.state;
return (
<div>
<div className={`clicks-${count}`}>
{count} clicks
</div>
<a onClick={() => this.setState({ count: count + 1 })}>
Increment
</a>
</div>
);
}
}
const wrapper = shallow(<Foo />);
expect(wrapper.find('.clicks-0').length).to.equal(1);
wrapper.simulate('click');
expect(wrapper.find('.clicks-1').length).to.equal(1);
I don't understand simulate('click')
here.. doesn't it need to find the anchor tag first to click it?
$.find(`.${ props.helloClass }`).first().html()
/Users/eric/Dropbox/dev/react-pure-component-starter/node_modules/fbjs/lib/invariant.js:45
throw error;
^
Error: Invariant Violation: renderToStaticMarkup(): You must pass a valid ReactElement.
Ok... maybe it didn't find anything?
$.find(`.${ props.helloClass }`).first().length // 1
Looks like it found something. How do I see what it found without a working .html()
?
I'd love to step through and find out, but I'm struggling to get iron-node or node-inspector to work on the tests. Wish I had more time to dig in.
Any hints?
Hello guys.
Thank you so much, reagent is exactly the lib I was waiting for!
I noticed that using mount()
does not work on functional stateless components.
There is a workaround: wrapping the function component with:
import React from 'react';
// Utility to wrap stateless component and allow functional stateless components
// to be tested using reagent
export var StatelessWrapper = Wrapped => class extends React.Component {
render() {
return <Wrapped {...this.props} />;
}
};
I would be nice if the lib did that automatically.
After running some tests enzyme rejects to run dom tests with the error message:
"before all" hook: mocha-jsdom: already a browser environment, or mocha-jsdom invoked twice. use 'skipWindowCheck' to disable this check.
This would allow one to selectively render deeper down the react tree when shallow rendering. Could be useful in some scenarios.
I would like to add to the docs a good way to test for null
, but I'm not sure which one is it.
This is the component:
import React, { PropTypes } from 'react';
const Component = (props) => {
return props.returnNull ? null : <div />;
};
Component.propTypes = {
returnNull: PropTypes.bool
};
export default Component;
I want to test if setting the flag to true
will return null
:
import React from 'react';
import { shallow } from 'reagent';
import { expect } from 'chai';
import Component from './Component';
describe('<Component />', () => {
it('returns null if the flag is on', () => {
const wrapper = shallow(
<Component returnNull={true} />
);
expect(wrapper.text()).to.equal('');
});
});
What is the correct way to do it? Would it perhaps be better if wrapper.type()
returned null
instead of throwing?
Running
input.simulate('keyDown', { which: val });
Produces
TypeError: Cannot read property 'apply' of undefined
at ReactWrapper.<anonymous> (node_modules/enzyme/build/ReactWrapper.js:351:44)
at ReactWrapper.single (node_modules/enzyme/build/ReactWrapper.js:739:17)
at ReactWrapper.simulate (node_modules/enzyme/build/ReactWrapper.js:350:12)
Because there's an unchecked reference to an event type. Instead, enzyme should give an informative message.
Ideally enzyme would update to babel 6 so that it can be compatible with applications built in babel 6 land and their configuration files.
This issue is sort of a misnomer. It usually goes something like this:
Say you have some component:
class Foo extends React.Component {
render() {
return <div className="in-foo">{this.props.foo}</div>
}
}
const wrapper = shallow(<Foo foo="bar" />);
expect(wrapper.props()).to.eql({ className: 'in-foo', children: 'bar' });
expect(wrapper.props()).to.not.eql({ foo: 'bar' });
The trick is you are looking at the root node of the rendered result of <Foo />
, not the <Foo />
itself. As a result, you are getting back className
and children
rather than the props that were passed in.
This is the expected behavior and will not change. If you want to get the props you passed in:
expect(wrapper.instance().props).to.eql({ foo: 'bar' });
It seems that after using reagent, the React Test Utilities is not needed, is that right? What's the best testing frameworks stack when using reagent?
It might make sense to support both?
This can be confused with the ClojureScript view-layer over React called Reagent. It should be mentioned in the Readme.
To set the value of an input currently I have to do:
const form = mount(<MyComponent />);
const input = form.find('input').get(0);
input.value = 'Blah blah';
It would be really nice in my opinion if I could instead do something akin to jQuery:
form.find('input').text('Blah blah')
// or probably better
form.find('input').value('Blah blah')
What are your thoughts on that? I'd be happy to attempt to work on the PR :)
Hi. I've loaded jsdom like this:
jsdom.env({
html: '<html><body></body</html>',
done: () => runTests()
});
And just to be through I tried const document = jsdom.env... and const window = jsdom.env ...
The runTests method had this statement: mount();
But ever time I run it, I get the following error: "It looks like you called mount()
without a jsdom document being loaded. ' + 'Make sure to only use mount()
inside of a `describeWithDOM(...)"
Your documentation says I don't need to use describeWithDOM if I'm not using mocha.
This methods exists in the API but it is not documented.
Proposed API:
const shallowWrapper = shallow(<MyComponent />);
const renderWrapper = wrapper.find('.foo').render();
Was trying to test a simple piece of code:
const renderShallow = shallow(<div id='ttt' className='ttt'>hello</div>);
console.log(renderShallow.find('#ttt')); // not found
console.log(renderShallow.find('.ttt')); // found
and it returns an empty array when i tried to use an id selector. It works fine on class selectors.
In order for this to work with jest, jest needs to know about some libraries specifically not to be mocked. It'd be nice if you could document this somewhere. This is specifically what I had to do to get tests running:
/package.json
{
"jest": {
"unmockedModulePathPatterns": [
"enzyme",
"cheerio",
"htmlparser2",
"sinon",
"..."
]
}
}
This is an exhaustive list of needed items
dep of cheerio, needed for render method
dep of htmlparser2, from cheerio
The goal of this is to
let spy;
describe('MyComponent', () => {
beforeEach() {
spy = reagent.spyLifecycle(sinon, MyComponent);
},
it('should do things', () => {
const my = mount(<MyComponent />);
my.setProps({ hello: 'world' });
assert.isTrue(spy.get('componentWillReceiveProps').calledOnce, 'receive props was called');
// or if we don't want to be stringly typed...
assert.isTrue(my.componentWillReceiveProps.calledOnce);
});
afterEach() {
spy.destroy();
}
});
Pros: It's more explicit what's happening.
Cons: You now have to do the beforeEach/afterEach setup yourself.
Ideally we'd merge spyLifecycle and spyMethods so it just spies every method.
I'm just curious as to why jsdom
is required for the full rendering. I understand the need for a DOM, but what about in environments in which a DOM already exists? In our current architecture, we're running our tests directly in the browser via Karma.
I do see that jsdom
is an optional dependency, and that its require statement is wrapped in a try/catch. Does this mean that it would still work if you're using some alternative DOM? If so, then just the docs are misleading.
I'm trying to make sure that a select box is rendered with the proper value selected. I don't see any hooks into value
in the docs for shallow
or render
. Is there an obvious thing I'm missing?
I'm writing a test that checks that a component does not include a <button>
when a certain prop isnt passed.
I'm using the shallow renderer as two of the examples on this section seems to do exactly what I intend to do.
const wrapper = shallow(<MyComponent />);
When I log the output html of the component through wrapper.html()
I see there's no <button>
in there. In fact I can see the <noscript/>
's created by my conditional rendering component.
However, wrapper.find('button')
or wrapper.find('.btn')
(as it has this class) both return an object with length equals 1.
wrapper.contanis('button')
is also true.
What could I be doing wrong, have I misunderstood something?
What is the best way to assert the presence of a node in the rendered output?
I could of course assert on the contents of html()
but that feels a bit brittle.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.