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');
+ });
+ });
+ });
+
});