diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index 40120d881..bb0b2eeaa 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -201,15 +201,22 @@ function nodeToHostNode(_node) { while (node && !Array.isArray(node) && node.instance === null) { node = node.rendered; } - if (Array.isArray(node)) { - // TODO(lmr): throw warning regarding not being able to get a host node here - throw new Error('Trying to get host node of an array'); - } // if the SFC returned null effectively, there is no host node. if (!node) { return null; } - return ReactDOM.findDOMNode(node.instance); + + const mapper = (item) => { + if (item && item.instance) return ReactDOM.findDOMNode(item.instance); + return null; + }; + if (Array.isArray(node)) { + return node.map(mapper); + } + if (Array.isArray(node.rendered) && node.nodeType === 'class') { + return node.rendered.map(mapper); + } + return mapper(node); } const eventOptions = { animation: true }; diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 765edc49a..e52982bdf 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -202,15 +202,22 @@ function nodeToHostNode(_node) { while (node && !Array.isArray(node) && node.instance === null) { node = node.rendered; } - if (Array.isArray(node)) { - // TODO(lmr): throw warning regarding not being able to get a host node here - throw new Error('Trying to get host node of an array'); - } // if the SFC returned null effectively, there is no host node. if (!node) { return null; } - return ReactDOM.findDOMNode(node.instance); + + const mapper = (item) => { + if (item && item.instance) return ReactDOM.findDOMNode(item.instance); + return null; + }; + if (Array.isArray(node)) { + return node.map(mapper); + } + if (Array.isArray(node.rendered) && node.nodeType === 'class') { + return node.rendered.map(mapper); + } + return mapper(node); } const eventOptions = { animation: true }; diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index 76a538684..c14a04319 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -219,15 +219,22 @@ function nodeToHostNode(_node) { while (node && !Array.isArray(node) && node.instance === null) { node = node.rendered; } - if (Array.isArray(node)) { - // TODO(lmr): throw warning regarding not being able to get a host node here - throw new Error('Trying to get host node of an array'); - } // if the SFC returned null effectively, there is no host node. if (!node) { return null; } - return ReactDOM.findDOMNode(node.instance); + + const mapper = (item) => { + if (item && item.instance) return ReactDOM.findDOMNode(item.instance); + return null; + }; + if (Array.isArray(node)) { + return node.map(mapper); + } + if (Array.isArray(node.rendered) && node.nodeType === 'class') { + return node.rendered.map(mapper); + } + return mapper(node); } const eventOptions = { animation: true }; diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index abdedb309..e8e1937e2 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -219,15 +219,22 @@ function nodeToHostNode(_node) { while (node && !Array.isArray(node) && node.instance === null) { node = node.rendered; } - if (Array.isArray(node)) { - // TODO(lmr): throw warning regarding not being able to get a host node here - throw new Error('Trying to get host node of an array'); - } // if the SFC returned null effectively, there is no host node. if (!node) { return null; } - return ReactDOM.findDOMNode(node.instance); + + const mapper = (item) => { + if (item && item.instance) return ReactDOM.findDOMNode(item.instance); + return null; + }; + if (Array.isArray(node)) { + return node.map(mapper); + } + if (Array.isArray(node.rendered) && node.nodeType === 'class') { + return node.rendered.map(mapper); + } + return mapper(node); } const eventOptions = { diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index d11f1b805..0bbfbd84c 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -3702,6 +3702,38 @@ describeWithDOM('mount', () => { expect(wrapper.text()).to.equal('{ some text }'); }); }); + + describeIf(is('> 16.2'), 'fragments', () => { + class FragmentClassExample extends React.Component { + render() { + return ( + +
Foo
+
Bar
+
+ ); + } + } + + const FragmentConstExample = () => ( + +
Foo
+
Bar
+
+ ); + + it('correctly gets text for both children for class', () => { + const classWrapper = mount(); + expect(classWrapper.text()).to.include('Foo'); + expect(classWrapper.text()).to.include('Bar'); + }); + + it('correctly gets text for both children for const', () => { + const constWrapper = mount(); + expect(constWrapper.text()).to.include('Foo'); + expect(constWrapper.text()).to.include('Bar'); + }); + }); }); describe('.props()', () => { @@ -5135,6 +5167,36 @@ describeWithDOM('mount', () => { expect(wrapper.find(Foo).html()).to.equal('
'); }); }); + + describeIf(is('>16.2'), 'fragments', () => { + class FragmentClassExample extends React.Component { + render() { + return ( + +
Foo
+
Bar
+
+ ); + } + } + + const FragmentConstExample = () => ( + +
Foo
+
Bar
+
+ ); + + it('correctly renders html for both children for class', () => { + const classWrapper = mount(); + expect(classWrapper.html()).to.equal('
Foo
Bar
'); + }); + + it('correctly renders html for both children for const', () => { + const constWrapper = mount(); + expect(constWrapper.html()).to.equal('
Foo
Bar
'); + }); + }); }); describe('.unmount()', () => { diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 958c1fb55..1e23d0d97 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -3653,6 +3653,38 @@ describe('shallow', () => { expect(wrapper.text()).to.equal('{ some text }'); }); }); + + describeIf(is('> 16.2'), 'fragments', () => { + class FragmentClassExample extends React.Component { + render() { + return ( + +
Foo
+
Bar
+
+ ); + } + } + + const FragmentConstExample = () => ( + +
Foo
+
Bar
+
+ ); + + it('correctly gets text for both children for class', () => { + const classWrapper = shallow(); + expect(classWrapper.text()).to.include('Foo'); + expect(classWrapper.text()).to.include('Bar'); + }); + + it('correctly gets text for both children for const', () => { + const constWrapper = shallow(); + expect(constWrapper.text()).to.include('Foo'); + expect(constWrapper.text()).to.include('Bar'); + }); + }); }); describe('.props()', () => { @@ -5207,6 +5239,36 @@ describe('shallow', () => { )); }); }); + + describeIf(is('>16.2'), 'fragments', () => { + class FragmentClassExample extends React.Component { + render() { + return ( + +
Foo
+
Bar
+
+ ); + } + } + + const FragmentConstExample = () => ( + +
Foo
+
Bar
+
+ ); + + it('correctly renders html for both children for class', () => { + const classWrapper = shallow(); + expect(classWrapper.html()).to.equal('
Foo
Bar
'); + }); + + it('correctly renders html for both children for const', () => { + const constWrapper = shallow(); + expect(constWrapper.html()).to.equal('
Foo
Bar
'); + }); + }); }); describe('.unmount()', () => { diff --git a/packages/enzyme/src/RSTTraversal.js b/packages/enzyme/src/RSTTraversal.js index 180f0f89a..9148674bd 100644 --- a/packages/enzyme/src/RSTTraversal.js +++ b/packages/enzyme/src/RSTTraversal.js @@ -141,6 +141,5 @@ export function getTextFromNode(node) { return `<${node.type.displayName || functionName(node.type)} />`; } - return childrenOfNode(node).map(getTextFromNode) - .join(''); + return childrenOfNode(node).map(getTextFromNode).join(''); } diff --git a/packages/enzyme/src/ReactWrapper.js b/packages/enzyme/src/ReactWrapper.js index 7752fa15e..6074a68de 100644 --- a/packages/enzyme/src/ReactWrapper.js +++ b/packages/enzyme/src/ReactWrapper.js @@ -579,7 +579,16 @@ class ReactWrapper { if (!node) { return typeof n === 'string' ? n : node; } - return node.textContent; + + const nodeArray = Array.isArray(node) ? node : [node]; + const textContent = nodeArray.map((item) => { + if (!item) { + return ''; + } + return item.textContent || ''; + }); + + return textContent.join(''); }); } @@ -594,10 +603,17 @@ class ReactWrapper { return this.single('html', (n) => { if (n === null) return null; const adapter = getAdapter(this[OPTIONS]); - const node = adapter.nodeToHostNode(n); - return node === null + const node = adapter.nodeToHostNode(n, true); + + if (node === null) return null; + + const nodeArray = Array.isArray(node) ? node : [node]; + const nodesHTML = nodeArray.map(item => (item === null ? null - : node.outerHTML.replace(/\sdata-(reactid|reactroot)+="([^"]*)+"/g, ''); + : item.outerHTML.replace(/\sdata-(reactid|reactroot)+="([^"]*)+"/g, '') + )); + + return nodesHTML.join(''); }); }