From 6b9e63cc858a567880cf39103416d289d8c8b7ec Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Sun, 13 Nov 2016 10:40:57 +0000 Subject: [PATCH 01/60] more tests for lifecycleExperimental - test that componentWillReceiveProps is called in the right order when render is aborted in shouldComponentUpdate - test that a 2nd re-render lifecycle works the same as the 1st one --- test/ShallowWrapper-spec.jsx | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/test/ShallowWrapper-spec.jsx b/test/ShallowWrapper-spec.jsx index 86f065c3a..44a6bc4b5 100644 --- a/test/ShallowWrapper-spec.jsx +++ b/test/ShallowWrapper-spec.jsx @@ -2720,6 +2720,7 @@ describe('shallow', () => { }, ); wrapper.setProps({ foo: 'baz' }); + wrapper.setProps({ foo: 'bax' }); expect(spy.args).to.deep.equal( [ [ @@ -2751,6 +2752,32 @@ describe('shallow', () => { { foo: 'state' }, { foo: 'state' }, { foo: 'context' }, ], + [ + 'componentWillReceiveProps', + { foo: 'baz' }, { foo: 'bax' }, + { foo: 'context' }, + ], + [ + 'shouldComponentUpdate', + { foo: 'baz' }, { foo: 'bax' }, + { foo: 'state' }, { foo: 'state' }, + { foo: 'context' }, + ], + [ + 'componentWillUpdate', + { foo: 'baz' }, { foo: 'bax' }, + { foo: 'state' }, { foo: 'state' }, + { foo: 'context' }, + ], + [ + 'render', + ], + [ + 'componentDidUpdate', + { foo: 'baz' }, { foo: 'bax' }, + { foo: 'state' }, { foo: 'state' }, + { foo: 'context' }, + ], ], ); }); @@ -2759,6 +2786,9 @@ describe('shallow', () => { const spy = sinon.spy(); class Foo extends React.Component { + componentWillReceiveProps() { + spy('componentWillReceiveProps'); + } shouldComponentUpdate() { spy('shouldComponentUpdate'); return false; @@ -2777,7 +2807,14 @@ describe('shallow', () => { const wrapper = shallow(, { lifecycleExperimental: true }); wrapper.setProps({ foo: 'baz' }); - expect(spy.args).to.deep.equal([['render'], ['shouldComponentUpdate']]); + wrapper.setProps({ foo: 'bax' }); + expect(spy.args).to.deep.equal([ + ['render'], + ['componentWillReceiveProps'], + ['shouldComponentUpdate'], + ['componentWillReceiveProps'], + ['shouldComponentUpdate'], + ]); }); it('should not provoke another renders to call setState in componentWillReceiveProps', () => { From 5cece32a721f28f19cccf0f92a77d0db2cd03816 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 16 Nov 2016 17:27:29 -0800 Subject: [PATCH 02/60] =?UTF-8?q?[Tests]=20ensure=20we=E2=80=99re=20using?= =?UTF-8?q?=20the=20system=20`npm`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `gitbook-cli` includes `npm` for some crazy reason, that means gitbook’s `npm` dominates the global one. If we don’t remove the binary, then `npm install` will fail with an “internal/fs” error. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4718b042a..2c90de5e2 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "test:env": "sh ./example-test.sh", "test:all": "npm run react:13 && npm run test:only && npm run react:14 && npm run test:only && npm run react:15 && npm run test:only", "react:clean": "rimraf node_modules/react node_modules/react-dom node_modules/react-addons-test-utils", - "react:13": "npm run react:clean && npm i react@0.13", - "react:14": "npm run react:clean && npm i react@0.14 react-dom@0.14 react-addons-test-utils@0.14", - "react:15": "npm run react:clean && npm i react@15 react-dom@15 react-addons-test-utils@15", + "react:13": "rimraf node_modules/.bin/npm && npm run react:clean && npm i react@0.13 && npm install", + "react:14": "rimraf node_modules/.bin/npm && npm run react:clean && npm i react@0.14 react-dom@0.14 react-addons-test-utils@0.14 && npm install", + "react:15": "rimraf node_modules/.bin/npm && npm run react:clean && npm i react@15 react-dom@15 react-addons-test-utils@15 && npm install", "docs:clean": "rimraf _book", "docs:prepare": "gitbook install", "docs:build": "npm run docs:prepare && gitbook build", From 200055e7b7626dd0df52887f46bf78f6e1d751a6 Mon Sep 17 00:00:00 2001 From: JonasMS Date: Thu, 17 Nov 2016 12:08:17 -0800 Subject: [PATCH 03/60] Update FindWhere.md typeof n.type() !== 'string' fails while n.type() !== 'string' works --- docs/api/ShallowWrapper/findWhere.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ShallowWrapper/findWhere.md b/docs/api/ShallowWrapper/findWhere.md index fc24afbe3..f6154845b 100644 --- a/docs/api/ShallowWrapper/findWhere.md +++ b/docs/api/ShallowWrapper/findWhere.md @@ -20,7 +20,7 @@ nodes. ```jsx const wrapper = shallow(); -const complexComponents = wrapper.findWhere(n => typeof n.type() !== 'string'); +const complexComponents = wrapper.findWhere(n => n.type() !== 'string'); expect(complexComponents).to.have.length(8); ``` From 7beab7b5969de4b1a5b32214d74e593b72670e2f Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 16 Nov 2016 17:29:04 -0800 Subject: [PATCH 04/60] [Dev Deps] update `coveralls`, `eslint`, `eslint-plugin-react` --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2c90de5e2..83dd74c6a 100644 --- a/package.json +++ b/package.json @@ -71,17 +71,17 @@ "babel-preset-airbnb": "^2.1.1", "babel-register": "^6.18.0", "chai": "^3.5.0", - "coveralls": "^2.11.14", + "coveralls": "^2.11.15", "enzyme-example-jest": "^0.1.0", "enzyme-example-karma": "^0.1.1", "enzyme-example-karma-webpack": "^0.1.4", "enzyme-example-mocha": "^0.1.0", "enzyme-example-react-native": "^0.1.0", - "eslint": "^3.9.1", + "eslint": "^3.10.2", "eslint-config-airbnb": "^13.0.0", "eslint-plugin-import": "^2.2.0", "eslint-plugin-jsx-a11y": "^2.2.3", - "eslint-plugin-react": "^6.6.0", + "eslint-plugin-react": "^6.7.1", "gitbook-cli": "^1.0.1", "istanbul": "^1.0.0-alpha.2", "jsdom": "^6.1.0", From 2185a4f87d4d4f2d3eb70576d6a285aa7b978f5b Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 16 Nov 2016 17:33:38 -0800 Subject: [PATCH 05/60] [Deps] update `lodash` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83dd74c6a..0ddea8be8 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "function.prototype.name": "^1.0.0", "in-publish": "^2.0.0", "is-subset": "^0.1.1", - "lodash": "^4.16.4", + "lodash": "^4.17.2", "object-is": "^1.0.1", "object.assign": "^4.0.4", "object.entries": "^1.0.3", From aaeedcf9fac74cd2e6d7a3565431c7a086a63fda Mon Sep 17 00:00:00 2001 From: indexzero Date: Wed, 16 Nov 2016 23:28:47 -0800 Subject: [PATCH 06/60] [api] Display a useful error when `withDom` fails to find "jsdom". --- withDom.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/withDom.js b/withDom.js index 383b3e914..215d39c8f 100644 --- a/withDom.js +++ b/withDom.js @@ -15,5 +15,12 @@ if (!global.document) { }; } catch (e) { // jsdom is not supported... + if (e.message === "Cannot find module 'jsdom'") { + console.error('[enzyme/withDom] Error: missing required module "jsdom"'); + console.error('[enzyme/withDom] To fix this you must run:'); + console.error('[enzyme/withDom] npm install jsdom --save-dev'); + } else { + console.error('[enzyme withDom] ' + (e.stack || e.message)); + } } } From 08f2f77c61092a56c4c17d19e4cb36669e29aaa6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 18 Nov 2016 20:40:44 -0800 Subject: [PATCH 07/60] [Refactor] `MountedTraversal`: use `Object.values` instead of `Object.keys` --- src/MountedTraversal.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 6723fc229..711c3835c 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -1,4 +1,5 @@ import isEmpty from 'lodash/isEmpty'; +import values from 'object.values'; import isSubset from 'is-subset'; import { internalInstance, @@ -116,16 +117,16 @@ export function childrenOfInstInternal(inst) { const currentElement = inst._currentElement; if (isDOMComponent(publicInst)) { const renderedChildren = renderedChildrenOfInst(inst); - return Object.keys(renderedChildren || {}).filter((key) => { - if (REACT013 && !renderedChildren[key].getPublicInstance) { + return values(renderedChildren || {}).filter((node) => { + if (REACT013 && !node.getPublicInstance) { return false; } return true; - }).map((key) => { - if (!REACT013 && typeof renderedChildren[key]._currentElement.type === 'function') { - return renderedChildren[key]._instance; + }).map((node) => { + if (!REACT013 && typeof node._currentElement.type === 'function') { + return node._instance; } - return renderedChildren[key].getPublicInstance(); + return node.getPublicInstance(); }); } else if ( !REACT013 && @@ -246,14 +247,14 @@ function findAllInRenderedTreeInternal(inst, test) { const currentElement = inst._currentElement; if (isDOMComponent(publicInst)) { const renderedChildren = renderedChildrenOfInst(inst); - Object.keys(renderedChildren || {}).filter((key) => { - if (REACT013 && !renderedChildren[key].getPublicInstance) { + values(renderedChildren || {}).filter((node) => { + if (REACT013 && !node.getPublicInstance) { return false; } return true; - }).forEach((key) => { + }).forEach((node) => { ret = ret.concat( - findAllInRenderedTreeInternal(renderedChildren[key], test), + findAllInRenderedTreeInternal(node, test), ); }); } else if ( From 8db9f23263be67a4ae24232f930eef0ca8a7d2f6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 17 Nov 2016 13:57:47 -0800 Subject: [PATCH 08/60] =?UTF-8?q?[Fix]=20`mount`:=20ensure=20that=20`react?= =?UTF-8?q?-text`=20comment=20nodes=20don=E2=80=99t=20break=20`.find`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add `shallow` test just in case - use `values` over `Object.keys` in MountedTraversal Fixes #689. --- src/MountedTraversal.js | 7 ++++--- test/ReactWrapper-spec.jsx | 18 ++++++++++++++++++ test/ShallowWrapper-spec.jsx | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 711c3835c..e458f60e9 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -126,6 +126,9 @@ export function childrenOfInstInternal(inst) { if (!REACT013 && typeof node._currentElement.type === 'function') { return node._instance; } + if (typeof node._stringText === 'string') { + return node; + } return node.getPublicInstance(); }); } else if ( @@ -253,9 +256,7 @@ function findAllInRenderedTreeInternal(inst, test) { } return true; }).forEach((node) => { - ret = ret.concat( - findAllInRenderedTreeInternal(node, test), - ); + ret = ret.concat(findAllInRenderedTreeInternal(node, test)); }); } else if ( !REACT013 && diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 45c8f3460..c45bf6991 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -378,7 +378,25 @@ describeWithDOM('mount', () => { expect(wrapper.find('span[htmlFor="foo"]')).to.have.length(1); expect(wrapper.find('span[htmlFor]')).to.have.length(1); + }); + it('works with an adjacent sibling selector', () => { + const a = 'some'; + const b = 'text'; + const wrapper = mount( +
+
+ {a} + {b} +
+
+ {a} + {b} +
+
, + ); + expect(wrapper.find('.row')).to.have.lengthOf(2); + expect(wrapper.find('.row + .row')).to.have.lengthOf(1); }); // React 15.2 warns when setting a non valid prop to an DOM element diff --git a/test/ShallowWrapper-spec.jsx b/test/ShallowWrapper-spec.jsx index 44a6bc4b5..abdb8f486 100644 --- a/test/ShallowWrapper-spec.jsx +++ b/test/ShallowWrapper-spec.jsx @@ -483,6 +483,25 @@ describe('shallow', () => { expect(wrapper.find('[title]')).to.have.length(1); }); + it('works with an adjacent sibling selector', () => { + const a = 'some'; + const b = 'text'; + const wrapper = shallow( +
+
+ {a} + {b} +
+
+ {a} + {b} +
+
, + ); + expect(wrapper.find('.row')).to.have.lengthOf(2); + expect(wrapper.find('.row + .row')).to.have.lengthOf(1); + }); + it('should error sensibly if prop selector without quotes', () => { const wrapper = shallow(
From fd2939b882b02422a342aaefc5bfbedef9400672 Mon Sep 17 00:00:00 2001 From: Christian Schulze Date: Sun, 13 Nov 2016 15:35:01 +1100 Subject: [PATCH 09/60] implement ReactWrapper#getDOMNode --- docs/api/ReactWrapper/getDOMNode.md | 21 ++++++++++++++++ docs/api/mount.md | 3 +++ src/ReactWrapper.jsx | 18 +++++++++++++ test/ReactWrapper-spec.jsx | 39 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 docs/api/ReactWrapper/getDOMNode.md diff --git a/docs/api/ReactWrapper/getDOMNode.md b/docs/api/ReactWrapper/getDOMNode.md new file mode 100644 index 000000000..3b56f007f --- /dev/null +++ b/docs/api/ReactWrapper/getDOMNode.md @@ -0,0 +1,21 @@ +# `.getDOMNode() => DOMComponent` + +Returns the outer most DOMComponent of the current wrapper. + +Notes: +- can only be called on a wrapper of a single node. +- will raise if called on a wrapper of a stateless functional component. + + +#### Returns + +`DOMComponent`: The retrieved DOM component. + + + +#### Examples + +```jsx +const wrapper = mount(); +expect(wrapper.getDOMNode()).to.have.property("className"); +``` diff --git a/docs/api/mount.md b/docs/api/mount.md index afcc8c9b6..c2ff8cf00 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -122,6 +122,9 @@ Returns a static HTML rendering of the current node. #### [`.get(index) => ReactElement`](ReactWrapper/get.md) Returns the node at the provided index of the current wrapper. +#### [`.getDOMNode() => DOMComponent`](ReactWrapper/getDOMNode.md) +Returns the outer most DOMComponent of the current wrapper. + #### [`.at(index) => ReactWrapper`](ReactWrapper/at.md) Returns a wrapper of the node at the provided index of the current wrapper. diff --git a/src/ReactWrapper.jsx b/src/ReactWrapper.jsx index f47323d29..75bb8abe0 100644 --- a/src/ReactWrapper.jsx +++ b/src/ReactWrapper.jsx @@ -29,6 +29,7 @@ import { typeOfNode, displayNameOfNode, ITERATOR_SYMBOL, + isFunctionalComponent, } from './Utils'; import { debugInsts, @@ -130,6 +131,23 @@ class ReactWrapper { return this.nodes; } + /** + * Returns the outer most DOMComponent of the current wrapper. + * + * NOTE: can only be called on a wrapper of a single node. + * + * @returns {DOMComponent} + */ + getDOMNode() { + return this.single('getDOMNode', (n) => { + if (isFunctionalComponent(n)) { + throw new TypeError('Method “getDOMNode” cannot be used on functional components.'); + } + + return findDOMNode(n); + }); + } + /** * If the root component contained a ref, you can access it here * and get a wrapper around it. diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index c45bf6991..dd67a9503 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -3285,6 +3285,45 @@ describeWithDOM('mount', () => { }); }); + describe('.getDOMNode', () => { + class Test extends React.Component { + render() { + return ( +
+
+ + +
+
+ ); + } + } + + it('should return the outer most DOMComponent of the root wrapper', () => { + const wrapper = mount(); + expect(wrapper.getDOMNode()).to.have.property('className', 'outer'); + }); + + it('should return the outer most DOMComponent of the inner div wrapper', () => { + const wrapper = mount(); + expect(wrapper.find('.inner').getDOMNode()).to.have.property('className', 'inner'); + }); + + it('should throw when wrapping multiple elements', () => { + const wrapper = mount().find('span'); + expect(() => wrapper.getDOMNode()).to.throw(Error); + }); + + describeIf(!REACT013, 'stateless components', () => { + const SFC = () => (
); + + it('should throw when wrapping an SFC', () => { + const wrapper = mount(); + expect(() => wrapper.getDOMNode()).to.throw(TypeError, 'Method “getDOMNode” cannot be used on functional components.'); + }); + }); + }); + describe('#single()', () => { it('throws if run on multiple nodes', () => { const wrapper = mount(
).children(); From 01f415cc0c6dda4e00beec26969cad37b6ca0b9e Mon Sep 17 00:00:00 2001 From: ian lyons Date: Sun, 27 Nov 2016 16:55:19 -0800 Subject: [PATCH 10/60] Correct README title Correct README title typo to make it clear that the test below it is for Jest version 15+. --- docs/guides/jest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/jest.md b/docs/guides/jest.md index 8bd205e24..ce1ae2665 100644 --- a/docs/guides/jest.md +++ b/docs/guides/jest.md @@ -15,7 +15,7 @@ import Foo from '../Foo'; You do **not** need to include Jest's own renderer, unless you want to use it _only_ for Jest snapshot testing. -## Example Project for Jest prior to version 15 +## Example Project for Jest version 15+ - [Example test for Jest 15+](https://github.com/vjwilson/enzyme-example-jest) From e19d0a6e7b87e4244f0c6d08f916f6f0ff2016bc Mon Sep 17 00:00:00 2001 From: Boris Serdiuk Date: Thu, 1 Dec 2016 18:24:29 +0100 Subject: [PATCH 11/60] [Tests] `mount`/`shallow`: add test case for broken `.parents()` call --- test/ReactWrapper-spec.jsx | 5 ++++- test/ShallowWrapper-spec.jsx | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index dd67a9503..4c7a8b64a 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -1950,7 +1950,7 @@ describeWithDOM('mount', () => { }); describe('.parents([selector])', () => { - it('should return an array of current nodes ancestors', () => { + it('should return an array of current node’s ancestors', () => { const wrapper = mount(
@@ -1958,6 +1958,9 @@ describeWithDOM('mount', () => {
+
+
+
, ); diff --git a/test/ShallowWrapper-spec.jsx b/test/ShallowWrapper-spec.jsx index abdb8f486..750c93cda 100644 --- a/test/ShallowWrapper-spec.jsx +++ b/test/ShallowWrapper-spec.jsx @@ -1785,7 +1785,7 @@ describe('shallow', () => { }); describe('.parents([selector])', () => { - it('should return an array of current nodes ancestors', () => { + it('should return an array of current node’s ancestors', () => { const wrapper = shallow(
@@ -1793,6 +1793,9 @@ describe('shallow', () => {
+
+
+
, ); @@ -1802,7 +1805,6 @@ describe('shallow', () => { expect(parents.at(0).hasClass('bar')).to.equal(true); expect(parents.at(1).hasClass('foo')).to.equal(true); expect(parents.at(2).hasClass('bax')).to.equal(true); - }); it('should work for non-leaf nodes as well', () => { From 16e2a2e14c1f0862e521dc5b8c6c7798360826ff Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 1 Dec 2016 20:12:46 -0800 Subject: [PATCH 12/60] [Fix] `mount`: `.parents()` now filters out sibling path trees. --- src/MountedTraversal.js | 120 +++++++++++++++++++++------------------- src/ReactWrapper.jsx | 2 +- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index e458f60e9..88a88fd82 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -161,15 +161,77 @@ export function childrenOfInst(node) { return childrenOfInstInternal(internalInstanceOrComponent(node)); } +// This function should be called with an "internal instance". Nevertheless, if it is +// called with a "public instance" instead, the function will call itself with the +// internal instance and return the proper result. +function findAllInRenderedTreeInternal(inst, test) { + if (!inst) { + return []; + } + + if (!inst.getPublicInstance) { + const internal = internalInstance(inst); + return findAllInRenderedTreeInternal(internal, test); + } + const publicInst = inst.getPublicInstance() || inst._instance; + let ret = test(publicInst) ? [publicInst] : []; + const currentElement = inst._currentElement; + if (isDOMComponent(publicInst)) { + const renderedChildren = renderedChildrenOfInst(inst); + values(renderedChildren || {}).filter((node) => { + if (REACT013 && !node.getPublicInstance) { + return false; + } + return true; + }).forEach((node) => { + ret = ret.concat(findAllInRenderedTreeInternal(node, test)); + }); + } else if ( + !REACT013 && + isElement(currentElement) && + typeof currentElement.type === 'function' + ) { + ret = ret.concat( + findAllInRenderedTreeInternal( + inst._renderedComponent, + test, + ), + ); + } else if ( + REACT013 && + isCompositeComponent(publicInst) + ) { + ret = ret.concat( + findAllInRenderedTreeInternal( + inst._renderedComponent, + test, + ), + ); + } + return ret; +} + +// This function could be called with a number of different things technically, so we need to +// pass the *right* thing to our internal helper. +export function treeFilter(node, test) { + return findAllInRenderedTreeInternal(internalInstanceOrComponent(node), test); +} + +function pathFilter(path, fn) { + return path.filter(tree => treeFilter(tree, fn).length !== 0); +} + export function pathToNode(node, root) { const queue = [root]; const path = []; + const hasNode = testNode => node === testNode; + while (queue.length) { const current = queue.pop(); const children = childrenOfInst(current); - if (current === node) return path; + if (current === node) return pathFilter(path, hasNode); path.push(current); @@ -232,59 +294,3 @@ export function buildInstPredicate(selector) { throw new TypeError('Enzyme::Selector expects a string, object, or Component Constructor'); } } - -// This function should be called with an "internal instance". Nevertheless, if it is -// called with a "public instance" instead, the function will call itself with the -// internal instance and return the proper result. -function findAllInRenderedTreeInternal(inst, test) { - if (!inst) { - return []; - } - - if (!inst.getPublicInstance) { - const internal = internalInstance(inst); - return findAllInRenderedTreeInternal(internal, test); - } - const publicInst = inst.getPublicInstance() || inst._instance; - let ret = test(publicInst) ? [publicInst] : []; - const currentElement = inst._currentElement; - if (isDOMComponent(publicInst)) { - const renderedChildren = renderedChildrenOfInst(inst); - values(renderedChildren || {}).filter((node) => { - if (REACT013 && !node.getPublicInstance) { - return false; - } - return true; - }).forEach((node) => { - ret = ret.concat(findAllInRenderedTreeInternal(node, test)); - }); - } else if ( - !REACT013 && - isElement(currentElement) && - typeof currentElement.type === 'function' - ) { - ret = ret.concat( - findAllInRenderedTreeInternal( - inst._renderedComponent, - test, - ), - ); - } else if ( - REACT013 && - isCompositeComponent(publicInst) - ) { - ret = ret.concat( - findAllInRenderedTreeInternal( - inst._renderedComponent, - test, - ), - ); - } - return ret; -} - -// This function could be called with a number of different things technically, so we need to -// pass the *right* thing to our internal helper. -export function treeFilter(node, test) { - return findAllInRenderedTreeInternal(internalInstanceOrComponent(node), test); -} diff --git a/src/ReactWrapper.jsx b/src/ReactWrapper.jsx index 75bb8abe0..f73f5dbbb 100644 --- a/src/ReactWrapper.jsx +++ b/src/ReactWrapper.jsx @@ -627,7 +627,7 @@ class ReactWrapper { */ parents(selector) { const allParents = this.wrap( - this.single('parents', n => parentsOfInst(n, this.root.getNode())), + this.single('parents', n => parentsOfInst(n, this.root.getNode())), ); return selector ? allParents.filter(selector) : allParents; } From 2a85a5fe7d054f0d95c13fab54b975943acb19df Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Tue, 6 Dec 2016 05:06:51 +0000 Subject: [PATCH 13/60] Update webpack usage with Webpack 2 (#717) Webpack 2 changes what's allowed in the externals part of the config, so I updated the docs to have Webpack 1 and Webpack 2 examples. --- docs/guides/webpack.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/guides/webpack.md b/docs/guides/webpack.md index bb821de77..4ffeb1a34 100644 --- a/docs/guides/webpack.md +++ b/docs/guides/webpack.md @@ -20,7 +20,9 @@ react/lib/ReactContext react/lib/ExecutionEnvironment ``` -Here is an example piece of configuration code marking these as external: +Depending on if you are using Webpack 1 or Webpack 2 you will need different configurations. + +### Webpack 1 ```js /* webpack.config.js */ @@ -34,6 +36,18 @@ externals: { // ... ``` +### Webpack 2 + +```js +externals: { + 'cheerio': 'window', + 'react/addons': 'react', + 'react/lib/ExecutionEnvironment': 'react', + 'react/lib/ReactContext': 'react', +}, +``` + + ## React 0.14 Compatibility If you are using React 0.14, the instructions above will be the same but with a different list of From 2b219bf9052a8d75dc002093cd51f6341fa9e0b6 Mon Sep 17 00:00:00 2001 From: Chris Temple Date: Tue, 13 Dec 2016 19:01:43 +0000 Subject: [PATCH 14/60] Deprecate isEmpty() for exists() and update docs (#722) --- book.json | 2 +- docs/README.md | 2 ++ docs/api/ReactWrapper/exists.md | 18 +++++++++++++ docs/api/ReactWrapper/isEmpty.md | 1 + docs/api/ShallowWrapper/exists.md | 18 +++++++++++++ docs/api/ShallowWrapper/isEmpty.md | 1 + docs/api/mount.md | 5 +++- docs/api/shallow.md | 5 +++- src/ReactWrapper.jsx | 15 +++++++++-- src/ShallowWrapper.js | 15 +++++++++-- test/ReactWrapper-spec.jsx | 43 +++++++++++++++++++++++++++--- test/ShallowWrapper-spec.jsx | 41 +++++++++++++++++++++++++--- 12 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 docs/api/ReactWrapper/exists.md create mode 100644 docs/api/ShallowWrapper/exists.md diff --git a/book.json b/book.json index 72c20d2fe..72b1976ad 100644 --- a/book.json +++ b/book.json @@ -8,7 +8,7 @@ }, "plugins": [ "edit-link", - "prism", + "prism@2.0.0", "-highlight", "github", "-search", diff --git a/docs/README.md b/docs/README.md index f1f98981b..08b3979c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,6 +30,7 @@ * [equals(node)](/docs/api/ShallowWrapper/equals.md) * [every(selector)](/docs/api/ShallowWrapper/every.md) * [everyWhere(predicate)](/docs/api/ShallowWrapper/everyWhere.md) + * [exists()](/docs/api/ShallowWrapper/exists.md) * [filter(selector)](/docs/api/ShallowWrapper/filter.md) * [filterWhere(predicate)](/docs/api/ShallowWrapper/filterWhere.md) * [find(selector)](/docs/api/ShallowWrapper/find.md) @@ -82,6 +83,7 @@ * [debug()](/docs/api/ReactWrapper/debug.md) * [detach()](/docs/api/ReactWrapper/detach.md) * [every(selector)](/docs/api/ReactWrapper/every.md) + * [exists()](/docs/api/ReactWrapper/exists.md) * [everyWhere(predicate)](/docs/api/ReactWrapper/everyWhere.md) * [filter(selector)](/docs/api/ReactWrapper/filter.md) * [filterWhere(predicate)](/docs/api/ReactWrapper/filterWhere.md) diff --git a/docs/api/ReactWrapper/exists.md b/docs/api/ReactWrapper/exists.md new file mode 100644 index 000000000..d50a197c2 --- /dev/null +++ b/docs/api/ReactWrapper/exists.md @@ -0,0 +1,18 @@ +# `.exists() => Boolean` + +Returns whether or not the current node exists. + + +#### Returns + +`Boolean`: whether or not the current node exists. + + + +#### Example + + +```jsx +const wrapper = mount(
); +expect(wrapper.find('.other-class').exists()).to.be(false); +``` diff --git a/docs/api/ReactWrapper/isEmpty.md b/docs/api/ReactWrapper/isEmpty.md index b3a2b4857..88c025561 100644 --- a/docs/api/ReactWrapper/isEmpty.md +++ b/docs/api/ReactWrapper/isEmpty.md @@ -1,4 +1,5 @@ # `.isEmpty() => Boolean` +**Deprecated**: Use [.exists()](exists.md) instead. Returns whether or not the current node is empty. diff --git a/docs/api/ShallowWrapper/exists.md b/docs/api/ShallowWrapper/exists.md new file mode 100644 index 000000000..312d9139c --- /dev/null +++ b/docs/api/ShallowWrapper/exists.md @@ -0,0 +1,18 @@ +# `.exists() => Boolean` + +Returns whether or not the current node exists. + + +#### Returns + +`Boolean`: whether or not the current node exists. + + + +#### Example + + +```jsx +const wrapper = shallow(
); +expect(wrapper.find('.other-class').exists()).to.be(false); +``` diff --git a/docs/api/ShallowWrapper/isEmpty.md b/docs/api/ShallowWrapper/isEmpty.md index 8a4910e59..ffb6bb01f 100644 --- a/docs/api/ShallowWrapper/isEmpty.md +++ b/docs/api/ShallowWrapper/isEmpty.md @@ -1,4 +1,5 @@ # `.isEmpty() => Boolean` +**Deprecated**: Use [.exists()](exists.md) instead. Returns whether or not the current node is empty. diff --git a/docs/api/mount.md b/docs/api/mount.md index c2ff8cf00..507ebbd91 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -89,8 +89,11 @@ Returns whether or not the current root node has the given class name or not. #### [`.is(selector) => Boolean`](ReactWrapper/is.md) Returns whether or not the current node matches a provided selector. +#### [`.exists() => Boolean`](ReactWrapper/exists.md) +Returns whether or not the current node exists. + #### [`.isEmpty() => Boolean`](ReactWrapper/isEmpty.md) -Returns whether or not the current node is empty. +*Deprecated*: Use [.exists()](ReactWrapper/exists.md) instead. #### [`.not(selector) => ReactWrapper`](ReactWrapper/not.md) Remove nodes in the current wrapper that match the provided selector. (inverse of `.filter()`) diff --git a/docs/api/shallow.md b/docs/api/shallow.md index 666ecaacb..fb248a45b 100644 --- a/docs/api/shallow.md +++ b/docs/api/shallow.md @@ -91,8 +91,11 @@ Returns whether or not the current node has the given class name or not. #### [`.is(selector) => Boolean`](ShallowWrapper/is.md) Returns whether or not the current node matches a provided selector. +#### [`.exists() => Boolean`](ShallowWrapper/exists.md) +Returns whether or not the current node exists. + #### [`.isEmpty() => Boolean`](ShallowWrapper/isEmpty.md) -Returns whether or not the current node is empty. +*Deprecated*: Use [.exists()](ShallowWrapper/exists.md) instead. #### [`.not(selector) => ShallowWrapper`](ShallowWrapper/not.md) Remove nodes in the current wrapper that match the provided selector. (inverse of `.filter()`) diff --git a/src/ReactWrapper.jsx b/src/ReactWrapper.jsx index f73f5dbbb..21fa7e1a3 100644 --- a/src/ReactWrapper.jsx +++ b/src/ReactWrapper.jsx @@ -885,12 +885,23 @@ class ReactWrapper { } /** - * Returns true if the current wrapper has no nodes. False otherwise. + * Delegates to exists() * * @returns {boolean} */ isEmpty() { - return this.length === 0; + // eslint-disable-next-line no-console + console.warn('Enzyme::Deprecated method isEmpty() called, use exists() instead.'); + return !this.exists(); + } + + /** + * Returns true if the current wrapper has nodes. False otherwise. + * + * @returns {boolean} + */ + exists() { + return this.length > 0; } /** diff --git a/src/ShallowWrapper.js b/src/ShallowWrapper.js index 45adbc3f1..64894cef2 100644 --- a/src/ShallowWrapper.js +++ b/src/ShallowWrapper.js @@ -942,12 +942,23 @@ class ShallowWrapper { } /** - * Returns true if the current wrapper has no nodes. False otherwise. + * Delegates to exists() * * @returns {boolean} */ isEmpty() { - return this.length === 0; + // eslint-disable-next-line no-console + console.warn('Enzyme::Deprecated method isEmpty() called, use exists() instead.'); + return !this.exists(); + } + + /** + * Returns true if the current wrapper has nodes. False otherwise. + * + * @returns {boolean} + */ + exists() { + return this.length > 0; } /** diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 4c7a8b64a..cc9913250 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -2464,12 +2464,47 @@ describeWithDOM('mount', () => { }); describe('.isEmpty()', () => { - it('should return true iff wrapper is empty', () => { + let warningStub; + let fooNode; + let missingNode; + + beforeEach(() => { + warningStub = sinon.stub(console, 'warn'); + const wrapper = mount( +
, + ); + fooNode = wrapper.find('.foo'); + missingNode = wrapper.find('.missing'); + }); + afterEach(() => { + warningStub.restore(); + }); + + it('should display a deprecation warning', () => { + fooNode.isEmpty(); + expect(warningStub.calledWith('Enzyme::Deprecated method isEmpty() called, use exists() instead.')).to.equal(true); + }); + + it('calls exists() instead', () => { + const existsSpy = sinon.spy(); + fooNode.exists = existsSpy; + fooNode.isEmpty(); + expect(existsSpy.called).to.equal(true); + }); + + it('should return true if wrapper is empty', () => { + expect(fooNode.isEmpty()).to.equal(false); + expect(missingNode.isEmpty()).to.equal(true); + }); + }); + + describe('.exists()', () => { + it('should return true if node exists in wrapper', () => { const wrapper = mount(
, ); - expect(wrapper.find('.bar').isEmpty()).to.equal(true); - expect(wrapper.find('.foo').isEmpty()).to.equal(false); + expect(wrapper.find('.bar').exists()).to.equal(false); + expect(wrapper.find('.foo').exists()).to.equal(true); }); }); @@ -3215,7 +3250,7 @@ describeWithDOM('mount', () => { const ref = wrapper.ref('not-a-ref'); expect(ref.length).to.equal(0); - expect(ref.isEmpty()).to.equal(true); + expect(ref.exists()).to.equal(false); }); }); }); diff --git a/test/ShallowWrapper-spec.jsx b/test/ShallowWrapper-spec.jsx index 750c93cda..4ba0f8d06 100644 --- a/test/ShallowWrapper-spec.jsx +++ b/test/ShallowWrapper-spec.jsx @@ -2474,12 +2474,47 @@ describe('shallow', () => { }); describe('.isEmpty()', () => { - it('should return true iff wrapper is empty', () => { + let warningStub; + let fooNode; + let missingNode; + + beforeEach(() => { + warningStub = sinon.stub(console, 'warn'); + const wrapper = shallow( +
, + ); + fooNode = wrapper.find('.foo'); + missingNode = wrapper.find('.missing'); + }); + afterEach(() => { + warningStub.restore(); + }); + + it('should display a deprecation warning', () => { + fooNode.isEmpty(); + expect(warningStub.calledWith('Enzyme::Deprecated method isEmpty() called, use exists() instead.')).to.equal(true); + }); + + it('calls exists() instead', () => { + const existsSpy = sinon.spy(); + fooNode.exists = existsSpy; + fooNode.isEmpty(); + expect(existsSpy.called).to.equal(true); + }); + + it('should return true if wrapper is empty', () => { + expect(fooNode.isEmpty()).to.equal(false); + expect(missingNode.isEmpty()).to.equal(true); + }); + }); + + describe('.exists()', () => { + it('should return true if node exists in wrapper', () => { const wrapper = shallow(
, ); - expect(wrapper.find('.bar').isEmpty()).to.equal(true); - expect(wrapper.find('.foo').isEmpty()).to.equal(false); + expect(wrapper.find('.bar').exists()).to.equal(false); + expect(wrapper.find('.foo').exists()).to.equal(true); }); }); From 7205a8564ba71c706fe6dbc162f8b395c8640784 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Tue, 13 Dec 2016 18:31:45 -0800 Subject: [PATCH 15/60] [Dev Deps] move `in-publish` to `devDependencies` It is not necessary to include this module in `dependencies` as the `prepublish` lifecycle is not executed when enzyme is installed as a dependency of another package. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ddea8be8..ee5619af5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "dependencies": { "cheerio": "^0.22.0", "function.prototype.name": "^1.0.0", - "in-publish": "^2.0.0", "is-subset": "^0.1.1", "lodash": "^4.17.2", "object-is": "^1.0.1", @@ -83,6 +82,7 @@ "eslint-plugin-jsx-a11y": "^2.2.3", "eslint-plugin-react": "^6.7.1", "gitbook-cli": "^1.0.1", + "in-publish": "^2.0.0", "istanbul": "^1.0.0-alpha.2", "jsdom": "^6.1.0", "json-loader": "^0.5.4", From 0dc6fa931ce0d13bbdbe395e0185d5698bbfeafd Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Sat, 17 Dec 2016 02:38:38 +0900 Subject: [PATCH 16/60] Remove unused import in README example (#731) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2686c7ae3..984a79533 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,6 @@ import React from 'react'; import sinon from 'sinon'; import { mount } from 'enzyme'; -import MyComponent from './MyComponent'; import Foo from './Foo'; describe('', () => { From d04aadec63f42d49f049ad0271fe1bfd2ec16ee0 Mon Sep 17 00:00:00 2001 From: Nadege Date: Tue, 20 Dec 2016 18:57:10 +0100 Subject: [PATCH 17/60] Fix documentation typo (#741) --- docs/api/ReactWrapper/setProps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ReactWrapper/setProps.md b/docs/api/ReactWrapper/setProps.md index 07f8b182c..2f842909c 100644 --- a/docs/api/ReactWrapper/setProps.md +++ b/docs/api/ReactWrapper/setProps.md @@ -12,7 +12,7 @@ NOTE: can only be called on a wrapper instance that is also the root instance. #### Arguments -1. `props` (`Object`): An object containing new props to merge in with the current state +1. `props` (`Object`): An object containing new props to merge in with the current props 2. `callback` (`Function` [optional]): If provided, the callback function will be executed once setProps has completed From b48a411c3af72b1d07cb24331bb5bf3da84862e3 Mon Sep 17 00:00:00 2001 From: Richard Kotze Date: Wed, 16 Nov 2016 21:14:22 +0000 Subject: [PATCH 18/60] Mention should-enzyme to readme Mention [should-enzyme](https://github.com/rkotze/should-enzyme) custom assertions for should.js --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 984a79533..9ed759684 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ testing your React components, you can consider using: * [`chai-enzyme`](https://github.com/producthunt/chai-enzyme) with Mocha/Chai. * [`jasmine-enzyme`](https://github.com/blainekasten/enzyme-matchers/tree/master/packages/jasmine-enzyme) with Jasmine. * [`jest-enzyme`](https://github.com/blainekasten/enzyme-matchers/tree/master/packages/jest-enzyme) with Jest. +* [`should-enzyme`](https://github.com/rkotze/should-enzyme) for should.js. [Using Enzyme with Mocha](/docs/guides/mocha.md) From 8ede787270c22dc0d8ea9bd48ba533a3c9ff34e3 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 21 Dec 2016 19:46:37 -0800 Subject: [PATCH 19/60] v2.7.0 --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8aa34605..e9a997163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## 2.7.0 (December 21, 2016) + +### New Stuff + +- `shallow`/`mount`: Add `.slice()` method ([#661](https://github.com/airbnb/enzyme/pull/661)) +- `mount`: implement ReactWrapper#getDOMNode ([#679](https://github.com/airbnb/enzyme/pull/679)) +- `shallow`/`mount`: Add `exists`; deprecate isEmpty() ([#722](https://github.com/airbnb/enzyme/pull/722)) + +### Fixes + +- `mount`: extract MountedTraversal.hasClassName from MountedTraversal.instHasClassName, which allows ReactWrapper.hasClass to bypass the !isDOMComponent(inst) call ([#677](https://github.com/airbnb/enzyme/pull/677) +- `withDom`: Display a useful error when `withDom` fails to find "jsdom" ([#686](https://github.com/airbnb/enzyme/pull/686)) +- `mount`: ensure that `react-text` comment nodes don’t break `.find` ([#691](https://github.com/airbnb/enzyme/pull/691)) +- `mount`: `.parents()` now filters out sibling path trees ([#713](https://github.com/airbnb/enzyme/pull/713)) + ## 2.6.0 (November 9, 2016) ### New Stuff diff --git a/package.json b/package.json index ee5619af5..cd5fadc33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "enzyme", - "version": "2.6.0", + "version": "2.7.0", "description": "JavaScript Testing utilities for React", "main": "build", "scripts": { From bfc6dd3c29568035f77ff3a1a84fb2c8c151e417 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Sun, 13 Nov 2016 10:07:33 +0000 Subject: [PATCH 20/60] added 2 more tests for hasClass on composite components - passing test for composite component that renders composite component which itself renders a dom element - failing test for composite component that renders null --- test/ReactWrapper-spec.jsx | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index cc9913250..f6eb798a3 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -2126,6 +2126,42 @@ describeWithDOM('mount', () => { expect(wrapper.hasClass('doesnt-exist')).to.equal(false); }); }); + + context('When using nested composite components', () => { + it('should return whether or not node has a certain class', () => { + class Foo extends React.Component { + render() { + return (
); + } + } + class Bar extends React.Component { + render() { + return ; + } + } + const wrapper = mount(); + + expect(wrapper.hasClass('foo')).to.equal(true); + expect(wrapper.hasClass('bar')).to.equal(true); + expect(wrapper.hasClass('baz')).to.equal(true); + expect(wrapper.hasClass('some-long-string')).to.equal(true); + expect(wrapper.hasClass('FoOo')).to.equal(true); + expect(wrapper.hasClass('doesnt-exist')).to.equal(false); + }); + }); + + context('When using a Composite component that renders null', () => { + it('should return whether or not node has a certain class', () => { + class Foo extends React.Component { + render() { + return null; + } + } + const wrapper = mount(); + + expect(wrapper.hasClass('foo')).to.equal(false); + }); + }); }); describe('.forEach(fn)', () => { From 79a70c4d8b5963dd0500ac6138ad10836e620edf Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Sun, 13 Nov 2016 10:07:55 +0000 Subject: [PATCH 21/60] fix for calling hasClass on component that renders null --- src/MountedTraversal.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 88a88fd82..8f887ecea 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -48,6 +48,9 @@ export function instEqual(a, b, lenComp) { export function instHasClassName(inst, className) { const node = findDOMNode(inst); + if (!node) { // inst renders null + return false; + } if (node.classList) { return node.classList.contains(className); } From 64569952430f31982ff251258b0c584f0fa14a21 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 14 Nov 2016 23:25:42 +0000 Subject: [PATCH 22/60] stricter condition following React documentationa see https://facebook.github.io/react/docs/react-dom.html#finddomnode for return values of findDOMNode --- src/MountedTraversal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 8f887ecea..7bc7fb3e6 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -48,7 +48,7 @@ export function instEqual(a, b, lenComp) { export function instHasClassName(inst, className) { const node = findDOMNode(inst); - if (!node) { // inst renders null + if (node === null) { // inst renders null return false; } if (node.classList) { From 4d0eb621c9a7d75cb1839946139d82712103b218 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Mon, 12 Dec 2016 22:46:32 +0000 Subject: [PATCH 23/60] Add test for hasClass used on SFCs, Remove limitation for use of getDOMNode on SFCs --- src/ReactWrapper.jsx | 9 +-------- test/ReactWrapper-spec.jsx | 39 ++++++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/ReactWrapper.jsx b/src/ReactWrapper.jsx index 21fa7e1a3..63d1a6f35 100644 --- a/src/ReactWrapper.jsx +++ b/src/ReactWrapper.jsx @@ -29,7 +29,6 @@ import { typeOfNode, displayNameOfNode, ITERATOR_SYMBOL, - isFunctionalComponent, } from './Utils'; import { debugInsts, @@ -139,13 +138,7 @@ class ReactWrapper { * @returns {DOMComponent} */ getDOMNode() { - return this.single('getDOMNode', (n) => { - if (isFunctionalComponent(n)) { - throw new TypeError('Method “getDOMNode” cannot be used on functional components.'); - } - - return findDOMNode(n); - }); + return this.single('getDOMNode', findDOMNode); } /** diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index f6eb798a3..754420f99 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -2109,7 +2109,21 @@ describeWithDOM('mount', () => { }); }); - context('When using a Composite component', () => { + describeIf(!REACT013, 'with stateless components', () => { + it('should return whether or not node has a certain class', () => { + const Foo = () =>
; + const wrapper = mount(); + + expect(wrapper.hasClass('foo')).to.equal(true); + expect(wrapper.hasClass('bar')).to.equal(true); + expect(wrapper.hasClass('baz')).to.equal(true); + expect(wrapper.hasClass('some-long-string')).to.equal(true); + expect(wrapper.hasClass('FoOo')).to.equal(true); + expect(wrapper.hasClass('doesnt-exist')).to.equal(false); + }); + }); + + context('When using a Composite class component', () => { it('should return whether or not node has a certain class', () => { class Foo extends React.Component { render() { @@ -3389,11 +3403,28 @@ describeWithDOM('mount', () => { }); describeIf(!REACT013, 'stateless components', () => { - const SFC = () => (
); + const SFC = () => ( +
+
+ + +
+
+ ); - it('should throw when wrapping an SFC', () => { + it('should return the outer most DOMComponent of the root wrapper', () => { const wrapper = mount(); - expect(() => wrapper.getDOMNode()).to.throw(TypeError, 'Method “getDOMNode” cannot be used on functional components.'); + expect(wrapper.getDOMNode()).to.have.property('className', 'outer'); + }); + + it('should return the outer most DOMComponent of the inner div wrapper', () => { + const wrapper = mount(); + expect(wrapper.find('.inner').getDOMNode()).to.have.property('className', 'inner'); + }); + + it('should throw when wrapping multiple elements', () => { + const wrapper = mount().find('span'); + expect(() => wrapper.getDOMNode()).to.throw(Error); }); }); }); From cb1daadbbc0c158039c1fe6d8eae971e386bcae1 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Tue, 13 Dec 2016 07:49:59 +0000 Subject: [PATCH 24/60] Fix typo --- test/ReactWrapper-spec.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 754420f99..29adfb0c8 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -3387,12 +3387,12 @@ describeWithDOM('mount', () => { } } - it('should return the outer most DOMComponent of the root wrapper', () => { + it('should return the outermost DOMComponent of the root wrapper', () => { const wrapper = mount(); expect(wrapper.getDOMNode()).to.have.property('className', 'outer'); }); - it('should return the outer most DOMComponent of the inner div wrapper', () => { + it('should return the outermost DOMComponent of the inner div wrapper', () => { const wrapper = mount(); expect(wrapper.find('.inner').getDOMNode()).to.have.property('className', 'inner'); }); @@ -3412,12 +3412,12 @@ describeWithDOM('mount', () => {
); - it('should return the outer most DOMComponent of the root wrapper', () => { + it('should return the outermost DOMComponent of the root wrapper', () => { const wrapper = mount(); expect(wrapper.getDOMNode()).to.have.property('className', 'outer'); }); - it('should return the outer most DOMComponent of the inner div wrapper', () => { + it('should return the outermost DOMComponent of the inner div wrapper', () => { const wrapper = mount(); expect(wrapper.find('.inner').getDOMNode()).to.have.property('className', 'inner'); }); From fc2fef066c7b870b64fdc0d7a91aac84201abdda Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Thu, 22 Dec 2016 14:17:25 +0000 Subject: [PATCH 25/60] Test error messages for getDOMNode --- test/ReactWrapper-spec.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 29adfb0c8..6723551cd 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -3399,7 +3399,10 @@ describeWithDOM('mount', () => { it('should throw when wrapping multiple elements', () => { const wrapper = mount().find('span'); - expect(() => wrapper.getDOMNode()).to.throw(Error); + expect(() => wrapper.getDOMNode()).to.throw( + Error, + 'Method “getDOMNode” is only meant to be run on a single node. 2 found instead.', + ); }); describeIf(!REACT013, 'stateless components', () => { @@ -3424,7 +3427,10 @@ describeWithDOM('mount', () => { it('should throw when wrapping multiple elements', () => { const wrapper = mount().find('span'); - expect(() => wrapper.getDOMNode()).to.throw(Error); + expect(() => wrapper.getDOMNode()).to.throw( + Error, + 'Method “getDOMNode” is only meant to be run on a single node. 2 found instead.', + ); }); }); }); From 010e9f0fda10ca49e3aa60d3ee558d6a9743ce32 Mon Sep 17 00:00:00 2001 From: siemiatj Date: Fri, 23 Dec 2016 17:35:19 +0100 Subject: [PATCH 26/60] - add crowdSPRING to 'in the wild' --- INTHEWILD.md | 1 + 1 file changed, 1 insertion(+) diff --git a/INTHEWILD.md b/INTHEWILD.md index d8d11965f..e9199e0e8 100644 --- a/INTHEWILD.md +++ b/INTHEWILD.md @@ -14,6 +14,7 @@ Organizations - [Airware](https://github.com/airware) - [Flatiron School](https://github.com/flatiron-labs) - [Outreach.io](https://github.com/getoutreach) + - [crowdSPRING](https://crowdspring.com) Projects ---------- From d42527e8a2807057af7c8a67c1e4948fd8d61342 Mon Sep 17 00:00:00 2001 From: blackpost38 Date: Fri, 23 Dec 2016 10:28:13 +0900 Subject: [PATCH 27/60] Update Docs for .getNode and .getNodes #743 --- docs/api/ReactWrapper/getNode.md | 34 +++++++++++++++++++++++++ docs/api/ReactWrapper/getNodes.md | 34 +++++++++++++++++++++++++ docs/api/ShallowWrapper/getNode.md | 38 ++++++++++++++++++++++++++++ docs/api/ShallowWrapper/getNodes.md | 39 +++++++++++++++++++++++++++++ docs/api/mount.md | 6 +++++ docs/api/shallow.md | 6 +++++ 6 files changed, 157 insertions(+) create mode 100644 docs/api/ReactWrapper/getNode.md create mode 100644 docs/api/ReactWrapper/getNodes.md create mode 100644 docs/api/ShallowWrapper/getNode.md create mode 100644 docs/api/ShallowWrapper/getNodes.md diff --git a/docs/api/ReactWrapper/getNode.md b/docs/api/ReactWrapper/getNode.md new file mode 100644 index 000000000..9e8cead3d --- /dev/null +++ b/docs/api/ReactWrapper/getNode.md @@ -0,0 +1,34 @@ +# `.getNode() => ReactElement` + +Returns the wrapper's underlying node. + + +#### Returns + +`ReactElement`: The retrieved node. + + + +#### Examples + +```jsx +class Test extends React.Component { + render() { + return ( +
+ + +
+ ); + } +} + +const wrapper = mount(); +expect(wrapper.getNode()).to.be.an.instanceof(Test); +``` + + + +#### Related Methods + +- [`.getNodes() => Array`](getNodes.md) diff --git a/docs/api/ReactWrapper/getNodes.md b/docs/api/ReactWrapper/getNodes.md new file mode 100644 index 000000000..e3eccce1f --- /dev/null +++ b/docs/api/ReactWrapper/getNodes.md @@ -0,0 +1,34 @@ +# `.getNodes() => Array` + +Returns the wrapper's underlying nodes. + + +#### Returns + +`Array`: The retrieved nodes. + + + +#### Examples + +```jsx +class Test extends React.Component { + render() { + return ( +
+ + +
+ ); + } +} + +const wrapper = mount(); +expect(wrapper.find('span').getNodes()).to.have.lengthOf(2); +``` + + + +#### Related Methods + +- [`.getNode() => ReactElement`](getNode.md) diff --git a/docs/api/ShallowWrapper/getNode.md b/docs/api/ShallowWrapper/getNode.md new file mode 100644 index 000000000..9b5dae298 --- /dev/null +++ b/docs/api/ShallowWrapper/getNode.md @@ -0,0 +1,38 @@ +# `.getNode() => ReactElement` + +Returns the wrapper's underlying node. + +If the current wrapper is wrapping the root component, returns the root component's latest render output. + + +#### Returns + +`ReactElement`: The retrieved node. + + + +#### Examples + +```jsx +const element = ( +
+ + +
+); + +class MyComponent extends React.Component { + render() { + return element; + } +} + +const wrapper = shallow(); +expect(wrapper.getNode()).to.equal(element); +``` + + + +#### Related Methods + +- [`.getNodes() => Array`](getNodes.md) diff --git a/docs/api/ShallowWrapper/getNodes.md b/docs/api/ShallowWrapper/getNodes.md new file mode 100644 index 000000000..ebffd742b --- /dev/null +++ b/docs/api/ShallowWrapper/getNodes.md @@ -0,0 +1,39 @@ +# `.getNodes() => Array` + +Returns the wrapper's underlying nodes. + +If the current wrapper is wrapping the root component, returns the root component's latest render output wrapped in an array. + + +#### Returns + +`Array`: The retrieved nodes. + + + +#### Examples + +```jsx +const one = ; +const two = ; + +class Test extends React.Component { + render() { + return ( +
+ { one } + { two } +
+ ); + } +} + +const wrapper = shallow(); +expect(wrapper.find('span').getNodes()).to.deep.equal([one, two]); +``` + + + +#### Related Methods + +- [`.getNode() => ReactElement`](getNode.md) diff --git a/docs/api/mount.md b/docs/api/mount.md index 507ebbd91..7184239ad 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -125,6 +125,12 @@ Returns a static HTML rendering of the current node. #### [`.get(index) => ReactElement`](ReactWrapper/get.md) Returns the node at the provided index of the current wrapper. +#### [`.getNode() => ReactElement`](ReactWrapper/getNode.md) +Returns the wrapper's underlying node. + +#### [`.getNodes() => Array`](ReactWrapper/getNodes.md) +Returns the wrapper's underlying nodes. + #### [`.getDOMNode() => DOMComponent`](ReactWrapper/getDOMNode.md) Returns the outer most DOMComponent of the current wrapper. diff --git a/docs/api/shallow.md b/docs/api/shallow.md index fb248a45b..0296a7d92 100644 --- a/docs/api/shallow.md +++ b/docs/api/shallow.md @@ -133,6 +133,12 @@ Returns a static HTML rendering of the current node. #### [`.get(index) => ReactElement`](ShallowWrapper/get.md) Returns the node at the provided index of the current wrapper. +#### [`.getNode() => ReactElement`](ShallowWrapper/getNode.md) +Returns the wrapper's underlying node. + +#### [`.getNodes() => Array`](ShallowWrapper/getNodes.md) +Returns the wrapper's underlying nodes. + #### [`.at(index) => ShallowWrapper`](ShallowWrapper/at.md) Returns a wrapper of the node at the provided index of the current wrapper. From c2545d9d8637d2b6a4d1f2d117b45a70f578baf5 Mon Sep 17 00:00:00 2001 From: blainekasten Date: Wed, 28 Dec 2016 12:27:45 -0600 Subject: [PATCH 28/60] Create Common issues and issue template --- .github/ISSUE_TEMPLATE.md | 13 ++++++ docs/common-issues.md | 89 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 docs/common-issues.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..349336960 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +Thanks for reporting an issue to us! We're glad you are using and invested in Enzyme. +Before submitting, please read over our commonly reported issues to prevent duplicates! + +### All common issues + +* [common issues](/docs/common-issues) + +### Notoriously common issues + +* [Webpack build issues](/docs/common-issues#webpackbuildissues) +* [Cannot find module 'react-dom/lib/ReactTestUtils'](/docs/common-issues) +* [Query Selector fails](/docs/common-issues#queryselectorfails) +* [Testing third party libraries](/docs/common-issues#testingthirdpartylibraries) diff --git a/docs/common-issues.md b/docs/common-issues.md new file mode 100644 index 000000000..c2d8b8d99 --- /dev/null +++ b/docs/common-issues.md @@ -0,0 +1,89 @@ +# Common Issues + +This list aims to be comprehensive. If you find an issue that has been frequently brought up in Github *issues* that is not here, please open a PR to add it. + +### Webpack Build Issues + +- [Related Github issue](https://github.com/airbnb/enzyme/issues/684) + +###### Common Solutions + +_Mismatched versions of React and React* libraries._ + +It is important to ensure all React and React* libraries your project depend on are matching versions. +If you are using React 15.4.0, you should ensure your React* libraries (like react-test-utils) are equivalently on 15.4.0. + +_Bad configuration._ + +Please see the guide for [webpack](/docs/guides/webpack) to ensure your configuration is correct for weback. + +### Error: Cannot find module 'react-dom/lib/ReactTestUtils' + +- [Related Github issue](https://github.com/airbnb/enzyme/issues/684) +- [Related code](https://github.com/airbnb/enzyme/blob/3aeb02461eabf2fd402613991915d8d6f4b88536/src/react-compat.js#L97-L105) + +###### Reason + +In order to properly support multiple versions of React, we have conditional requirements that npm does not support with tools like +`peerDependencies`. Instead we manually require and throw errors if the dependency is not met. + +###### Solution + +Install a matching version of React for `react-test-utils`. Example package.json + +```json +{ + "devDependencies": { + "react": "15.4.0", + "react-test-utils": "15.4.0" + } +} +``` + +### Query Selector fails + +###### Reason + +This could be due to a regression, or the feature is not yet implemented. If you are wanting to use a +certain query syntax, make sure it is implemented first before raising an issue. Here is the list of +selectors we currently support: https://github.com/airbnb/enzyme/blob/master/docs/api/selector.md + +### Testing third party libraries + +Some third party libraries are difficult or impossible to test. Enzyme's scope is severly limited to what +React exposes and provides for us. Things like "portals" are not currently testable with Enzyme directly for that reason. + +An example: + +If you are testing a library that creates a Modal, and it manually appends it to a different part of the DOM, React has lost +track of this component, and therefore Enzyme has also lost track of it. + +Even moreso, if this library appends dom elements into react components, react still does not know about it. A library like d3 which +appends DOM elements would be an example here. + +###### Solutions + +You can use the `render` API to attempt to access and assert on the appended DOM components. This will likely become natively supported +when React natively supports Portals, which is expected to land with Fiber. + +If the third party solution lets you attach a `ref`, that would be the ideal scenario. With a `ref` you can then get that element from Enzyme. + +example + +```jsx +import ThirdPartyPortalLibrary from 'someplace'; + +class Comp extends React.Component { + render() { + return this.portal = node}> + } +} + +const wrapper = mount(); +const portal = wrapper.instance().portal; +// assert on `portal` +``` + + + + From 64ac7441d23157732d38b62a29003646cc92bcd2 Mon Sep 17 00:00:00 2001 From: Toru Kobayashi Date: Thu, 5 Jan 2017 12:52:38 +0900 Subject: [PATCH 29/60] Add a link for `ShallowWrapper#dive()` --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 08b3979c1..a2c049031 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,6 +27,7 @@ * [containsAnyMatchingElements(nodes)](/docs/api/ShallowWrapper/containsAnyMatchingElements.md) * [context([key])](/docs/api/ShallowWrapper/context.md) * [debug()](/docs/api/ShallowWrapper/debug.md) + * [dive()](/docs/api/ShallowWrapper/dive.md) * [equals(node)](/docs/api/ShallowWrapper/equals.md) * [every(selector)](/docs/api/ShallowWrapper/every.md) * [everyWhere(predicate)](/docs/api/ShallowWrapper/everyWhere.md) From a4dd0b485c8eba76705a084c9e86e14b0bf146a8 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Thu, 22 Sep 2016 12:19:29 -0500 Subject: [PATCH 30/60] Ignore text nodes in childrenOfInstInternal --- src/MountedTraversal.js | 3 +++ test/ReactWrapper-spec.jsx | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 7bc7fb3e6..7540f719e 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -124,6 +124,9 @@ export function childrenOfInstInternal(inst) { if (REACT013 && !node.getPublicInstance) { return false; } + if (typeof renderedChildren[key]._stringText !== 'undefined') { + return false; + } return true; }).map((node) => { if (!REACT013 && typeof node._currentElement.type === 'function') { diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 6723551cd..77d468686 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -1910,6 +1910,12 @@ describeWithDOM('mount', () => { expect(children.at(1).hasClass('baz')).to.equal(true); }); + it('should not attempt to get an instance for text nodes', () => { + const wrapper = mount(
BC
); + const children = wrapper.children(); + expect(children.length).to.equal(1); + }); + describeIf(!REACT013, 'stateless function components', () => { it('should handle mixed children with and without arrays', () => { const Foo = props => ( From 8fa87173cae4537e09319128eb5f72cf5612d803 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Mon, 9 Jan 2017 17:46:48 -0600 Subject: [PATCH 31/60] Use node._stringText instead of renderedChildren Since we no longer iterate over the keys --- src/MountedTraversal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 7540f719e..914dbf909 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -124,7 +124,7 @@ export function childrenOfInstInternal(inst) { if (REACT013 && !node.getPublicInstance) { return false; } - if (typeof renderedChildren[key]._stringText !== 'undefined') { + if (typeof node._stringText !== 'undefined') { return false; } return true; From dd9eae1d3b9a1d36ddf895df6f53934907acf9ef Mon Sep 17 00:00:00 2001 From: Toru Kobayashi Date: Thu, 5 Jan 2017 13:37:53 +0900 Subject: [PATCH 32/60] [Docs] Fix alphabetical order of API lists --- docs/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index a2c049031..4fe21f837 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,9 +22,9 @@ * [children()](/docs/api/ShallowWrapper/children.md) * [closest(selector)](/docs/api/ShallowWrapper/closest.md) * [contains(nodeOrNodes)](/docs/api/ShallowWrapper/contains.md) - * [containsMatchingElement(node)](/docs/api/ShallowWrapper/containsMatchingElement.md) * [containsAllMatchingElements(nodes)](/docs/api/ShallowWrapper/containsAllMatchingElements.md) * [containsAnyMatchingElements(nodes)](/docs/api/ShallowWrapper/containsAnyMatchingElements.md) + * [containsMatchingElement(node)](/docs/api/ShallowWrapper/containsMatchingElement.md) * [context([key])](/docs/api/ShallowWrapper/context.md) * [debug()](/docs/api/ShallowWrapper/debug.md) * [dive()](/docs/api/ShallowWrapper/dive.md) @@ -43,10 +43,10 @@ * [html()](/docs/api/ShallowWrapper/html.md) * [instance()](/docs/api/ShallowWrapper/instance.md) * [is(selector)](/docs/api/ShallowWrapper/is.md) + * [isEmpty()](/docs/api/ShallowWrapper/isEmpty.md) * [key()](/docs/api/ShallowWrapper/key.md) * [last()](/docs/api/ShallowWrapper/last.md) * [map(fn)](/docs/api/ShallowWrapper/map.md) - * [isEmpty()](/docs/api/ShallowWrapper/isEmpty.md) * [matchesElement(node)](/docs/api/ShallowWrapper/matchesElement.md) * [name()](/docs/api/ShallowWrapper/name.md) * [not(selector)](/docs/api/ShallowWrapper/not.md) @@ -74,9 +74,9 @@ * [Full DOM Rendering](/docs/api/mount.md) * [at(index)](/docs/api/ReactWrapper/at.md) * [contains(nodeOrNodes)](/docs/api/ReactWrapper/contains.md) - * [containsMatchingElement(node)](/docs/api/ReactWrapper/containsMatchingElement.md) * [containsAllMatchingElements(nodes)](/docs/api/ReactWrapper/containsAllMatchingElements.md) * [containsAnyMatchingElements(nodes)](/docs/api/ReactWrapper/containsAnyMatchingElements.md) + * [containsMatchingElement(node)](/docs/api/ReactWrapper/containsMatchingElement.md) * [childAt()](/docs/api/ReactWrapper/childAt.md) * [children()](/docs/api/ReactWrapper/children.md) * [closest(selector)](/docs/api/ReactWrapper/closest.md) @@ -84,8 +84,8 @@ * [debug()](/docs/api/ReactWrapper/debug.md) * [detach()](/docs/api/ReactWrapper/detach.md) * [every(selector)](/docs/api/ReactWrapper/every.md) - * [exists()](/docs/api/ReactWrapper/exists.md) * [everyWhere(predicate)](/docs/api/ReactWrapper/everyWhere.md) + * [exists()](/docs/api/ReactWrapper/exists.md) * [filter(selector)](/docs/api/ReactWrapper/filter.md) * [filterWhere(predicate)](/docs/api/ReactWrapper/filterWhere.md) * [find(selector)](/docs/api/ReactWrapper/find.md) @@ -97,10 +97,10 @@ * [html()](/docs/api/ReactWrapper/html.md) * [instance()](/docs/api/ReactWrapper/instance.md) * [is(selector)](/docs/api/ReactWrapper/is.md) + * [isEmpty()](/docs/api/ReactWrapper/isEmpty.md) * [key()](/docs/api/ReactWrapper/key.md) * [last()](/docs/api/ReactWrapper/last.md) * [map(fn)](/docs/api/ReactWrapper/map.md) - * [isEmpty()](/docs/api/ReactWrapper/isEmpty.md) * [matchesElement(node)](/docs/api/ReactWrapper/matchesElement.md) * [mount()](/docs/api/ReactWrapper/mount.md) * [name()](/docs/api/ReactWrapper/name.md) From ea8be1701049d3b1e48a6fd6b438e7834cfbdaa3 Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Tue, 10 Jan 2017 09:43:36 -0800 Subject: [PATCH 33/60] Include nteract as a user of enzyme --- INTHEWILD.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/INTHEWILD.md b/INTHEWILD.md index e9199e0e8..eb8f442f0 100644 --- a/INTHEWILD.md +++ b/INTHEWILD.md @@ -15,6 +15,7 @@ Organizations - [Flatiron School](https://github.com/flatiron-labs) - [Outreach.io](https://github.com/getoutreach) - [crowdSPRING](https://crowdspring.com) + - [nteract](https://nteract.io) Projects ---------- @@ -24,3 +25,4 @@ Projects - [Recompose](https://github.com/acdlite/recompose) - [Reapop](https://github.com/LouisBarranqueiro/reapop) - [React Dates](https://github.com/airbnb/react-dates) + - [nteract notebook](https://github.com/nteract/nteract) From 7f89d7517dce597c3dafba768d1157294dada18f Mon Sep 17 00:00:00 2001 From: Cezar Augusto Date: Thu, 12 Jan 2017 23:55:59 -0200 Subject: [PATCH 34/60] =?UTF-8?q?Add=20Brave=20=F0=9F=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref: https://github.com/brave/browser-laptop/blob/master/package.json#L144 🔥😎👍 --- INTHEWILD.md | 1 + 1 file changed, 1 insertion(+) diff --git a/INTHEWILD.md b/INTHEWILD.md index eb8f442f0..e4dc4dcd2 100644 --- a/INTHEWILD.md +++ b/INTHEWILD.md @@ -16,6 +16,7 @@ Organizations - [Outreach.io](https://github.com/getoutreach) - [crowdSPRING](https://crowdspring.com) - [nteract](https://nteract.io) + - [Brave](https://brave.com) Projects ---------- From e4be00c4f16e3a072d909eb40ab352b3828d6e15 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 22 Jan 2017 10:42:09 -0800 Subject: [PATCH 35/60] v2.7.1 --- CHANGELOG.md | 13 +++++++++++++ package.json | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a997163..fc2945451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## 2.7.1 (January 22, 2017) + +### Fixes + +- `mount`: Fix bug from ([#677](https://github.com/airbnb/enzyme/pull/677) ([#680](https://github.com/airbnb/enzyme/pull/680) +- `mount`: ignore text nodes in childrenOfInst ([#604](https://github.com/airbnb/enzyme/pull/604) + +### Documentation + +- Update Docs for .getNode and .getNodes ([#743](https://github.com/airbnb/enzyme/pull/743) +- Add a link for `ShallowWrapper#dive()` ([#759](https://github.com/airbnb/enzyme/pull/759) +- Fix alphabetical order of API lists ([#761](https://github.com/airbnb/enzyme/pull/761) + ## 2.7.0 (December 21, 2016) ### New Stuff diff --git a/package.json b/package.json index cd5fadc33..ffefe577e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "enzyme", - "version": "2.7.0", + "version": "2.7.1", "description": "JavaScript Testing utilities for React", "main": "build", "scripts": { From ddac679231820431536d58821d6ebf01690a9169 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 14 Jan 2017 01:53:50 -0800 Subject: [PATCH 36/60] [New] `Utils`: add `nodeMatches`, `childrenMatch` --- src/Utils.js | 51 +++++++-- test/Utils-spec.jsx | 246 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 267 insertions(+), 30 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index 3ae53d4fc..9d6601768 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -71,27 +71,48 @@ export function nodeHasType(node, type) { functionName(node.type) === type : node.type.name === type) || node.type.displayName === type; } -export function childrenEqual(a, b, lenComp) { +function internalChildrenCompare(a, b, lenComp, isLoose) { + const nodeCompare = isLoose ? nodeMatches : nodeEqual; + if (a === b) return true; if (!Array.isArray(a) && !Array.isArray(b)) { - return nodeEqual(a, b, lenComp); + return nodeCompare(a, b, lenComp); } if (!a && !b) return true; if (a.length !== b.length) return false; if (a.length === 0 && b.length === 0) return true; for (let i = 0; i < a.length; i += 1) { - if (!nodeEqual(a[i], b[i], lenComp)) return false; + if (!nodeCompare(a[i], b[i], lenComp)) return false; } return true; } -export function nodeEqual(a, b, lenComp = is) { +export function childrenMatch(a, b, lenComp) { + return internalChildrenCompare(a, b, lenComp, true); +} + +export function childrenEqual(a, b, lenComp) { + return internalChildrenCompare(a, b, lenComp, false); +} + +function removeNullaryReducer(acc, [key, value]) { + const addition = value == null ? {} : { [key]: value }; + return assign({}, acc, addition); +} + +function internalNodeCompare(a, b, lenComp, isLoose) { if (a === b) return true; if (!a || !b) return false; if (a.type !== b.type) return false; - const left = propsOfNode(a); + + let left = propsOfNode(a); + let right = propsOfNode(b); + if (isLoose) { + left = entries(left).reduce(removeNullaryReducer, {}); + right = entries(right).reduce(removeNullaryReducer, {}); + } + const leftKeys = Object.keys(left); - const right = propsOfNode(b); for (let i = 0; i < leftKeys.length; i += 1) { const prop = leftKeys[i]; // we will check children later @@ -110,11 +131,13 @@ export function nodeEqual(a, b, lenComp = is) { const leftHasChildren = 'children' in left; const rightHasChildren = 'children' in right; + const childCompare = isLoose ? childrenMatch : childrenEqual; if (leftHasChildren || rightHasChildren) { - if (!childrenEqual( - childrenToArray(left.children), - childrenToArray(right.children), - lenComp)) { + if (!childCompare( + childrenToArray(left.children), + childrenToArray(right.children), + lenComp, + )) { return false; } } @@ -127,6 +150,14 @@ export function nodeEqual(a, b, lenComp = is) { return false; } +export function nodeMatches(a, b, lenComp = is) { + return internalNodeCompare(a, b, lenComp, true); +} + +export function nodeEqual(a, b, lenComp = is) { + return internalNodeCompare(a, b, lenComp, false); +} + export function containsChildrenSubArray(match, node, subArray) { const children = childrenOfNode(node); const checker = (_, i) => arraysEqual(match, children.slice(i, i + subArray.length), subArray); diff --git a/test/Utils-spec.jsx b/test/Utils-spec.jsx index 21a135798..80f9291b7 100644 --- a/test/Utils-spec.jsx +++ b/test/Utils-spec.jsx @@ -9,6 +9,7 @@ import { coercePropValue, getNode, nodeEqual, + nodeMatches, isPseudoClassSelector, propFromEvent, SELECTOR, @@ -21,7 +22,6 @@ import { REACT013 } from '../src/version'; describe('Utils', () => { describeWithDOM('getNode', () => { - it('should return a DOMNode when a DOMComponent is given', () => { const div = mount(
).getNode(); expect(getNode(div)).to.be.instanceOf(window.HTMLElement); @@ -45,27 +45,21 @@ describe('Utils', () => { }); describe('nodeEqual', () => { - it('should match empty elements of same tag', () => { - expect(nodeEqual(
,
, )).to.equal(true); - }); it('should not match empty elements of different type', () => { - expect(nodeEqual(
,