diff --git a/package.json b/package.json index 12bae69..ace4c61 100644 --- a/package.json +++ b/package.json @@ -46,5 +46,8 @@ "react": "^0.14.0", "react-addons-test-utils": "^0.14.0" }, - "dependencies": {} + "dependencies": { + "lodash.get": "^3.7.0", + "lodash.isequal": "^3.0.4" + } } diff --git a/specs/find-all-spec.js b/specs/find-all-spec.js index d3bf66a..ebe356f 100644 --- a/specs/find-all-spec.js +++ b/specs/find-all-spec.js @@ -2,40 +2,36 @@ import {findAll} from '../src'; import {createRenderer} from 'react-addons-test-utils'; import React from 'react'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class TestWithForm extends React.Component { - render() { - return ( -
- -
-
-
-
- - - -
- - Some content -
- ); - } +function TestWithForm() { + return ( +
+ +
+
+
+
+ + + +
+ + Some content +
+ ); } describe('`findAll`', function() { diff --git a/specs/find-all-with-class-spec.js b/specs/find-all-with-class-spec.js index 38a5431..7d617e1 100644 --- a/specs/find-all-with-class-spec.js +++ b/specs/find-all-with-class-spec.js @@ -4,19 +4,17 @@ import React from 'react'; describe('`findAllWithClass`', function() { beforeEach(function() { - class TestWithClasses extends React.Component { - render() { - return ( -
- -
-
-
-
- Some content -
- ); - } + function TestWithClasses() { + return ( +
+ +
+
+
+
+ Some content +
+ ); } this.renderer = createRenderer(); diff --git a/specs/find-all-with-prop-spec.js b/specs/find-all-with-prop-spec.js new file mode 100644 index 0000000..3700a16 --- /dev/null +++ b/specs/find-all-with-prop-spec.js @@ -0,0 +1,70 @@ +import {findAllWithProp} from '../src'; +import {createRenderer} from 'react-addons-test-utils'; +import React from 'react'; + +describe('`findAllWithProp`', function() { + beforeEach(function() { + function TestComponent() { + const deepObject = { + top: { + deep: true + } + }; + + return ( +
+
+
+
+
+
+ ); + } + + this.renderer = createRenderer(); + this.renderer.render(); + this.tree = this.renderer.getRenderOutput(); + }); + + it('should find one component with `testProp1` equal to `1`', function() { + const found = findAllWithProp(this.tree, 'testProp1', 1); + + expect(found.length).toBe(1); + }); + + it('should find no component with `testProp1` equal to `2`', function() { + const found = findAllWithProp(this.tree, 'testProp1', 2); + + expect(found.length).toBe(0); + }); + + it('should find two components with `testProp3.top` equal to `{deep:true}`', function() { + const found = findAllWithProp(this.tree, 'testProp3.top', {deep:true}); + + expect(found.length).toBe(2); + }); + + it('should find no components with `testProp3.top` equal to `{deep:false}`', function() { + const found = findAllWithProp(this.tree, 'testProp3.top', {deep:false}); + + expect(found.length).toBe(0); + }); + + it('should find four components with `sameProp` equal to `same`', function() { + const found = findAllWithProp(this.tree, 'sameProp', 'same'); + + expect(found.length).toBe(4); + }); + + it('should find no components with `sameProp` equal to `not-same`', function() { + const found = findAllWithProp(this.tree, 'sameProp', 'not-same'); + + expect(found.length).toBe(0); + }); + + it('should find no components with non-existant prop', function() { + const found = findAllWithProp(this.tree, 'blahblahblah', 1); + + expect(found.length).toBe(0); + }); +}); diff --git a/specs/find-all-with-type-spec.js b/specs/find-all-with-type-spec.js index bcc1861..8623eaf 100644 --- a/specs/find-all-with-type-spec.js +++ b/specs/find-all-with-type-spec.js @@ -2,39 +2,33 @@ import {findAllWithType} from '../src'; import {createRenderer} from 'react-addons-test-utils'; import React from 'react'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class YetAnotherComponent extends React.Component { - render() { - return ( -
- ); - } +function YetAnotherComponent() { + return ( +
+ ); } -class TestWithTypes extends React.Component { - render() { - return ( -
- -
-
-
- - - - - - -
- ); - } +function TestWithTypes() { + return ( +
+ +
+
+
+ + + + + + +
+ ); } describe('`findAllWithType`', function() { diff --git a/specs/find-with-class-spec.js b/specs/find-with-class-spec.js index 6cba63e..e45b850 100644 --- a/specs/find-with-class-spec.js +++ b/specs/find-with-class-spec.js @@ -4,18 +4,16 @@ import React from 'react'; describe('`findWithClass`', function() { beforeEach(function() { - class TestWithClasses extends React.Component { - render() { - return ( -
- -
-
-
- Some content -
- ); - } + function TestWithClasses() { + return ( +
+ +
+
+
+ Some content +
+ ); } this.renderer = createRenderer(); diff --git a/specs/find-with-prop-spec.js b/specs/find-with-prop-spec.js new file mode 100644 index 0000000..9f266e8 --- /dev/null +++ b/specs/find-with-prop-spec.js @@ -0,0 +1,76 @@ +import {findWithProp} from '../src'; +import {createRenderer} from 'react-addons-test-utils'; +import React from 'react'; + +describe('`findWithProp`', function() { + beforeEach(function() { + function TestComponent() { + const deepObject = { + top: { + deep: true + } + }; + + const deepArray = [{ + name: 'Dave' + }, { + name: 'Sarah' + }, { + name: 'John' + }]; + + return ( +
+
+
+
+
+
+ ); + } + + this.renderer = createRenderer(); + this.renderer.render(); + this.tree = this.renderer.getRenderOutput(); + }); + + it('should find component with `testProp1` equal to `1`', function() { + expect(() => findWithProp(this.tree, 'testProp1', 1)).not.toThrow(); + }); + + it('should not find component with `testProp1` equal to `2`', function() { + expect(() => findWithProp(this.tree, 'testProp1', 2)).toThrow(); + }); + + it('should find component with `testProp2` equal to `testProp2Value`', function() { + expect(() => findWithProp(this.tree, 'testProp2', 'testProp2Value')).not.toThrow(); + }); + + it('should not find component with `testProp2` equal to `NOTtestProp2Value`', function() { + expect(() => findWithProp(this.tree, 'testProp2', 'NOTtestProp2Value')).toThrow(); + }); + + it('should find component with `testProp3.top` equal to `{deep:true}`', function() { + expect(() => findWithProp(this.tree, 'testProp3.top', {deep:true})).not.toThrow(); + }); + + it('should not find component with `testProp3.top` equal to `{deep:false}`', function() { + expect(() => findWithProp(this.tree, 'testProp3.top', {deep:false})).toThrow(); + }); + + it('should find component with `testProp4[1].name` equal to `{name: Sarah}`', function() { + expect(() => findWithProp(this.tree, 'testProp4[1]', {name: 'Sarah'})).not.toThrow(); + }); + + it('should find component with `testProp4[1].name` equal to `{name: Chris}`', function() { + expect(() => findWithProp(this.tree, 'testProp4[1]', {name: 'Chris'})).toThrow(); + }); + + it('should not find exactly one component with `sameProp` equal to `same`', function() { + expect(() => findWithProp(this.tree, 'sameProp', 'same')).toThrow(); + }); + + it('should find no components with non-existant prop', function() { + expect(() => findWithProp(this.tree, 'blahblahblah', 1)).toThrow(); + }); +}); diff --git a/specs/find-with-ref-spec.js b/specs/find-with-ref-spec.js index 81c5f04..37f848a 100644 --- a/specs/find-with-ref-spec.js +++ b/specs/find-with-ref-spec.js @@ -2,25 +2,21 @@ import {findWithRef} from '../src'; import {createRenderer} from 'react-addons-test-utils'; import React from 'react'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class TestWithRefs extends React.Component { - render() { - return ( -
- -
- - -
- ); - } +function TestWithRefs() { + return ( +
+ +
+ + +
+ ); } describe('`findWithRef`', function() { diff --git a/specs/find-with-type-spec.js b/specs/find-with-type-spec.js index 5418511..120a257 100644 --- a/specs/find-with-type-spec.js +++ b/specs/find-with-type-spec.js @@ -2,27 +2,23 @@ import {findWithType} from '../src'; import {createRenderer} from 'react-addons-test-utils'; import React from 'react'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class TestWithTypes extends React.Component { - render() { - return ( -
- -
-
-
- Some content - -
- ); - } +function TestWithTypes() { + return ( +
+ +
+
+
+ Some content + +
+ ); } describe('`findWithType`', function() { diff --git a/specs/is-component-of-type-spec.js b/specs/is-component-of-type-spec.js index 74c8949..d7adf90 100644 --- a/specs/is-component-of-type-spec.js +++ b/specs/is-component-of-type-spec.js @@ -2,30 +2,24 @@ import {isComponentOfType} from '../src'; import React from 'react'; import {createRenderer} from 'react-addons-test-utils'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class WrongComponent extends React.Component { - render() { - return ( -
- ); - } +function WrongComponent() { + return ( +
+ ); } -class Test extends React.Component { - render() { - return ( -
- -
- ); - } +function Test() { + return ( +
+ +
+ ); } describe('`isComponentOfType`', function() { diff --git a/specs/is-dom-component-spec.js b/specs/is-dom-component-spec.js index 16a670c..c38d03a 100644 --- a/specs/is-dom-component-spec.js +++ b/specs/is-dom-component-spec.js @@ -2,22 +2,18 @@ import {isDOMComponent} from '../src'; import {createRenderer} from 'react-addons-test-utils'; import React from 'react'; -class OtherComponent extends React.Component { - render() { - return ( -
- ); - } +function OtherComponent() { + return ( +
+ ); } -class Test extends React.Component { - render() { - return ( -
- -
- ); - } +function Test() { + return ( +
+ +
+ ); } describe('`isDOMComponent`', function() { diff --git a/src/find-all-with-prop.js b/src/find-all-with-prop.js new file mode 100644 index 0000000..6045f62 --- /dev/null +++ b/src/find-all-with-prop.js @@ -0,0 +1,25 @@ +import isEqual from 'lodash.isequal'; +import findAll from './find-all'; +import get from 'lodash.get'; +import React from 'react'; + +/** + * Finds all instances of components in the tree with a prop found at the + * `propPath` that matches `propValue`. + * + * @param {ReactComponent} tree the rendered tree to traverse + * @param {String} propPath the prop path to find + * @param {*} propValue the prop value to find + * @return {Array} all matching components + */ +export default function findAllWithProp(tree, propPath, propValue) { + return findAll(tree, component => { + if (React.isValidElement(component)) { + const value = get(component.props, propPath); + + return isEqual(value, propValue); + } + + return false; + }); +} diff --git a/src/find-with-class.js b/src/find-with-class.js index 23f4080..fe267a2 100644 --- a/src/find-with-class.js +++ b/src/find-with-class.js @@ -7,6 +7,7 @@ import findAllWithClass from './find-all-with-class'; * This is different to React's `findRenderedDOMComponentWithClass` in that * it will check *all* components and not just DOM components. * + * @throws Will throw an error if none or more than one component is found. * @param {ReactComponent} tree the rendered tree to traverse * @param {String} className the class to find * @return {ReactComponent} the matching component diff --git a/src/find-with-prop.js b/src/find-with-prop.js new file mode 100644 index 0000000..343b5d5 --- /dev/null +++ b/src/find-with-prop.js @@ -0,0 +1,21 @@ +import findAllWithProp from './find-all-with-prop'; + +/** + * Find only one instance of a component in the tree with a prop found at the + * `propPath` that matches `propValue`. + * + * @throws Will throw an error if none or more than one component is found. + * @param {ReactComponent} tree the rendered tree to traverse + * @param {String} propPath the prop path to find + * @param {*} propValue the prop value to find + * @return {ReactComponent} the matching component + */ +export default function findWithProp(root, propPath, propValue) { + const found = findAllWithProp(root, propPath, propValue); + + if (found.length !== 1) { + throw new Error(`Did not find exactly once match for prop: ${propPath}, with value: ${propValue}`); + } + + return found[0]; +} diff --git a/src/find-with-ref.js b/src/find-with-ref.js index e03cf12..3e444e1 100644 --- a/src/find-with-ref.js +++ b/src/find-with-ref.js @@ -5,6 +5,7 @@ import findAll from './find-all'; * Finds component in the tree with a ref that matches * `ref`. * + * @throws Will throw an error if none or more than one component is found. * @param {ReactComponent} tree the rendered tree to traverse * @param {String} ref to find * @return {ReactComponent} found component diff --git a/src/find-with-type.js b/src/find-with-type.js index bfea626..8642e85 100644 --- a/src/find-with-type.js +++ b/src/find-with-type.js @@ -8,6 +8,7 @@ import findAllWithType from './find-all-with-type'; * `findRenderedComponentWithType` as you can supply a component class or a * DOM tag. * + * @throws Will throw an error if none or more than one component is found. * @param {ReactComponent} tree the rendered tree to traverse * @param {Function|String} type the component type or tag to find * @return {ReactComponent} the matching component diff --git a/src/index.js b/src/index.js index bdddcd0..88deaa7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,9 @@ export findAll from './find-all'; export findAllWithClass from './find-all-with-class'; +export findAllWithProp from './find-all-with-prop'; export findAllWithType from './find-all-with-type'; export findWithClass from './find-with-class'; +export findWithProp from './find-with-prop'; export findWithRef from './find-with-ref'; export findWithType from './find-with-type'; export getMountedInstance from './get-mounted-instance';