Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an isEmptyRender() method to both ShallowWrapper and ReactWrapper #339

Merged
merged 5 commits into from
May 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
instEqual,
treeFilter,
getNode,
internalInstanceOrComponent,
} from './MountedTraversal';
import {
renderWithOptions,
Expand All @@ -30,6 +31,7 @@ import {
import {
debugInsts,
} from './Debug';
import { REACT15 } from './version';

/**
* Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
Expand Down Expand Up @@ -388,6 +390,24 @@ export default class ReactWrapper {
return this.single(n => predicate(n));
}

/**
* Returns true if the component rendered nothing, i.e., null or false.
*
* @returns {boolean}
*/
isEmptyRender() {
return this.single((n) => {
// Stateful components and stateless function components have different internal structures,
// so we need to find the correct internal instance, and validate the rendered node type
// equals 2, which is the `ReactNodeTypes.EMPTY` value.
if (REACT15) {
return internalInstanceOrComponent(n)._renderedNodeType === 2;
}

return findDOMNode(n) === null;
});
}

/**
* Returns a new wrapper instance with only the nodes of the current wrapper instance that match
* the provided predicate function.
Expand Down
9 changes: 9 additions & 0 deletions src/ShallowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,15 @@ export default class ShallowWrapper {
return this.single(predicate);
}

/**
* Returns true if the component rendered nothing, i.e., null or false.
*
* @returns {boolean}
*/
isEmptyRender() {
return this.type() === null;
}

/**
* Returns a new wrapper instance with only the nodes of the current wrapper instance that match
* the provided predicate function. The predicate should receive a wrapped node as its first
Expand Down
43 changes: 41 additions & 2 deletions test/ReactWrapper-spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describeWithDOM, describeIf } from './_helpers';
import { describeWithDOM, describeIf, itWithData, generateEmptyRenderData } from './_helpers';
import React from 'react';
import { expect } from 'chai';
import {
Expand All @@ -7,7 +7,7 @@ import {
ReactWrapper,
} from '../src/';
import sinon from 'sinon';
import { REACT013 } from '../src/version';
import { REACT013, REACT15 } from '../src/version';

describeWithDOM('mount', () => {

Expand Down Expand Up @@ -1240,6 +1240,45 @@ describeWithDOM('mount', () => {
});
});

describe('.isEmptyRender()', () => {
const emptyRenderValues = generateEmptyRenderData();

itWithData(emptyRenderValues, 'when a React class component returns: ', (data) => {
const Foo = React.createClass({
render() {
return data.value;
},
});
const wrapper = mount(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});

itWithData(emptyRenderValues, 'when an ES2015 class component returns: ', (data) => {
class Foo extends React.Component {
render() {
return data.value;
}
}
const wrapper = mount(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});

it('should not return true for HTML elements', () => {
const wrapper = mount(<div className="bar baz" />);
expect(wrapper.isEmptyRender()).to.equal(false);
});

describeIf(REACT15, 'stateless function components', () => {
itWithData(emptyRenderValues, 'when a component returns: ', (data) => {
function Foo() {
return data.value;
}
const wrapper = mount(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});
});
});

describe('.not(selector)', () => {
it('filters to things not matching a selector', () => {
const wrapper = mount(
Expand Down
43 changes: 41 additions & 2 deletions test/ShallowWrapper-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';
import { expect } from 'chai';
import { shallow, render, ShallowWrapper } from '../src/';
import sinon from 'sinon';
import { describeIf } from './_helpers';
import { REACT013 } from '../src/version';
import { describeIf, itWithData, generateEmptyRenderData } from './_helpers';
import { REACT013, REACT15 } from '../src/version';

describe('shallow', () => {

Expand Down Expand Up @@ -1127,6 +1127,45 @@ describe('shallow', () => {
});
});

describe('.isEmptyRender()', () => {
const emptyRenderValues = generateEmptyRenderData();

itWithData(emptyRenderValues, 'when a React class component returns: ', (data) => {
const Foo = React.createClass({
render() {
return data.value;
},
});
const wrapper = shallow(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});

itWithData(emptyRenderValues, 'when an ES2015 class component returns: ', (data) => {
class Foo extends React.Component {
render() {
return data.value;
}
}
const wrapper = shallow(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});

it('should not return true for HTML elements', () => {
const wrapper = shallow(<div className="bar baz" />);
expect(wrapper.isEmptyRender()).to.equal(false);
});

describeIf(REACT15, 'stateless function components', () => {
itWithData(emptyRenderValues, 'when a component returns: ', (data) => {
function Foo() {
return data.value;
}
const wrapper = shallow(<Foo />);
expect(wrapper.isEmptyRender()).to.equal(data.expectResponse);
});
});
});

describe('.not(selector)', () => {
it('filters to things not matching a selector', () => {
const wrapper = shallow(
Expand Down
38 changes: 38 additions & 0 deletions test/_helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* globals jsdom */

import React from 'react';

/**
* Simple wrapper around mocha describe which allows a boolean to be passed in first which
* determines whether or not the test will be run
Expand All @@ -24,6 +26,20 @@ export function itIf(test, a, b) {
}
}

/**
* Simple wrapper around mocha it which allows an array of possible values to test against.
* Each test will be wrapped in a try/catch block to handle any errors.
*
* @param {Object[]} data
* @param {String} message
* @param {Function} factory
*/
export function itWithData(data, message, factory) {
data.forEach((testCase) => {
it(`${message} ${testCase.message}`, () => factory(testCase));
});
}

function only(a, b) {
describe('(uses jsdom)', () => {
if (typeof jsdom === 'function') {
Expand Down Expand Up @@ -62,3 +78,25 @@ export function describeWithDOM(a, b) {
describeWithDOM.only = only;
describeWithDOM.skip = skip;

/**
* React component used for testing.
*/
class TestHelper extends React.Component {
render() {
return <div />;
}
}

/**
* Possible values for React render() checks.
*/
export function generateEmptyRenderData() {
return [
// Returns true for empty
{ message: 'false', value: false, expectResponse: true },
{ message: 'null', value: null, expectResponse: true },
// Returns false for empty, valid returns
{ message: 'React component', value: <TestHelper />, expectResponse: false },
{ message: 'React element', value: <span />, expectResponse: false },
];
}