1574
});
1575
});
1576
1577
+ describe('carriage return and null character', () => {
1578
+ // HTML parsing normalizes CR and CRLF to LF.
1579
+ // It also ignores null character.
1580
+ // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
1581
+ // If we have a mismatch, it might be caused by that (and should not be reported).
1582
+ // We won't be patching up in this case as that matches our past behavior.
1583
+
1584
+ itRenders(
1585
+ 'an element with one text child with special characters',
1586
+ async render => {
1587
+ const e = await render(<div>{'foo\rbar\r\nbaz\nqux\u0000'}</div>);
1588
+ if (render === serverRender || render === streamRender) {
1589
+ expect(e.childNodes.length).toBe(1);
1590
+ // Everything becomes LF when parsed from server HTML.
1591
+ // Null character is ignored.
1592
+ expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar\nbaz\nqux');
1593
+ } else {
1594
+ expect(e.childNodes.length).toBe(1);
1595
+ // Client rendering (or hydration) uses JS value with CR.
1596
+ // Null character stays.
1597
+ expectNode(
1598
+ e.childNodes[0],
1599
+ TEXT_NODE_TYPE,
1600
+ 'foo\rbar\r\nbaz\nqux\u0000',
1601
+ );
1602
+ }
1603
+ },
1604
+ );
1605
+
1606
+ itRenders(
1607
+ 'an element with two text children with special characters',
1608
+ async render => {
1609
+ const e = await render(<div>{'foo\rbar'}{'\r\nbaz\nqux\u0000'}</div>);
1610
+ if (render === serverRender || render === streamRender) {
1611
+ // We have three nodes because there is a comment between them.
1612
+ expect(e.childNodes.length).toBe(3);
1613
+ // Everything becomes LF when parsed from server HTML.
1614
+ // Null character is ignored.
1615
+ expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\nbar');
1616
+ expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\nbaz\nqux');
1617
+ } else if (render === clientRenderOnServerString) {
1618
+ // We have three nodes because there is a comment between them.
1619
+ expect(e.childNodes.length).toBe(3);
1620
+ // Hydration uses JS value with CR and null character.
1621
+ expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar');
1622
+ expectNode(e.childNodes[2], TEXT_NODE_TYPE, '\r\nbaz\nqux\u0000');
1623
+ } else {
1624
+ expect(e.childNodes.length).toBe(2);
1625
+ // Client rendering uses JS value with CR and null character.
1626
+ expectNode(e.childNodes[0], TEXT_NODE_TYPE, 'foo\rbar');
1627
+ expectNode(e.childNodes[1], TEXT_NODE_TYPE, '\r\nbaz\nqux\u0000');
1628
+ }
1629
+ },
1630
+ );
1631
+
1632
+ itRenders(
1633
+ 'an element with an attribute value with special characters',
1634
+ async render => {
1635
+ const e = await render(<a title={'foo\rbar\r\nbaz\nqux\u0000'} />);
1636
+ if (
1637
+ render === serverRender ||
1638
+ render === streamRender ||
1639
+ render === clientRenderOnServerString
1640
+ ) {
1641
+ // Everything becomes LF when parsed from server HTML.
1642
+ // Null character in an attribute becomes the replacement character.
1643
+ // Hydration also ends up with LF because we don't patch up attributes.
1644
+ expect(e.title).toBe('foo\nbar\nbaz\nqux\uFFFD');
1645
+ } else {
1646
+ // Client rendering uses JS value with CR and null character.
1647
+ expect(e.title).toBe('foo\rbar\r\nbaz\nqux\u0000');
1648
+ }
1649
+ },
1650
+ );
1651
+ });
1652
+
1653
describe('components that throw errors', function() {
1654
itThrowsWhenRendering(
1655
'a function returning undefined',