diff --git a/src/core/__tests__/ReactMultiChildText-test.js b/src/core/__tests__/ReactMultiChildText-test.js
index 3ecef8ed7820f..51a0cffc7a938 100644
--- a/src/core/__tests__/ReactMultiChildText-test.js
+++ b/src/core/__tests__/ReactMultiChildText-test.js
@@ -28,65 +28,74 @@ var ReactTestUtils = require('ReactTestUtils');
var reactComponentExpect = require('reactComponentExpect');
-var assertNodeText = function(instance, text) {
- expect(instance.getDOMNode().childNodes.length).toBe(1);
- expect(instance.getDOMNode().innerHTML).toBe('' + text);
-};
+// Helpers
+var testAllPermutations = function(testCases) {
+ for (var i = 0; i < testCases.length; i += 2) {
+ var renderWithChildren = testCases[i];
+ var expectedResultAfterRender = testCases[i + 1];
-var assertEmptyNode = function(instance) {
- expect(instance.getDOMNode().childNodes.length).toBe(0);
-};
+ for (var j = 0; j < testCases.length; j += 2) {
+ var updateWithChildren = testCases[j];
+ var expectedResultAfterUpdate = testCases[j + 1];
-var assertMultiChild = function(instance, textOne, textTwo) {
- expect(instance.getDOMNode().childNodes.length).toBe(2);
- var firstTextDOMNode =
- reactComponentExpect(instance)
- .expectRenderedChildAt(0)
- .toBeTextComponent()
- .instance()
- .getDOMNode();
- expect(firstTextDOMNode.childNodes.length).toBe(textOne === '' ? 0 : 1);
- expect(firstTextDOMNode.innerHTML).toBe('' + textOne);
-
- var secondTextDOMNode =
- reactComponentExpect(instance)
- .expectRenderedChildAt(1)
- .toBeTextComponent()
- .instance()
- .getDOMNode();
- expect(secondTextDOMNode.childNodes.length).toBe(textTwo === '' ? 0 : 1);
- expect(secondTextDOMNode.innerHTML).toBe('' + textTwo);
-};
+ var d = renderChildren(renderWithChildren);
+ expectChildren(d, expectedResultAfterRender);
-var assertSingleChild = function(instance, text) {
- expect(instance.getDOMNode().childNodes.length).toBe(1);
- var textDOMNode =
- reactComponentExpect(instance)
- .expectRenderedChildAt(0)
- .toBeTextComponent()
- .instance()
- .getDOMNode();
- expect(textDOMNode.childNodes.length).toBe(1);
- expect(textDOMNode.innerHTML).toBe('' + text);
+ updateChildren(d, updateWithChildren);
+ expectChildren(d, expectedResultAfterUpdate);
+ }
+ }
};
-// Helpers
-var renderSingleTextChild = function(text) {
- var d = ReactTestUtils.renderIntoDocument(
{text}
);
- return d;
+var renderChildren = function(children) {
+ return ReactTestUtils.renderIntoDocument(
+ React.DOM.div({children: children})
+ );
};
-var renderMultipleTextChildren = function(textOne, textTwo) {
- var d = ReactTestUtils.renderIntoDocument({textOne}{textTwo}
);
- return d;
+
+var updateChildren = function(d, children) {
+ d.replaceProps({children: children});
};
-var TestCompositeComponent = React.createClass({
- render: function() {
- return (
-
- );
+var expectChildren = function(d, children) {
+ if (typeof children === 'string') {
+ var textNode = d.getDOMNode().firstChild;
+
+ if (children === '') {
+ expect(textNode != null).toBe(false);
+ } else {
+ expect(textNode != null).toBe(true);
+ expect(textNode.nodeType).toBe(3);
+ expect(textNode.data).toBe('' + children);
+ }
+ } else {
+ expect(d.getDOMNode().childNodes.length).toBe(children.length);
+
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+
+ if (typeof child === 'string') {
+ var textWrapperNode =
+ reactComponentExpect(d)
+ .expectRenderedChildAt(i)
+ .toBeTextComponent()
+ .instance();
+
+ expectChildren(textWrapperNode, child);
+ } else {
+ var elementDOMNode =
+ reactComponentExpect(d)
+ .expectRenderedChildAt(i)
+ .toBeComponentOfType(React.DOM.div)
+ .instance()
+ .getDOMNode();
+
+ expect(elementDOMNode.tagName).toBe('DIV');
+ }
+ }
}
-});
+};
+
/**
* ReactMultiChild DOM integration test. In ReactDOM components, we make sure
@@ -94,190 +103,107 @@ var TestCompositeComponent = React.createClass({
* faster to render and update.
*/
describe('ReactMultiChildText', function() {
- it('should render null as empty', function() {
- var d = renderSingleTextChild(null);
- // false should act exactly as a null child
- assertEmptyNode(d);
- });
-
- it('should render undefined as empty', function() {
- var d = renderSingleTextChild(undefined);
- // false should act exactly as a null child
- assertEmptyNode(d);
- });
-
- it('should render null as empty then switch to text node', function() {
- var d = renderSingleTextChild(null);
- // false should act exactly as a null child
- assertEmptyNode(d);
- d.replaceProps({children: 'hello'});
- assertNodeText(d, 'hello');
- });
-
- it('should render undefined as empty then switch to text node', function() {
- var d = renderSingleTextChild(undefined);
- // false should act exactly as a null child
- assertEmptyNode(d);
- d.replaceProps({children: 'hello'});
- assertNodeText(d, 'hello');
- });
-
- it('should render null as empty then switch to span children', function() {
- var d = renderSingleTextChild(null);
- // false should act exactly as a null child
- assertEmptyNode(d);
- d.replaceProps({children: ['hello', 'goodbye']});
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render null as empty then switch to span children', function() {
- var d = renderSingleTextChild(undefined);
- // false should act exactly as a null child
- assertEmptyNode(d);
- d.replaceProps({children: ['hello', 'goodbye']});
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render zero string as text node then switch to spans', function() {
- var d = renderSingleTextChild('0');
- // false should act exactly as a null child
- assertNodeText(d, '0');
- d.replaceProps({children: ['hello', 'goodbye']});
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render zero number as text node then switch to spans', function() {
- var d = renderSingleTextChild(0);
- // false should act exactly as a null child
- assertNodeText(d, '0');
- d.replaceProps({children: ['hello', 'goodbye']});
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render a single text child to a single text node', function() {
- var d = renderSingleTextChild('hello');
- assertNodeText(d, 'hello');
- });
-
- it('should render two string children to two spans', function() {
- var d = renderMultipleTextChildren('hello', 'goodbye');
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render false as a null child', function() {
- var d = renderMultipleTextChildren(false, 234.2);
- // false should act exactly as a null child
- assertSingleChild(d, '234.2');
- });
-
- it('should render true as a null child', function() {
- var d = renderMultipleTextChildren(true, 234.2);
- // false should act exactly as a null child
- assertSingleChild(d, '234.2');
- });
-
- it('should render true as a null child', function() {
- var d = renderMultipleTextChildren(true, 234.2);
- // false should act exactly as a null child
- assertSingleChild(d, '234.2');
- });
-
- it('should render one true as no children', function() {
- var d = renderSingleTextChild(true);
- assertEmptyNode(d);
- });
-
- it('should render one false as no children', function() {
- var d = renderSingleTextChild(false);
- assertEmptyNode(d);
- });
-
- it('should render empty string as no children', function() {
- var d = renderSingleTextChild('');
- assertEmptyNode(d);
- });
-
- it('should render two empty strings as two empty spans', function() {
- var d = renderMultipleTextChildren('', '');
- assertMultiChild(d, '', '');
- });
-
- it('should render empty string and string as two spans', function() {
- var d = renderMultipleTextChildren('', 'yo');
- assertMultiChild(d, '', 'yo');
- });
-
- it('should render child string zero as text node', function() {
- var d = renderSingleTextChild('0');
- // false should act exactly as a null child
- assertNodeText(d, '0');
- });
-
- it('should render child number zero as text node', function() {
- var d = renderSingleTextChild(0);
- // false should act exactly as a null child
- assertNodeText(d, '0');
- });
-
- it('should render content string zero as text node', function() {
- var d = renderSingleTextChild('0');
- // false should act exactly as a null child
- assertNodeText(d, '0');
- });
-
- it('should render zero string as string child', function() {
- var d = renderMultipleTextChildren('0', 234.2);
- // false should act exactly as a null child
- assertMultiChild(d, '0', '234.2');
- });
-
- it('should render zero string as string child then text node', function() {
- var d = renderMultipleTextChildren('0', 234.2);
- // false should act exactly as a null child
- assertMultiChild(d, '0', '234.2');
- d.replaceProps({children: '0'});
- assertNodeText(d, '0');
- });
-
- it('should render zero number as string child then text node', function() {
- var d = renderMultipleTextChildren(0, 234.2);
- // false should act exactly as a null child
- assertMultiChild(d, '0', '234.2');
- d.replaceProps({children: 0});
- // BELOW REVEALS A BUG IN JSDOM
- // assertNodeText(d, '0'); // This works in the browser.
- });
-
- it('should render multiple children then switch to inline', function() {
- var d = renderMultipleTextChildren('hello', 'goodbye');
- assertMultiChild(d, 'hello', 'goodbye');
- d.replaceProps({children: 'hello'});
- assertNodeText(d, 'hello');
- });
-
- it('should render multiple children then switch to inline child', function() {
- var d = renderMultipleTextChildren('hello', 'goodbye');
- assertMultiChild(d, 'hello', 'goodbye');
- // Even when switching from content to a single child, it should render
- // that single child as inline content.
- d.replaceProps({children: 'hello'});
- assertNodeText(d, 'hello');
- });
-
- it('should render inline child, then switch to text components ', function() {
- var d = renderSingleTextChild('hello');
- assertNodeText(d, 'hello');
- d.replaceProps({children: ['hello', 'goodbye']});
- assertMultiChild(d, 'hello', 'goodbye');
- });
-
- it('should render inline child, then switch to composite', function() {
- var d = renderSingleTextChild('hello');
- assertNodeText(d, 'hello');
- d.replaceProps({children: });
- reactComponentExpect(d)
- .expectRenderedChildAt(0)
- .toBeCompositeComponentWithType(TestCompositeComponent);
+ it('should correctly handle all possible children for render and update', function() {
+ testAllPermutations([
+ // basic values
+ undefined, [],
+ null, [],
+ false, [],
+ true, [],
+ 0, '0',
+ 1.2, '1.2',
+ '', '',
+ 'foo', 'foo',
+
+ [], [],
+ [undefined], [],
+ [null], [],
+ [false], [],
+ [true], [],
+ [0], ['0'],
+ [1.2], ['1.2'],
+ [''], [''],
+ ['foo'], ['foo'],
+ [], [],
+
+ // two adjacent values
+ [true, 0], ['0'],
+ [0, 0], ['0', '0'],
+ [1.2, 0], ['1.2', '0'],
+ [0, ''], ['0', ''],
+ ['foo', 0], ['foo', '0'],
+ [0, ], ['0', ],
+
+ [true, 1.2], ['1.2'],
+ [1.2, 0], ['1.2', '0'],
+ [1.2, 1.2], ['1.2', '1.2'],
+ [1.2, ''], ['1.2', ''],
+ ['foo', 1.2], ['foo', '1.2'],
+ [1.2, ], ['1.2', ],
+
+ [true, ''], [''],
+ ['', 0], ['', '0'],
+ [1.2, ''], ['1.2', ''],
+ ['', ''], ['', ''],
+ ['foo', ''], ['foo', ''],
+ ['', ], ['', ],
+
+ [true, 'foo'], ['foo'],
+ ['foo', 0], ['foo', '0'],
+ [1.2, 'foo'], ['1.2', 'foo'],
+ ['foo', ''], ['foo', ''],
+ ['foo', 'foo'], ['foo', 'foo'],
+ ['foo', ], ['foo', ],
+
+ // values separated by an element
+ [true, , true], [],
+ [1.2, , 1.2], ['1.2', , '1.2'],
+ ['', , ''], ['', , ''],
+ ['foo', , 'foo'], ['foo', , 'foo'],
+
+ [true, 1.2, , '', 'foo'], ['1.2', , '', 'foo'],
+ [1.2, '', , 'foo', true], ['1.2', '', , 'foo'],
+ ['', 'foo', , true, 1.2], ['', 'foo', , '1.2'],
+
+ [true, 1.2, '', , 'foo', true, 1.2], ['1.2', '', , 'foo', '1.2'],
+ ['', 'foo', true, , 1.2, '', 'foo'], ['', 'foo', , '1.2', '', 'foo'],
+
+ // values inside arrays
+ [[true], [true]], [],
+ [[1.2], [1.2]], ['1.2', '1.2'],
+ [[''], ['']], ['', ''],
+ [['foo'], ['foo']], ['foo', 'foo'],
+ [[], []], [, ],
+
+ [[true, 1.2, ], '', 'foo'], ['1.2', , '', 'foo'],
+ [1.2, '', [, 'foo', true]], ['1.2', '', , 'foo'],
+ ['', ['foo', , true], 1.2], ['', 'foo', , '1.2'],
+
+ [true, [1.2, '', , 'foo'], true, 1.2], ['1.2', '', , 'foo', '1.2'],
+ ['', 'foo', [true, , 1.2, ''], 'foo'], ['', 'foo', , '1.2', '', 'foo'],
+
+ // values inside objects
+ [{a: true}, {a: true}], [],
+ [{a: 1.2}, {a: 1.2}], ['1.2', '1.2'],
+ [{a: ''}, {a: ''}], ['', ''],
+ [{a: 'foo'}, {a: 'foo'}], ['foo', 'foo'],
+ [{a: }, {a: }], [, ],
+
+ [{a: true, b: 1.2, c: }, '', 'foo'], ['1.2', , '', 'foo'],
+ [1.2, '', {a: , b: 'foo', c: true}], ['1.2', '', , 'foo'],
+ ['', {a: 'foo', b: , c: true}, 1.2], ['', 'foo', , '1.2'],
+
+ [true, {a: 1.2, b: '', c: , d: 'foo'}, true, 1.2], ['1.2', '', , 'foo', '1.2'],
+ ['', 'foo', {a: true, b: , c: 1.2, d: ''}, 'foo'], ['', 'foo', , '1.2', '', 'foo'],
+
+ // values inside elements
+ [, '', 'foo'], [, '', 'foo'],
+ [1.2, '', ], ['1.2', '', ],
+ ['', , 1.2], ['', , '1.2'],
+
+ [true, , true, 1.2], [, '1.2'],
+ ['', 'foo', , 'foo'], ['', 'foo', , 'foo']
+ ]);
});
it('should throw if rendering both HTML and children', function() {