mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add more SSR unit tests for elements and children. (#9221)
* Adding more SSR unit tests for elements and children. * Some of my SSR tests were testing for react-text and react-empty elements that no longer exist in Fiber. Fixed the tests so that they expect correct markup in Fiber. * Tweaked some test names after @gaearon review comment https://github.com/facebook/react/pull/9221#discussion_r107045673 . Also realized that one of the tests was essentially a direct copy of another, so deleted it. * Responding to code review https://github.com/facebook/react/pull/9221#pullrequestreview-28996315 . Thanks @spicyj!
This commit is contained in:
committed by
Dan Abramov
parent
7dba33b2cf
commit
60e78e37b8
@@ -46,6 +46,62 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
|
||||
* renders unknown attributes for custom elements with client render on top of good server markup
|
||||
* renders unknown attributes for custom elements using is with client render on top of good server markup
|
||||
* renders no HTML events with client render on top of good server markup
|
||||
* renders a div with text with client render on top of good server markup
|
||||
* renders a div with text with flanking whitespace with client render on top of good server markup
|
||||
* renders a div with an empty text child with client render on top of good server markup
|
||||
* renders a div with multiple empty text children with server string render
|
||||
* renders a div with multiple empty text children with client render on top of good server markup
|
||||
* renders a div with multiple whitespace children with server string render
|
||||
* renders a div with multiple whitespace children with client render on top of good server markup
|
||||
* renders a div with text sibling to a node with server string render
|
||||
* renders a div with text sibling to a node with client render on top of good server markup
|
||||
* renders a non-standard element with text with client render on top of good server markup
|
||||
* renders a custom element with text with client render on top of good server markup
|
||||
* renders a leading blank child with a text sibling with server string render
|
||||
* renders a leading blank child with a text sibling with client render on top of good server markup
|
||||
* renders a trailing blank child with a text sibling with server string render
|
||||
* renders a trailing blank child with a text sibling with client render on top of good server markup
|
||||
* renders an element with two text children with server string render
|
||||
* renders an element with two text children with client render on top of good server markup
|
||||
* renders a number as single child with client render on top of good server markup
|
||||
* renders zero as single child with client render on top of good server markup
|
||||
* renders an element with number and text children with server string render
|
||||
* renders an element with number and text children with client render on top of good server markup
|
||||
* renders null single child as blank with client render on top of good server markup
|
||||
* renders false single child as blank with client render on top of good server markup
|
||||
* renders undefined single child as blank with client render on top of good server markup
|
||||
* renders a null component children as empty with server string render
|
||||
* renders a null component children as empty with client render on top of good server markup
|
||||
* renders null children as blank with server string render
|
||||
* renders null children as blank with client render on top of good server markup
|
||||
* renders false children as blank with server string render
|
||||
* renders false children as blank with client render on top of good server markup
|
||||
* renders null and false children together as blank with server string render
|
||||
* renders null and false children together as blank with client render on top of good server markup
|
||||
* renders only null and false children as blank with client render on top of good server markup
|
||||
* renders an svg element with client render on top of good server markup
|
||||
* renders svg element with an xlink with client render on top of good server markup
|
||||
* renders a math element with client render on top of good server markup
|
||||
* renders an img with client render on top of good server markup
|
||||
* renders a button with client render on top of good server markup
|
||||
* renders a div with dangerouslySetInnerHTML with client render on top of good server markup
|
||||
* renders a newline-eating tag with content not starting with \n with client render on top of good server markup
|
||||
* renders a newline-eating tag with content starting with \n with client render on top of good server markup
|
||||
* renders a normal tag with content starting with \n with client render on top of good server markup
|
||||
* renders stateless components with client render on top of good server markup
|
||||
* renders ES6 class components with client render on top of good server markup
|
||||
* renders factory components with client render on top of good server markup
|
||||
* renders single child hierarchies of components with client render on top of good server markup
|
||||
* renders multi-child hierarchies of components with client render on top of good server markup
|
||||
* renders a div with a child with client render on top of good server markup
|
||||
* renders a div with multiple children with client render on top of good server markup
|
||||
* renders a div with multiple children separated by whitespace with server string render
|
||||
* renders a div with multiple children separated by whitespace with client render on top of good server markup
|
||||
* renders a div with a single child surrounded by whitespace with server string render
|
||||
* renders a div with a single child surrounded by whitespace with client render on top of good server markup
|
||||
* renders >,<, and & as single child with client render on top of good server markup
|
||||
* renders >,<, and & as multiple children with server string render
|
||||
* renders >,<, and & as multiple children with client render on top of good server markup
|
||||
|
||||
src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
|
||||
* can reconcile text merged by Node.normalize() alongside other elements
|
||||
|
||||
@@ -47,6 +47,54 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
|
||||
* renders unknown attributes for custom elements with client render on top of bad server markup
|
||||
* renders unknown attributes for custom elements using is with client render on top of bad server markup
|
||||
* renders no HTML events with client render on top of bad server markup
|
||||
* renders a div with text with client render on top of bad server markup
|
||||
* renders a div with text with flanking whitespace with client render on top of bad server markup
|
||||
* renders a div with an empty text child with client render on top of bad server markup
|
||||
* renders a div with multiple empty text children with client render on top of bad server markup
|
||||
* renders a div with multiple whitespace children with client render on top of bad server markup
|
||||
* renders a div with text sibling to a node with client render on top of bad server markup
|
||||
* renders a non-standard element with text with client render on top of bad server markup
|
||||
* renders a custom element with text with client render on top of bad server markup
|
||||
* renders a leading blank child with a text sibling with client render on top of bad server markup
|
||||
* renders a trailing blank child with a text sibling with client render on top of bad server markup
|
||||
* renders an element with two text children with client render on top of bad server markup
|
||||
* renders a number as single child with client render on top of bad server markup
|
||||
* renders zero as single child with client render on top of bad server markup
|
||||
* renders an element with number and text children with client render on top of bad server markup
|
||||
* renders null single child as blank with client render on top of bad server markup
|
||||
* renders false single child as blank with client render on top of bad server markup
|
||||
* renders undefined single child as blank with client render on top of bad server markup
|
||||
* renders a null component children as empty with client render on top of bad server markup
|
||||
* renders null children as blank with client render on top of bad server markup
|
||||
* renders false children as blank with client render on top of bad server markup
|
||||
* renders null and false children together as blank with client render on top of bad server markup
|
||||
* renders only null and false children as blank with client render on top of bad server markup
|
||||
* renders an svg element with client render on top of bad server markup
|
||||
* renders svg element with an xlink with client render on top of bad server markup
|
||||
* renders a math element with client render on top of bad server markup
|
||||
* renders an img with client render on top of bad server markup
|
||||
* renders a button with client render on top of bad server markup
|
||||
* renders a div with dangerouslySetInnerHTML with client render on top of bad server markup
|
||||
* renders a newline-eating tag with content not starting with \n with client render on top of bad server markup
|
||||
* renders a newline-eating tag with content starting with \n with client render on top of bad server markup
|
||||
* renders a normal tag with content starting with \n with client render on top of bad server markup
|
||||
* renders stateless components with client render on top of bad server markup
|
||||
* renders ES6 class components with client render on top of bad server markup
|
||||
* renders factory components with client render on top of bad server markup
|
||||
* renders single child hierarchies of components with client render on top of bad server markup
|
||||
* renders multi-child hierarchies of components with client render on top of bad server markup
|
||||
* renders a div with a child with client render on top of bad server markup
|
||||
* renders a div with multiple children with client render on top of bad server markup
|
||||
* renders a div with multiple children separated by whitespace with client render on top of bad server markup
|
||||
* renders a div with a single child surrounded by whitespace with client render on top of bad server markup
|
||||
* renders >,<, and & as single child with client render on top of bad server markup
|
||||
* renders >,<, and & as multiple children with client render on top of bad server markup
|
||||
* throws when rendering a string component with clean client render
|
||||
* throws when rendering a string component with client render on top of bad server markup
|
||||
* throws when rendering an undefined component with clean client render
|
||||
* throws when rendering an undefined component with client render on top of bad server markup
|
||||
* throws when rendering a number component with clean client render
|
||||
* throws when rendering a number component with client render on top of bad server markup
|
||||
|
||||
src/renderers/dom/shared/__tests__/ReactMount-test.js
|
||||
* should warn if mounting into dirty rendered markup
|
||||
|
||||
@@ -1066,6 +1066,94 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
|
||||
* renders unknown attributes for custom elements using is with clean client render
|
||||
* renders no HTML events with server string render
|
||||
* renders no HTML events with clean client render
|
||||
* renders a div with text with server string render
|
||||
* renders a div with text with clean client render
|
||||
* renders a div with text with flanking whitespace with server string render
|
||||
* renders a div with text with flanking whitespace with clean client render
|
||||
* renders a div with an empty text child with server string render
|
||||
* renders a div with an empty text child with clean client render
|
||||
* renders a div with multiple empty text children with clean client render
|
||||
* renders a div with multiple whitespace children with clean client render
|
||||
* renders a div with text sibling to a node with clean client render
|
||||
* renders a non-standard element with text with server string render
|
||||
* renders a non-standard element with text with clean client render
|
||||
* renders a custom element with text with server string render
|
||||
* renders a custom element with text with clean client render
|
||||
* renders a leading blank child with a text sibling with clean client render
|
||||
* renders a trailing blank child with a text sibling with clean client render
|
||||
* renders an element with two text children with clean client render
|
||||
* renders a number as single child with server string render
|
||||
* renders a number as single child with clean client render
|
||||
* renders zero as single child with server string render
|
||||
* renders zero as single child with clean client render
|
||||
* renders an element with number and text children with clean client render
|
||||
* renders null single child as blank with server string render
|
||||
* renders null single child as blank with clean client render
|
||||
* renders false single child as blank with server string render
|
||||
* renders false single child as blank with clean client render
|
||||
* renders undefined single child as blank with server string render
|
||||
* renders undefined single child as blank with clean client render
|
||||
* renders a null component children as empty with clean client render
|
||||
* renders null children as blank with clean client render
|
||||
* renders false children as blank with clean client render
|
||||
* renders null and false children together as blank with clean client render
|
||||
* renders only null and false children as blank with server string render
|
||||
* renders only null and false children as blank with clean client render
|
||||
* renders an svg element with server string render
|
||||
* renders an svg element with clean client render
|
||||
* renders svg element with an xlink with server string render
|
||||
* renders svg element with an xlink with clean client render
|
||||
* renders a math element with server string render
|
||||
* renders a math element with clean client render
|
||||
* renders an img with server string render
|
||||
* renders an img with clean client render
|
||||
* renders a button with server string render
|
||||
* renders a button with clean client render
|
||||
* renders a div with dangerouslySetInnerHTML with server string render
|
||||
* renders a div with dangerouslySetInnerHTML with clean client render
|
||||
* renders a newline-eating tag with content not starting with \n with server string render
|
||||
* renders a newline-eating tag with content not starting with \n with clean client render
|
||||
* renders a newline-eating tag with content starting with \n with server string render
|
||||
* renders a newline-eating tag with content starting with \n with clean client render
|
||||
* renders a normal tag with content starting with \n with server string render
|
||||
* renders a normal tag with content starting with \n with clean client render
|
||||
* renders stateless components with server string render
|
||||
* renders stateless components with clean client render
|
||||
* renders ES6 class components with server string render
|
||||
* renders ES6 class components with clean client render
|
||||
* renders factory components with server string render
|
||||
* renders factory components with clean client render
|
||||
* renders single child hierarchies of components with server string render
|
||||
* renders single child hierarchies of components with clean client render
|
||||
* renders multi-child hierarchies of components with server string render
|
||||
* renders multi-child hierarchies of components with clean client render
|
||||
* renders a div with a child with server string render
|
||||
* renders a div with a child with clean client render
|
||||
* renders a div with multiple children with server string render
|
||||
* renders a div with multiple children with clean client render
|
||||
* renders a div with multiple children separated by whitespace with clean client render
|
||||
* renders a div with a single child surrounded by whitespace with clean client render
|
||||
* renders >,<, and & as single child with server string render
|
||||
* renders >,<, and & as single child with clean client render
|
||||
* renders >,<, and & as multiple children with clean client render
|
||||
* throws when rendering a string component with server string render
|
||||
* throws when rendering an undefined component with server string render
|
||||
* throws when rendering a number component with server string render
|
||||
* throws when rendering null with server string render
|
||||
* throws when rendering null with clean client render
|
||||
* throws when rendering null with client render on top of bad server markup
|
||||
* throws when rendering false with server string render
|
||||
* throws when rendering false with clean client render
|
||||
* throws when rendering false with client render on top of bad server markup
|
||||
* throws when rendering undefined with server string render
|
||||
* throws when rendering undefined with clean client render
|
||||
* throws when rendering undefined with client render on top of bad server markup
|
||||
* throws when rendering number with server string render
|
||||
* throws when rendering number with clean client render
|
||||
* throws when rendering number with client render on top of bad server markup
|
||||
* throws when rendering string with server string render
|
||||
* throws when rendering string with clean client render
|
||||
* throws when rendering string with client render on top of bad server markup
|
||||
|
||||
src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
|
||||
* updates a mounted text component in place
|
||||
|
||||
@@ -15,6 +15,7 @@ let ExecutionEnvironment;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactDOMFeatureFlags;
|
||||
|
||||
// Helper functions for rendering tests
|
||||
// ====================================
|
||||
@@ -148,6 +149,31 @@ function itClientRenders(desc, testFn) {
|
||||
testFn(clientRenderOnBadMarkup));
|
||||
}
|
||||
|
||||
function itThrows(desc, testFn) {
|
||||
it(`throws ${desc}`, () => {
|
||||
return testFn()
|
||||
.then(() =>
|
||||
expect(false).toBe('The promise resolved and should not have.'))
|
||||
.catch(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
function itThrowsWhenRendering(desc, testFn) {
|
||||
itThrows(`when rendering ${desc} with server string render`, () =>
|
||||
testFn(serverRender));
|
||||
itThrows(`when rendering ${desc} with clean client render`, () =>
|
||||
testFn(clientCleanRender));
|
||||
|
||||
// we subtract one from the warning count here because the throw means that it won't
|
||||
// get the usual markup mismatch warning.
|
||||
itThrows(
|
||||
`when rendering ${desc} with client render on top of bad server markup`,
|
||||
() =>
|
||||
testFn((element, warningCount = 0) =>
|
||||
clientRenderOnBadMarkup(element, warningCount - 1)),
|
||||
);
|
||||
}
|
||||
|
||||
// When there is a test that renders on server and then on client and expects a logged
|
||||
// error, you want to see the error show up both on server and client. Unfortunately,
|
||||
// React refuses to issue the same error twice to avoid clogging up the console.
|
||||
@@ -157,6 +183,7 @@ function resetModules() {
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
|
||||
ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
}
|
||||
|
||||
@@ -410,4 +437,563 @@ describe('ReactDOMServerIntegration', () => {
|
||||
expect(e.getAttribute('click')).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('elements and children', function() {
|
||||
// helper functions.
|
||||
const TEXT_NODE_TYPE = 3;
|
||||
const COMMENT_NODE_TYPE = 8;
|
||||
|
||||
function expectNode(node, type, value) {
|
||||
expect(node).not.toBe(null);
|
||||
expect(node.nodeType).toBe(type);
|
||||
expect(node.nodeValue).toMatch(value);
|
||||
}
|
||||
|
||||
function expectTextNode(node, text) {
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, a React text node is just a DOM text node.
|
||||
expectNode(node, TEXT_NODE_TYPE, text);
|
||||
} else {
|
||||
// with Stack, a React text node is a DOM text node surrounded by
|
||||
// react-text comment nodes.
|
||||
expectNode(node, COMMENT_NODE_TYPE, / react-text: [0-9]+ /);
|
||||
if (text.length > 0) {
|
||||
node = node.nextSibling;
|
||||
expectNode(node, TEXT_NODE_TYPE, text);
|
||||
}
|
||||
expectNode(node.nextSibling, COMMENT_NODE_TYPE, / \/react-text /);
|
||||
}
|
||||
}
|
||||
|
||||
function expectEmptyNode(node) {
|
||||
expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
|
||||
}
|
||||
|
||||
describe('text children', function() {
|
||||
itRenders('a div with text', async render => {
|
||||
const e = await render(<div>Text</div>);
|
||||
expect(e.tagName).toBe('DIV');
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
|
||||
});
|
||||
|
||||
itRenders('a div with text with flanking whitespace', async render => {
|
||||
// prettier-ignore
|
||||
const e = await render(<div> Text </div>);
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.childNodes[0], TEXT_NODE_TYPE, ' Text ');
|
||||
});
|
||||
|
||||
itRenders('a div with an empty text child', async render => {
|
||||
const e = await render(<div>{''}</div>);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('a div with multiple empty text children', async render => {
|
||||
const e = await render(<div>{''}{''}{''}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just three separate text node children,
|
||||
// each of which is blank.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
expectTextNode(e.childNodes[0], '');
|
||||
expectTextNode(e.childNodes[1], '');
|
||||
expectTextNode(e.childNodes[2], '');
|
||||
} else {
|
||||
// with Stack, there are six react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(6);
|
||||
expectTextNode(e.childNodes[0], '');
|
||||
expectTextNode(e.childNodes[2], '');
|
||||
expectTextNode(e.childNodes[4], '');
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('a div with multiple whitespace children', async render => {
|
||||
const e = await render(<div>{' '}{' '}{' '}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just three text nodes.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
expectTextNode(e.childNodes[0], ' ');
|
||||
expectTextNode(e.childNodes[1], ' ');
|
||||
expectTextNode(e.childNodes[2], ' ');
|
||||
} else {
|
||||
// with Stack, each of the text nodes is surrounded by react-text
|
||||
// comment nodes, making 9 nodes in total.
|
||||
expect(e.childNodes.length).toBe(9);
|
||||
expectTextNode(e.childNodes[0], ' ');
|
||||
expectTextNode(e.childNodes[3], ' ');
|
||||
expectTextNode(e.childNodes[6], ' ');
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('a div with text sibling to a node', async render => {
|
||||
const e = await render(<div>Text<span>More Text</span></div>);
|
||||
let spanNode;
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are only two children, the "Text" text node and
|
||||
// the span element.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
spanNode = e.childNodes[1];
|
||||
} else {
|
||||
// with Stack, there are four children, a "Text" text node surrounded
|
||||
// by react-text comment nodes, and the span element.
|
||||
expect(e.childNodes.length).toBe(4);
|
||||
spanNode = e.childNodes[3];
|
||||
}
|
||||
expectTextNode(e.childNodes[0], 'Text');
|
||||
expect(spanNode.tagName).toBe('SPAN');
|
||||
expect(spanNode.childNodes.length).toBe(1);
|
||||
expectNode(spanNode.firstChild, TEXT_NODE_TYPE, 'More Text');
|
||||
});
|
||||
|
||||
itRenders('a non-standard element with text', async render => {
|
||||
const e = await render(<nonstandard>Text</nonstandard>);
|
||||
expect(e.tagName).toBe('NONSTANDARD');
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
|
||||
});
|
||||
|
||||
itRenders('a custom element with text', async render => {
|
||||
const e = await render(<custom-element>Text</custom-element>);
|
||||
expect(e.tagName).toBe('CUSTOM-ELEMENT');
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
|
||||
});
|
||||
|
||||
itRenders('a leading blank child with a text sibling', async render => {
|
||||
const e = await render(<div>{''}foo</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just two text nodes.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], '');
|
||||
expectTextNode(e.childNodes[1], 'foo');
|
||||
} else {
|
||||
// with Stack, there are five nodes: two react-text comment nodes
|
||||
// without any text between them, and the text node foo surrounded
|
||||
// by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(5);
|
||||
expectTextNode(e.childNodes[0], '');
|
||||
expectTextNode(e.childNodes[2], 'foo');
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('a trailing blank child with a text sibling', async render => {
|
||||
const e = await render(<div>foo{''}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just two text nodes.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[1], '');
|
||||
} else {
|
||||
// with Stack, there are five nodes: the text node foo surrounded
|
||||
// by react-text comment nodes, and two react-text comment nodes
|
||||
// without any text between them.
|
||||
expect(e.childNodes.length).toBe(5);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[3], '');
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('an element with two text children', async render => {
|
||||
const e = await render(<div>{'foo'}{'bar'}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just two text nodes.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[1], 'bar');
|
||||
} else {
|
||||
// with Stack, there are six nodes: two text nodes, each surrounded
|
||||
// by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(6);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[3], 'bar');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('number children', function() {
|
||||
itRenders('a number as single child', async render => {
|
||||
const e = await render(<div>{3}</div>);
|
||||
expect(e.textContent).toBe('3');
|
||||
});
|
||||
|
||||
// zero is falsey, so it could look like no children if the code isn't careful.
|
||||
itRenders('zero as single child', async render => {
|
||||
const e = await render(<div>{0}</div>);
|
||||
expect(e.textContent).toBe('0');
|
||||
});
|
||||
|
||||
itRenders('an element with number and text children', async render => {
|
||||
const e = await render(<div>{'foo'}{40}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just two text nodes.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[1], '40');
|
||||
} else {
|
||||
// with Stack, there are six nodes: two text nodes, each surrounded
|
||||
// by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(6);
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
expectTextNode(e.childNodes[3], '40');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('null, false, and undefined children', function() {
|
||||
itRenders('null single child as blank', async render => {
|
||||
const e = await render(<div>{null}</div>);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('false single child as blank', async render => {
|
||||
const e = await render(<div>{false}</div>);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('undefined single child as blank', async render => {
|
||||
const e = await render(<div>{undefined}</div>);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('a null component children as empty', async render => {
|
||||
const NullComponent = () => null;
|
||||
const e = await render(<div><NullComponent /></div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, an empty component results in no markup.
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
} else {
|
||||
// with Stack, an empty component results in one react-empty comment
|
||||
// node.
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectEmptyNode(e.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('null children as blank', async render => {
|
||||
const e = await render(<div>{null}foo</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there is just one text node.
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
} else {
|
||||
// with Stack, there's a text node surronded by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
}
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
});
|
||||
|
||||
itRenders('false children as blank', async render => {
|
||||
const e = await render(<div>{false}foo</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there is just one text node.
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
} else {
|
||||
// with Stack, there's a text node surronded by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
}
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
});
|
||||
|
||||
itRenders('null and false children together as blank', async render => {
|
||||
const e = await render(<div>{false}{null}foo{null}{false}</div>);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there is just one text node.
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
} else {
|
||||
// with Stack, there's a text node surronded by react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
}
|
||||
expectTextNode(e.childNodes[0], 'foo');
|
||||
});
|
||||
|
||||
itRenders('only null and false children as blank', async render => {
|
||||
const e = await render(<div>{false}{null}{null}{false}</div>);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('elements with implicit namespaces', function() {
|
||||
itRenders('an svg element', async render => {
|
||||
const e = await render(<svg />);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
expect(e.tagName).toBe('svg');
|
||||
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
|
||||
});
|
||||
|
||||
itRenders('svg element with an xlink', async render => {
|
||||
let e = await render(
|
||||
<svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
|
||||
);
|
||||
e = e.firstChild;
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
expect(e.tagName).toBe('image');
|
||||
expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
|
||||
expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
|
||||
'http://i.imgur.com/w7GCRPb.png',
|
||||
);
|
||||
});
|
||||
|
||||
itRenders('a math element', async render => {
|
||||
const e = await render(<math />);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
expect(e.tagName).toBe('math');
|
||||
expect(e.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
|
||||
});
|
||||
});
|
||||
// specially wrapped components
|
||||
// (see the big switch near the beginning ofReactDOMComponent.mountComponent)
|
||||
itRenders('an img', async render => {
|
||||
const e = await render(<img />);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
expect(e.nextSibling).toBe(null);
|
||||
expect(e.tagName).toBe('IMG');
|
||||
});
|
||||
|
||||
itRenders('a button', async render => {
|
||||
const e = await render(<button />);
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
expect(e.nextSibling).toBe(null);
|
||||
expect(e.tagName).toBe('BUTTON');
|
||||
});
|
||||
|
||||
itRenders('a div with dangerouslySetInnerHTML', async render => {
|
||||
const e = await render(
|
||||
<div dangerouslySetInnerHTML={{__html: "<span id='child'/>"}} />,
|
||||
);
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expect(e.firstChild.tagName).toBe('SPAN');
|
||||
expect(e.firstChild.getAttribute('id')).toBe('child');
|
||||
expect(e.firstChild.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
describe('newline-eating elements', function() {
|
||||
itRenders(
|
||||
'a newline-eating tag with content not starting with \\n',
|
||||
async render => {
|
||||
const e = await render(<pre>Hello</pre>);
|
||||
expect(e.textContent).toBe('Hello');
|
||||
},
|
||||
);
|
||||
itRenders(
|
||||
'a newline-eating tag with content starting with \\n',
|
||||
async render => {
|
||||
const e = await render(<pre>{'\nHello'}</pre>);
|
||||
expect(e.textContent).toBe('\nHello');
|
||||
},
|
||||
);
|
||||
itRenders('a normal tag with content starting with \\n', async render => {
|
||||
const e = await render(<div>{'\nHello'}</div>);
|
||||
expect(e.textContent).toBe('\nHello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('different component implementations', function() {
|
||||
function checkFooDiv(e) {
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.firstChild, TEXT_NODE_TYPE, 'foo');
|
||||
}
|
||||
|
||||
itRenders('stateless components', async render => {
|
||||
const StatelessComponent = () => <div>foo</div>;
|
||||
checkFooDiv(await render(<StatelessComponent />));
|
||||
});
|
||||
|
||||
itRenders('ES6 class components', async render => {
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
return <div>foo</div>;
|
||||
}
|
||||
}
|
||||
checkFooDiv(await render(<ClassComponent />));
|
||||
});
|
||||
|
||||
itRenders('factory components', async render => {
|
||||
const FactoryComponent = () => {
|
||||
return {
|
||||
render: function() {
|
||||
return <div>foo</div>;
|
||||
},
|
||||
};
|
||||
};
|
||||
checkFooDiv(await render(<FactoryComponent />));
|
||||
});
|
||||
});
|
||||
|
||||
describe('component hierarchies', async function() {
|
||||
itRenders('single child hierarchies of components', async render => {
|
||||
const Component = props => <div>{props.children}</div>;
|
||||
let e = await render(
|
||||
<Component>
|
||||
<Component>
|
||||
<Component>
|
||||
<Component />
|
||||
</Component>
|
||||
</Component>
|
||||
</Component>,
|
||||
);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
expect(e.tagName).toBe('DIV');
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
e = e.firstChild;
|
||||
}
|
||||
expect(e.tagName).toBe('DIV');
|
||||
expect(e.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('multi-child hierarchies of components', async render => {
|
||||
const Component = props => <div>{props.children}</div>;
|
||||
const e = await render(
|
||||
<Component>
|
||||
<Component>
|
||||
<Component /><Component />
|
||||
</Component>
|
||||
<Component>
|
||||
<Component /><Component />
|
||||
</Component>
|
||||
</Component>,
|
||||
);
|
||||
expect(e.tagName).toBe('DIV');
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var child = e.childNodes[i];
|
||||
expect(child.tagName).toBe('DIV');
|
||||
expect(child.childNodes.length).toBe(2);
|
||||
for (var j = 0; j < 2; j++) {
|
||||
var grandchild = child.childNodes[j];
|
||||
expect(grandchild.tagName).toBe('DIV');
|
||||
expect(grandchild.childNodes.length).toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
itRenders('a div with a child', async render => {
|
||||
const e = await render(<div id="parent"><div id="child" /></div>);
|
||||
expect(e.id).toBe('parent');
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expect(e.childNodes[0].id).toBe('child');
|
||||
expect(e.childNodes[0].childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders('a div with multiple children', async render => {
|
||||
const e = await render(
|
||||
<div id="parent"><div id="child1" /><div id="child2" /></div>,
|
||||
);
|
||||
expect(e.id).toBe('parent');
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expect(e.childNodes[0].id).toBe('child1');
|
||||
expect(e.childNodes[0].childNodes.length).toBe(0);
|
||||
expect(e.childNodes[1].id).toBe('child2');
|
||||
expect(e.childNodes[1].childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
itRenders(
|
||||
'a div with multiple children separated by whitespace',
|
||||
async render => {
|
||||
const e = await render(
|
||||
<div id="parent"><div id="child1" /> <div id="child2" /></div>,
|
||||
);
|
||||
expect(e.id).toBe('parent');
|
||||
let child1, child2, textNode;
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are three children: the child1 element, a
|
||||
// single space text node, and the child2 element.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
child1 = e.childNodes[0];
|
||||
textNode = e.childNodes[1];
|
||||
child2 = e.childNodes[2];
|
||||
} else {
|
||||
// with Stack, there are five children: the child1 element, a single
|
||||
// space surrounded by react-text comments, and the child2 element.
|
||||
expect(e.childNodes.length).toBe(5);
|
||||
child1 = e.childNodes[0];
|
||||
textNode = e.childNodes[1];
|
||||
child2 = e.childNodes[4];
|
||||
}
|
||||
expect(child1.id).toBe('child1');
|
||||
expect(child1.childNodes.length).toBe(0);
|
||||
expectTextNode(textNode, ' ');
|
||||
expect(child2.id).toBe('child2');
|
||||
expect(child2.childNodes.length).toBe(0);
|
||||
},
|
||||
);
|
||||
|
||||
itRenders(
|
||||
'a div with a single child surrounded by whitespace',
|
||||
async render => {
|
||||
// prettier-ignore
|
||||
const e = await render(<div id="parent"> <div id="child" /> </div>); // eslint-disable-line no-multi-spaces
|
||||
let textNode1, child, textNode2;
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are three children: a one-space text node, the
|
||||
// child element, and a two-space text node.
|
||||
expect(e.childNodes.length).toBe(3);
|
||||
textNode1 = e.childNodes[0];
|
||||
child = e.childNodes[1];
|
||||
textNode2 = e.childNodes[2];
|
||||
} else {
|
||||
// with Stack, there are 7 children: a one-space text node surrounded
|
||||
// by react-text comments, the child element, and a two-space text node
|
||||
// surrounded by react-text comments.
|
||||
expect(e.childNodes.length).toBe(7);
|
||||
textNode1 = e.childNodes[0];
|
||||
child = e.childNodes[3];
|
||||
textNode2 = e.childNodes[4];
|
||||
}
|
||||
expect(e.id).toBe('parent');
|
||||
expectTextNode(textNode1, ' ');
|
||||
expect(child.id).toBe('child');
|
||||
expect(child.childNodes.length).toBe(0);
|
||||
expectTextNode(textNode2, ' ');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('escaping >, <, and &', function() {
|
||||
itRenders('>,<, and & as single child', async render => {
|
||||
const e = await render(<div>{'<span>Text"</span>'}</div>);
|
||||
expect(e.childNodes.length).toBe(1);
|
||||
expectNode(e.firstChild, TEXT_NODE_TYPE, '<span>Text"</span>');
|
||||
});
|
||||
|
||||
itRenders('>,<, and & as multiple children', async render => {
|
||||
const e = await render(
|
||||
<div>{'<span>Text1"</span>'}{'<span>Text2"</span>'}</div>,
|
||||
);
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
// with Fiber, there are just two text nodes.
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], '<span>Text1"</span>');
|
||||
expectTextNode(e.childNodes[1], '<span>Text2"</span>');
|
||||
} else {
|
||||
// with Stack there are six nodes: two text nodes each surrounded by
|
||||
// two react-text comment nodes.
|
||||
expect(e.childNodes.length).toBe(6);
|
||||
expectTextNode(e.childNodes[0], '<span>Text1"</span>');
|
||||
expectTextNode(e.childNodes[3], '<span>Text2"</span>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('components that throw errors', function() {
|
||||
itThrowsWhenRendering('a string component', async render => {
|
||||
const StringComponent = () => 'foo';
|
||||
await render(<StringComponent />, 1);
|
||||
});
|
||||
|
||||
itThrowsWhenRendering('an undefined component', async render => {
|
||||
const UndefinedComponent = () => undefined;
|
||||
await render(<UndefinedComponent />, 1);
|
||||
});
|
||||
|
||||
itThrowsWhenRendering('a number component', async render => {
|
||||
const NumberComponent = () => 54;
|
||||
await render(<NumberComponent />, 1);
|
||||
});
|
||||
|
||||
itThrowsWhenRendering('null', render => render(null));
|
||||
itThrowsWhenRendering('false', render => render(false));
|
||||
itThrowsWhenRendering('undefined', render => render(undefined));
|
||||
itThrowsWhenRendering('number', render => render(30));
|
||||
itThrowsWhenRendering('string', render => render('foo'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user