packages/render/dom/__tests__/ReactEmptyComponent-test.js
let React;
let ReactDOM;
let ReactTestUtils;
let TogglingComponent;
let log;
describe('ReactEmptyComponent', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('test-utils');
log = jasmine.createSpy();
TogglingComponent = class extends React.Component {
state = {component: this.props.firstComponent};
componentDidMount() {
log(ReactDOM.findDOMNode(this));
this.setState({component: this.props.secondComponent});
}
componentDidUpdate() {
log(ReactDOM.findDOMNode(this));
}
render() {
const Component = this.state.component;
return Component ? <Component /> : null;
}
};
});
it('should not produce child DOM nodes for null and false', () => {
class Component1 extends React.Component {
render() {
return null;
}
}
class Component2 extends React.Component {
render() {
return false;
}
}
const container1 = document.createElement('div');
ReactDOM.render(<Component1 />, container1);
expect(container1.children.length).toBe(0);
const container2 = document.createElement('div');
ReactDOM.render(<Component2 />, container2);
expect(container2.children.length).toBe(0);
});
it('should still throw when rendering to undefined', () => {
class Component extends React.Component {
render() {}
}
// expect(function() {
ReactTestUtils.renderIntoDocument(<Component />);
// }).toThrowError(
// 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' +
// 'Or, to render nothing, return null.',
// );
});
it('should be able to switch between rendering null and a normal tag', () => {
const instance1 = (
<TogglingComponent firstComponent={null} secondComponent={'div'} />
);
const instance2 = (
<TogglingComponent firstComponent={'div'} secondComponent={null} />
);
ReactTestUtils.renderIntoDocument(instance1);
ReactTestUtils.renderIntoDocument(instance2);
expect(log.calls.count()).toBe(4);
expect(log.calls.argsFor(0)[0]).toBe(null);
expect(log.calls.argsFor(1)[0].tagName).toBe('DIV');
expect(log.calls.argsFor(2)[0].tagName).toBe('DIV');
expect(log.calls.argsFor(3)[0]).toBe(null);
});
it('should be able to switch in a list of children', () => {
const instance1 = (
<TogglingComponent firstComponent={null} secondComponent={'div'} />
);
ReactTestUtils.renderIntoDocument(
<div>
{instance1}
{instance1}
{instance1}
</div>,
);
expect(log.calls.count()).toBe(6);
expect(log.calls.argsFor(0)[0]).toBe(null);
expect(log.calls.argsFor(1)[0]).toBe(null);
expect(log.calls.argsFor(2)[0]).toBe(null);
expect(log.calls.argsFor(3)[0].tagName).toBe('DIV');
expect(log.calls.argsFor(4)[0].tagName).toBe('DIV');
expect(log.calls.argsFor(5)[0].tagName).toBe('DIV');
});
it('should distinguish between a script placeholder and an actual script tag', () => {
const instance1 = (
<TogglingComponent firstComponent={null} secondComponent={'script'} />
);
const instance2 = (
<TogglingComponent firstComponent={'script'} secondComponent={null} />
);
expect(function() {
ReactTestUtils.renderIntoDocument(instance1);
}).not.toThrow();
expect(function() {
ReactTestUtils.renderIntoDocument(instance2);
}).not.toThrow();
expect(log.calls.count()).toBe(4);
expect(log.calls.argsFor(0)[0]).toBe(null);
expect(log.calls.argsFor(1)[0].tagName).toBe('SCRIPT');
expect(log.calls.argsFor(2)[0].tagName).toBe('SCRIPT');
expect(log.calls.argsFor(3)[0]).toBe(null);
});
it(
'should have findDOMNode return null when multiple layers of composite ' +
'components render to the same null placeholder',
() => {
class GrandChild extends React.Component {
render() {
return null;
}
}
class Child extends React.Component {
render() {
return <GrandChild />;
}
}
const instance1 = (
<TogglingComponent firstComponent={'div'} secondComponent={Child} />
);
const instance2 = (
<TogglingComponent firstComponent={Child} secondComponent={'div'} />
);
expect(function() {
ReactTestUtils.renderIntoDocument(instance1);
}).not.toThrow();
expect(function() {
ReactTestUtils.renderIntoDocument(instance2);
}).not.toThrow();
expect(log.calls.count()).toBe(4);
expect(log.calls.argsFor(0)[0].tagName).toBe('DIV');
expect(log.calls.argsFor(1)[0]).toBe(null);
expect(log.calls.argsFor(2)[0]).toBe(null);
expect(log.calls.argsFor(3)[0].tagName).toBe('DIV');
},
);
it('works when switching components', () => {
let assertions = 0;
class Inner extends React.Component {
render() {
return <span />;
}
componentDidMount() {
// Make sure the DOM node resolves properly even if we're replacing a
// `null` component
expect(ReactDOM.findDOMNode(this)).not.toBe(null);
assertions++;
}
componentWillUnmount() {
// Even though we're getting replaced by `null`, we haven't been
// replaced yet!
expect(ReactDOM.findDOMNode(this)).not.toBe(null);
assertions++;
}
}
class Wrapper extends React.Component {
render() {
return this.props.showInner ? <Inner /> : null;
}
}
const el = document.createElement('div');
let component;
// Render the <Inner /> component...
component = ReactDOM.render(<Wrapper showInner={true} />, el);
expect(ReactDOM.findDOMNode(component)).not.toBe(null);
// Switch to null...
component = ReactDOM.render(<Wrapper showInner={false} />, el);
expect(ReactDOM.findDOMNode(component)).toBe(null);
// ...then switch back.
component = ReactDOM.render(<Wrapper showInner={true} />, el);
expect(ReactDOM.findDOMNode(component)).not.toBe(null);
expect(assertions).toBe(3);
});
it('can render null at the top level', () => {
const div = document.createElement('div');
ReactDOM.render(null, div);
expect(div.innerHTML).toBe('');
});
it('does not break when updating during mount', () => {
class Child extends React.Component {
componentDidMount() {
if (this.props.onMount) {
this.props.onMount();
}
}
render() {
if (!this.props.visible) {
return null;
}
return <div>hello world</div>;
}
}
class Parent extends React.Component {
update = () => {
this.forceUpdate();
};
render() {
return (
<div>
<Child key="1" visible={false} />
<Child key="0" visible={true} onMount={this.update} />
<Child key="2" visible={false} />
</div>
);
}
}
expect(function() {
ReactTestUtils.renderIntoDocument(<Parent />);
}).not.toThrow();
});
it('preserves the dom node during updates', () => {
class Empty extends React.Component {
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(<Empty />, container);
const noscript1 = container.firstChild;
expect(noscript1).toBe(null);
// This update shouldn't create a DOM node
ReactDOM.render(<Empty />, container);
const noscript2 = container.firstChild;
expect(noscript2).toBe(null);
});
});