diff --git a/docs/README.md b/docs/README.md index d32799eda..48e9fa6ad 100644 --- a/docs/README.md +++ b/docs/README.md @@ -43,6 +43,7 @@ * [last()](/docs/api/ShallowWrapper/last.md) * [map(fn)](/docs/api/ShallowWrapper/map.md) * [matchesElement(node)](/docs/api/ShallowWrapper/matchesElement.md) + * [name()](/docs/api/ShallowWrapper/name.md) * [not(selector)](/docs/api/ShallowWrapper/not.md) * [parent()](/docs/api/ShallowWrapper/parent.md) * [parents()](/docs/api/ShallowWrapper/parents.md) @@ -93,6 +94,7 @@ * [map(fn)](/docs/api/ReactWrapper/map.md) * [matchesElement(node)](/docs/api/ReactWrapper/matchesElement.md) * [mount()](/docs/api/ReactWrapper/mount.md) + * [name()](/docs/api/ReactWrapper/name.md) * [not(selector)](/docs/api/ReactWrapper/not.md) * [parent()](/docs/api/ReactWrapper/parent.md) * [parents()](/docs/api/ReactWrapper/parents.md) diff --git a/docs/api/ReactWrapper/name.md b/docs/api/ReactWrapper/name.md new file mode 100644 index 000000000..18a4e7f44 --- /dev/null +++ b/docs/api/ReactWrapper/name.md @@ -0,0 +1,34 @@ +# `.name() => String|null` + +Returns the name of the current node of this wrapper. If it's a composite component, this will be +the name of the component. If it's native DOM node, it will be a string of the tag name. If it's +`null`, it will be `null`. + +The order of precedence on returning the name is: `type.displayName` -> `type.name` -> `type`. + +Note: can only be called on a wrapper of a single node. + + +#### Returns + +`String|null`: The name of the current node + + + +#### Examples + +```jsx +const wrapper = mount(
); +expect(wrapper.name()).to.equal('div'); +``` + +```jsx +const wrapper = mount(); +expect(wrapper.name()).to.equal('Foo'); +``` + +```jsx +Foo.displayName = 'A cool custom name'; +const wrapper = mount(); +expect(wrapper.name()).to.equal('A cool custom name'); +``` diff --git a/docs/api/ShallowWrapper/name.md b/docs/api/ShallowWrapper/name.md new file mode 100644 index 000000000..943708660 --- /dev/null +++ b/docs/api/ShallowWrapper/name.md @@ -0,0 +1,36 @@ +# `.name() => String|null` + +Returns the name of the current node of this wrapper. If it's a composite component, this will be +the name of the top-most rendered component. If it's native DOM node, it will be a string of the +tag name. If it's `null`, it will be `null`. + +The order of precedence on returning the name is: `type.displayName` -> `type.name` -> `type`. + +Note: can only be called on a wrapper of a single node. + + +#### Returns + +`String|null`: The name of the current node + + + +#### Examples + +```jsx +const wrapper = shallow(
); +expect(wrapper.name()).to.equal('div'); +``` + +```jsx +const SomeWrappingComponent = () => ; +const wrapper = shallow(); +expect(wrapper.name()).to.equal('Foo'); +``` + +```jsx +Foo.displayName = 'A cool custom name'; +const SomeWrappingComponent = () => ; +const wrapper = shallow(); +expect(wrapper.name()).to.equal('A cool custom name'); +``` diff --git a/docs/api/mount.md b/docs/api/mount.md index 0c9b9e7f7..b08c5bf5c 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -171,6 +171,9 @@ Returns a string representation of the current render tree for debugging purpose #### [`.type() => String|Function`](ReactWrapper/type.md) Returns the type of the current node of the wrapper. +#### [`.name() => String`](ReactWrapper/name.md) +Returns the name of the current node of the wrapper. + #### [`.forEach(fn) => ReactWrapper`](ReactWrapper/forEach.md) Iterates through each node of the current wrapper and executes the provided function diff --git a/docs/api/shallow.md b/docs/api/shallow.md index b1aa8f4c4..45414f718 100644 --- a/docs/api/shallow.md +++ b/docs/api/shallow.md @@ -175,6 +175,9 @@ Returns a string representation of the current shallow render tree for debugging #### [`.type() => String|Function`](ShallowWrapper/type.md) Returns the type of the current node of the wrapper. +#### [`.name() => String`](ShallowWrapper/name.md) +Returns the name of the current node of the wrapper. + #### [`.forEach(fn) => ShallowWrapper`](ShallowWrapper/forEach.md) Iterates through each node of the current wrapper and executes the provided function diff --git a/src/ReactWrapper.js b/src/ReactWrapper.js index 1db05fda9..52c03d139 100644 --- a/src/ReactWrapper.js +++ b/src/ReactWrapper.js @@ -25,6 +25,7 @@ import { containsChildrenSubArray, propsOfNode, typeOfNode, + displayNameOfNode, } from './Utils'; import { debugInsts, @@ -616,6 +617,17 @@ export default class ReactWrapper { return this.single(n => typeOfNode(getNode(n))); } + /** + * Returns the name of the root node of this wrapper. + * + * In order of precedence => type.displayName -> type.name -> type. + * + * @returns {String} + */ + name() { + return this.single(n => displayNameOfNode(getNode(n))); + } + /** * Returns whether or not the current root node has the given class name or not. * diff --git a/src/ShallowWrapper.js b/src/ShallowWrapper.js index 384727479..c548b9c98 100644 --- a/src/ShallowWrapper.js +++ b/src/ShallowWrapper.js @@ -12,6 +12,7 @@ import { propsOfNode, typeOfNode, isReactElementAlike, + displayNameOfNode, } from './Utils'; import { debugNodes, @@ -612,6 +613,17 @@ export default class ShallowWrapper { return this.single(typeOfNode); } + /** + * Returns the name of the root node of this wrapper. + * + * In order of precedence => type.displayName -> type.name -> type. + * + * @returns {String} + */ + name() { + return this.single(displayNameOfNode); + } + /** * Returns whether or not the current root node has the given class name or not. * diff --git a/src/Utils.js b/src/Utils.js index 8cb93b116..3f3956a8e 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -276,3 +276,11 @@ export function mapNativeEventNames(event) { return nativeToReactEventMap[event] || event; } + +export function displayNameOfNode(node) { + const { type } = node; + + if (!type) return null; + + return type.displayName || type.name || type; +} diff --git a/test/ReactWrapper-spec.js b/test/ReactWrapper-spec.js index d40fe1275..b1137bbc4 100644 --- a/test/ReactWrapper-spec.js +++ b/test/ReactWrapper-spec.js @@ -2187,4 +2187,88 @@ describeWithDOM('mount', () => { expect(spy2.callCount).to.equal(0); }); }); + + describe('.name()', () => { + describe('node with displayName', () => { + it('should return the displayName of the node', () => { + class Foo extends React.Component { + render() { return
; } + } + + Foo.displayName = 'CustomWrapper'; + + const wrapper = mount(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + + describeIf(!REACT013, 'stateless function components', () => { + it('should return the name of the node', () => { + function SFC() { + return
; + } + + SFC.displayName = 'CustomWrapper'; + + const wrapper = mount(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + }); + + describe('React.createClass', () => { + it('should return the name of the node', () => { + const Foo = React.createClass({ + displayName: 'CustomWrapper', + render() { + return
; + }, + }); + + const wrapper = mount(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + }); + }); + + describe('node without displayName', () => { + it('should return the name of the node', () => { + class Foo extends React.Component { + render() { return
; } + } + + const wrapper = mount(); + expect(wrapper.name()).to.equal('Foo'); + }); + + describeIf(!REACT013, 'stateless function components', () => { + it('should return the name of the node', () => { + function SFC() { + return
; + } + + const wrapper = mount(); + expect(wrapper.name()).to.equal('SFC'); + }); + }); + + describe('React.createClass', () => { + it('should return the name of the node', () => { + const Foo = React.createClass({ + render() { + return
; + }, + }); + + const wrapper = mount(); + expect(wrapper.name()).to.equal('Foo'); + }); + }); + }); + + describe('DOM node', () => { + it('should return the name of the node', () => { + const wrapper = mount(
); + expect(wrapper.name()).to.equal('div'); + }); + }); + }); }); diff --git a/test/ShallowWrapper-spec.js b/test/ShallowWrapper-spec.js index f781c0787..46e36ddc9 100644 --- a/test/ShallowWrapper-spec.js +++ b/test/ShallowWrapper-spec.js @@ -2539,4 +2539,108 @@ describe('shallow', () => { expect(spy2.callCount).to.equal(0); }); }); + describe('.name()', () => { + describe('node with displayName', () => { + it('should return the displayName of the node', () => { + class Foo extends React.Component { + render() { return
; } + } + + class Wrapper extends React.Component { + render() { return ; } + } + + Foo.displayName = 'CustomWrapper'; + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + + describeIf(!REACT013, 'stateless function components', () => { + it('should return the name of the node', () => { + function SFC() { + return
; + } + const Wrapper = () => ; + + SFC.displayName = 'CustomWrapper'; + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + }); + + describe('React.createClass', () => { + it('should return the name of the node', () => { + const Foo = React.createClass({ + displayName: 'CustomWrapper', + render() { + return
; + }, + }); + const Wrapper = React.createClass({ + render() { + return ; + }, + }); + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('CustomWrapper'); + }); + }); + }); + + describe('node without displayName', () => { + it('should return the name of the node', () => { + class Foo extends React.Component { + render() { return
; } + } + + class Wrapper extends React.Component { + render() { return ; } + } + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('Foo'); + }); + + describeIf(!REACT013, 'stateless function components', () => { + it('should return the name of the node', () => { + function SFC() { + return
; + } + const Wrapper = () => ; + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('SFC'); + }); + }); + + describe('React.createClass', () => { + it('should return the name of the node', () => { + const Foo = React.createClass({ + render() { + return
; + }, + }); + const Wrapper = React.createClass({ + render() { + return ; + }, + }); + + const wrapper = shallow(); + expect(wrapper.name()).to.equal('Foo'); + }); + }); + }); + + describe('DOM node', () => { + it('should return the name of the node', () => { + const wrapper = shallow(
); + expect(wrapper.name()).to.equal('div'); + }); + }); + }); + }); diff --git a/test/Utils-spec.js b/test/Utils-spec.js index c4b8c95e3..22a61e71f 100644 --- a/test/Utils-spec.js +++ b/test/Utils-spec.js @@ -11,6 +11,7 @@ import { SELECTOR, selectorType, mapNativeEventNames, + displayNameOfNode, } from '../src/Utils'; describe('Utils', () => { @@ -240,4 +241,34 @@ describe('Utils', () => { }); }); + describe('displayNameOfNode', () => { + describe('given a node with displayName', () => { + it('should return the displayName', () => { + class Foo extends React.Component { + render() { return
; } + } + + Foo.displayName = 'CustomWrapper'; + + expect(displayNameOfNode()).to.equal('CustomWrapper'); + }); + }); + + describe('given a node without displayName', () => { + it('should return the name', () => { + class Foo extends React.Component { + render() { return
; } + } + + expect(displayNameOfNode()).to.equal('Foo'); + }); + }); + + describe('given a DOM node', () => { + it('should return the type', () => { + expect(displayNameOfNode(
)).to.equal('div'); + }); + }); + }); + });