diff --git a/docs/api/ReactWrapper/find.md b/docs/api/ReactWrapper/find.md index 223bfa2e7..0417b5e7a 100644 --- a/docs/api/ReactWrapper/find.md +++ b/docs/api/ReactWrapper/find.md @@ -47,7 +47,11 @@ const wrapper = mount(); expect(wrapper.find('Foo')).to.have.length(1); ``` - +Object Property Selector: +```jsx +const wrapper = mount(); +expect(wrapper.find({prop: 'value'})).to.have.length(1); +``` #### Related Methods diff --git a/docs/api/ShallowWrapper/find.md b/docs/api/ShallowWrapper/find.md index 635602de1..85dd30579 100644 --- a/docs/api/ShallowWrapper/find.md +++ b/docs/api/ShallowWrapper/find.md @@ -44,6 +44,11 @@ const wrapper = shallow(); expect(wrapper.find('Foo')).to.have.length(1); ``` +Object Property Selector: +```jsx +const wrapper = shallow(); +expect(wrapper.find({prop: 'value'})).to.have.length(1); +``` #### Related Methods diff --git a/docs/api/selector.md b/docs/api/selector.md index 1eb357396..5d11ae734 100644 --- a/docs/api/selector.md +++ b/docs/api/selector.md @@ -6,7 +6,7 @@ one of the following three categories: ### 1. A Valid CSS Selector -Enzyme supports a subset of valid CSS selectors to find nodes inside a render tree. Support is as +Enzyme supports a subset of valid CSS selectors to find nodes inside a render tree. Support is as follows: - class syntax (`.foo`, `.foo-bar`, etc.) @@ -92,6 +92,25 @@ MyComponent.displayName = 'MyComponent'; const myComponents = wrapper.find('MyComponent'); ``` -NOTE: This will *only* work if the selector (and thus the component's `displayName`) is a string +NOTE: This will *only* work if the selector (and thus the component's `displayName`) is a string starting with a capital letter. Strings starting with lower case letters will assume it is a CSS selector using the tag syntax. + + + +### 4. Object Property Selector + +Enzyme allows you to find components and nodes based on a subset of their properties: + + +```jsx +const wrapper = mount( +
+ +
+) + +wrapper.find({ foo: 3 }) +wrapper.find({ bar: false }) +wrapper.find({ title: 'baz'}) +``` diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index 54bea0128..28e0a278f 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -1,3 +1,4 @@ +import { isArray, isNull, isMatch, isEmpty } from 'underscore'; import { coercePropValue, nodeEqual, @@ -177,6 +178,12 @@ export function parentsOfInst(inst, root) { return pathToNode(inst, root).reverse(); } +export function instHasProps(inst, props) { + if (!isDOMComponent(inst)) return false; + const node = getNode(inst); + return !isEmpty(props) && isMatch(propsOfNode(node), props); +} + export function buildInstPredicate(selector) { switch (typeof selector) { case 'function': @@ -207,8 +214,14 @@ export function buildInstPredicate(selector) { } break; + case 'object': + if (!isArray(selector) && !isNull(selector)) { + return node => instHasProps(node, selector); + } + throw new TypeError('An array or null is not supported as a selector'); + default: - throw new TypeError('Expecting a string or Component Constructor'); + throw new TypeError('Expecting a string, object, or Component Constructor'); } } diff --git a/src/ShallowTraversal.js b/src/ShallowTraversal.js index e634c2d2e..ed8dc1855 100644 --- a/src/ShallowTraversal.js +++ b/src/ShallowTraversal.js @@ -1,4 +1,5 @@ import React from 'react'; +import { isArray, isNull, isMatch, isEmpty } from 'underscore'; import { coercePropValue, propsOfNode, @@ -95,6 +96,10 @@ export function nodeHasType(node, type) { return node.type.name === type || node.type.displayName === type; } +export function nodeHasProps(node, props) { + return !isEmpty(props) && isMatch(propsOfNode(node), props); +} + export function buildPredicate(selector) { switch (typeof selector) { case 'function': @@ -127,9 +132,14 @@ export function buildPredicate(selector) { } break; + case 'object': + if (!isArray(selector) && !isNull(selector)) { + return node => nodeHasProps(node, selector); + } + throw new TypeError('An array or null is not supported as a selector'); default: - throw new TypeError('Expecting a string or Component Constructor'); + throw new TypeError('Expecting a string, object, or Component Constructor'); } } diff --git a/src/__tests__/ReactWrapper-spec.js b/src/__tests__/ReactWrapper-spec.js index 39a216318..4d550d407 100644 --- a/src/__tests__/ReactWrapper-spec.js +++ b/src/__tests__/ReactWrapper-spec.js @@ -261,6 +261,61 @@ describeWithDOM('mount', () => { expect(() => wrapper.find('.foo .foo')).to.throw(Error); }); + it('should support object property selectors', () => { + const wrapper = mount( +
+ + +