diff --git a/scripts/fiber/tests-failing.txt b/scripts/fiber/tests-failing.txt index c583af694fb07..b99355d063d4e 100644 --- a/scripts/fiber/tests-failing.txt +++ b/scripts/fiber/tests-failing.txt @@ -102,6 +102,17 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js * 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 +* renders class child with context with client render on top of good server markup +* renders stateless child with context with client render on top of good server markup +* renders class child without context with client render on top of good server markup +* renders stateless child without context with client render on top of good server markup +* renders class child with wrong context with client render on top of good server markup +* renders stateless child with wrong context with client render on top of good server markup +* renders with context passed through to a grandchild with client render on top of good server markup +* renders a child context overriding a parent context with client render on top of good server markup +* renders a child context merged with a parent context with client render on top of good server markup +* renders with a call to componentWillMount before getChildContext with client render on top of good server markup +* should send the correct element to ref functions on client src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js * can reconcile text merged by Node.normalize() alongside other elements diff --git a/scripts/fiber/tests-passing-except-dev.txt b/scripts/fiber/tests-passing-except-dev.txt index 4ab8bafc5e95d..5b7ce16d9231d 100644 --- a/scripts/fiber/tests-passing-except-dev.txt +++ b/scripts/fiber/tests-passing-except-dev.txt @@ -95,6 +95,35 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js * 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 +* renders class child with context with client render on top of bad server markup +* renders stateless child with context with client render on top of bad server markup +* renders class child without context with client render on top of bad server markup +* renders stateless child without context with client render on top of bad server markup +* renders class child with wrong context with client render on top of bad server markup +* renders stateless child with wrong context with client render on top of bad server markup +* renders with context passed through to a grandchild with client render on top of bad server markup +* renders a child context overriding a parent context with client render on top of bad server markup +* renders a child context merged with a parent context with client render on top of bad server markup +* renders with a call to componentWillMount before getChildContext with client render on top of bad server markup +* should error reconnecting different element types +* should error reconnecting missing attributes +* should error reconnecting added attributes +* should error reconnecting different attribute values +* should error reconnecting different text +* should error reconnecting different numbers +* should error reconnecting different number from text +* should error reconnecting different text in two code blocks +* should error reconnecting missing children +* should error reconnecting added children +* should error reconnecting more children +* should error reconnecting fewer children +* should error reconnecting reordered children +* should error reconnecting a div with children separated by whitespace on the client +* should error reconnecting a div with children separated by different whitespace on the server +* should error reconnecting a div with children separated by different whitespace +* can distinguish an empty component from a dom node +* can distinguish an empty component from an empty text component +* should error reconnecting a div with different dangerouslySetInnerHTML src/renderers/dom/shared/__tests__/ReactMount-test.js * should warn if mounting into dirty rendered markup diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index 16aab7d56eaaf..05ca4a1b2bf44 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -1154,6 +1154,46 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js * 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 +* renders class child with context with server string render +* renders class child with context with clean client render +* renders stateless child with context with server string render +* renders stateless child with context with clean client render +* renders class child without context with server string render +* renders class child without context with clean client render +* renders stateless child without context with server string render +* renders stateless child without context with clean client render +* renders class child with wrong context with server string render +* renders class child with wrong context with clean client render +* renders stateless child with wrong context with server string render +* renders stateless child with wrong context with clean client render +* renders with context passed through to a grandchild with server string render +* renders with context passed through to a grandchild with clean client render +* renders a child context overriding a parent context with server string render +* renders a child context overriding a parent context with clean client render +* renders a child context merged with a parent context with server string render +* renders a child context merged with a parent context with clean client render +* renders with a call to componentWillMount before getChildContext with server string render +* renders with a call to componentWillMount before getChildContext with clean client render +* throws when rendering if getChildContext exists without childContextTypes with server string render +* throws when rendering if getChildContext exists without childContextTypes with clean client render +* throws when rendering if getChildContext exists without childContextTypes with client render on top of bad server markup +* throws when rendering if getChildContext returns a value not in childContextTypes with server string render +* throws when rendering if getChildContext returns a value not in childContextTypes with clean client render +* throws when rendering if getChildContext returns a value not in childContextTypes with client render on top of bad server markup +* should not run ref code on server +* should run ref code on client +* should have string refs on client when rendered over server markup +* should reconnect ES6 Class to ES6 Class +* should reconnect Pure Component to ES6 Class +* should reconnect Bare Element to ES6 Class +* should reconnect ES6 Class to Pure Component +* should reconnect Pure Component to Pure Component +* should reconnect Bare Element to Pure Component +* should reconnect ES6 Class to Bare Element +* should reconnect Pure Component to Bare Element +* should reconnect Bare Element to Bare Element +* should reconnect a div with a number and string version of number +* should reconnect if component trees differ but resulting markup is the same src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js * updates a mounted text component in place diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js index eb3c5e25b1592..2eda9c38fc87a 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js @@ -174,6 +174,31 @@ function itThrowsWhenRendering(desc, testFn) { ); } +// renders serverElement to a string, sticks it into a DOM element, and then +// tries to render clientElement on top of it. shouldMatch is a boolean +// telling whether we should expect the markup to match or not. +async function testMarkupMatch(serverElement, clientElement, shouldMatch) { + const domElement = await serverRender(serverElement); + resetModules(); + return renderIntoDom( + clientElement, + domElement.parentNode, + shouldMatch ? 0 : 1, + ); +} + +// expects that rendering clientElement on top of a server-rendered +// serverElement does NOT raise a markup mismatch warning. +function expectMarkupMatch(serverElement, clientElement) { + return testMarkupMatch(serverElement, clientElement, true); +} + +// expects that rendering clientElement on top of a server-rendered +// serverElement DOES raise a markup mismatch warning. +function expectMarkupMismatch(serverElement, clientElement) { + return testMarkupMatch(serverElement, clientElement, false); +} + // 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. @@ -996,4 +1021,448 @@ describe('ReactDOMServerIntegration', () => { itThrowsWhenRendering('string', render => render('foo')); }); }); + + describe('context', function() { + let PurpleContext, RedContext; + beforeEach(() => { + class Parent extends React.Component { + getChildContext() { + return {text: this.props.text}; + } + render() { + return this.props.children; + } + } + Parent.childContextTypes = {text: React.PropTypes.string}; + + PurpleContext = props => {props.children}; + RedContext = props => {props.children}; + }); + + itRenders('class child with context', async render => { + class ClassChildWithContext extends React.Component { + render() { + return
{this.context.text}
; + } + } + ClassChildWithContext.contextTypes = {text: React.PropTypes.string}; + + const e = await render( + , + ); + expect(e.textContent).toBe('purple'); + }); + + itRenders('stateless child with context', async render => { + function StatelessChildWithContext(props, context) { + return
{context.text}
; + } + StatelessChildWithContext.contextTypes = {text: React.PropTypes.string}; + + const e = await render( + , + ); + expect(e.textContent).toBe('purple'); + }); + + itRenders('class child without context', async render => { + class ClassChildWithoutContext extends React.Component { + render() { + // this should render blank; context isn't passed to this component. + return
{this.context.text}
; + } + } + + const e = await render( + , + ); + expect(e.textContent).toBe(''); + }); + + itRenders('stateless child without context', async render => { + function StatelessChildWithoutContext(props, context) { + // this should render blank; context isn't passed to this component. + return
{context.text}
; + } + + const e = await render( + , + ); + expect(e.textContent).toBe(''); + }); + + itRenders('class child with wrong context', async render => { + class ClassChildWithWrongContext extends React.Component { + render() { + // this should render blank; context.text isn't passed to this component. + return
{this.context.text}
; + } + } + ClassChildWithWrongContext.contextTypes = {foo: React.PropTypes.string}; + + const e = await render( + , + ); + expect(e.textContent).toBe(''); + }); + + itRenders('stateless child with wrong context', async render => { + function StatelessChildWithWrongContext(props, context) { + // this should render blank; context.text isn't passed to this component. + return
{context.text}
; + } + StatelessChildWithWrongContext.contextTypes = { + foo: React.PropTypes.string, + }; + + const e = await render( + , + ); + expect(e.textContent).toBe(''); + }); + + itRenders('with context passed through to a grandchild', async render => { + function Grandchild(props, context) { + return
{context.text}
; + } + Grandchild.contextTypes = {text: React.PropTypes.string}; + + const Child = props => ; + + const e = await render(); + expect(e.textContent).toBe('purple'); + }); + + itRenders('a child context overriding a parent context', async render => { + const Grandchild = (props, context) => { + return
{context.text}
; + }; + Grandchild.contextTypes = {text: React.PropTypes.string}; + + const e = await render( + , + ); + expect(e.textContent).toBe('red'); + }); + + itRenders('a child context merged with a parent context', async render => { + class Parent extends React.Component { + getChildContext() { + return {text1: 'purple'}; + } + render() { + return ; + } + } + Parent.childContextTypes = {text1: React.PropTypes.string}; + + class Child extends React.Component { + getChildContext() { + return {text2: 'red'}; + } + render() { + return ; + } + } + Child.childContextTypes = {text2: React.PropTypes.string}; + + const Grandchild = (props, context) => { + return ( +
+
{context.text1}
+
{context.text2}
+
+ ); + }; + Grandchild.contextTypes = { + text1: React.PropTypes.string, + text2: React.PropTypes.string, + }; + + const e = await render(); + expect(e.querySelector('#first').textContent).toBe('purple'); + expect(e.querySelector('#second').textContent).toBe('red'); + }); + + itRenders( + 'with a call to componentWillMount before getChildContext', + async render => { + class WillMountContext extends React.Component { + getChildContext() { + return {text: this.state.text}; + } + componentWillMount() { + this.setState({text: 'foo'}); + } + render() { + return ; + } + } + WillMountContext.childContextTypes = {text: React.PropTypes.string}; + + const Child = (props, context) => { + return
{context.text}
; + }; + Child.contextTypes = {text: React.PropTypes.string}; + + const e = await render(); + expect(e.textContent).toBe('foo'); + }, + ); + + itThrowsWhenRendering( + 'if getChildContext exists without childContextTypes', + render => { + class Component extends React.Component { + render() { + return
; + } + getChildContext() { + return {foo: 'bar'}; + } + } + return render(); + }, + ); + + itThrowsWhenRendering( + 'if getChildContext returns a value not in childContextTypes', + render => { + class Component extends React.Component { + render() { + return
; + } + getChildContext() { + return {value1: 'foo', value2: 'bar'}; + } + } + Component.childContextTypes = {value1: React.PropTypes.string}; + return render(); + }, + ); + }); + + describe('refs', function() { + it('should not run ref code on server', async () => { + let refCount = 0; + class RefsComponent extends React.Component { + render() { + return
refCount++} />; + } + } + await expectMarkupMatch(,
); + expect(refCount).toBe(0); + }); + + it('should run ref code on client', async () => { + let refCount = 0; + class RefsComponent extends React.Component { + render() { + return
refCount++} />; + } + } + await expectMarkupMatch(
, ); + expect(refCount).toBe(1); + }); + + it('should send the correct element to ref functions on client', async () => { + let refElement = null; + class RefsComponent extends React.Component { + render() { + return
refElement = e} />; + } + } + const e = await clientRenderOnServerString(); + expect(refElement).not.toBe(null); + expect(refElement).toBe(e); + }); + + it('should have string refs on client when rendered over server markup', async () => { + class RefsComponent extends React.Component { + render() { + return
; + } + } + + const markup = ReactDOMServer.renderToString(); + const root = document.createElement('div'); + root.innerHTML = markup; + let component = null; + resetModules(); + await asyncReactDOMRender( + component = e} />, + root, + ); + expect(component.refs.myDiv).toBe(root.firstChild); + }); + }); + + describe('reconnecting to server markup', function() { + var EmptyComponent; + beforeEach(() => { + EmptyComponent = class extends React.Component { + render() { + return null; + } + }; + }); + + describe('elements', function() { + describe('reconnecting different component implementations', function() { + let ES6ClassComponent, PureComponent, bareElement; + beforeEach(() => { + // try each type of component on client and server. + ES6ClassComponent = class extends React.Component { + render() { + return
; + } + }; + PureComponent = props =>
; + bareElement =
; + }); + + it('should reconnect ES6 Class to ES6 Class', () => + expectMarkupMatch( + , + , + )); + + it('should reconnect Pure Component to ES6 Class', () => + expectMarkupMatch( + , + , + )); + + it('should reconnect Bare Element to ES6 Class', () => + expectMarkupMatch(, bareElement)); + + it('should reconnect ES6 Class to Pure Component', () => + expectMarkupMatch( + , + , + )); + + it('should reconnect Pure Component to Pure Component', () => + expectMarkupMatch( + , + , + )); + + it('should reconnect Bare Element to Pure Component', () => + expectMarkupMatch(, bareElement)); + + it('should reconnect ES6 Class to Bare Element', () => + expectMarkupMatch(bareElement, )); + + it('should reconnect Pure Component to Bare Element', () => + expectMarkupMatch(bareElement, )); + + it('should reconnect Bare Element to Bare Element', () => + expectMarkupMatch(bareElement, bareElement)); + }); + + it('should error reconnecting different element types', () => + expectMarkupMismatch(
, )); + + it('should error reconnecting missing attributes', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting added attributes', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting different attribute values', () => + expectMarkupMismatch(
,
)); + }); + + describe('text nodes', function() { + it('should error reconnecting different text', () => + expectMarkupMismatch(
Text
,
Other Text
)); + + it('should reconnect a div with a number and string version of number', () => + expectMarkupMatch(
{2}
,
2
)); + + it('should error reconnecting different numbers', () => + expectMarkupMismatch(
{2}
,
{3}
)); + + it('should error reconnecting different number from text', () => + expectMarkupMismatch(
{2}
,
3
)); + + it('should error reconnecting different text in two code blocks', () => + expectMarkupMismatch( +
{'Text1'}{'Text2'}
, +
{'Text1'}{'Text3'}
, + )); + }); + + describe('element trees and children', function() { + it('should error reconnecting missing children', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting added children', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting more children', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting fewer children', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting reordered children', () => + expectMarkupMismatch( +
, +
, + )); + + it('should error reconnecting a div with children separated by whitespace on the client', () => + expectMarkupMismatch( +
, + // prettier-ignore +
, // eslint-disable-line no-multi-spaces + )); + + it('should error reconnecting a div with children separated by different whitespace on the server', () => + expectMarkupMismatch( + // prettier-ignore +
, // eslint-disable-line no-multi-spaces +
, + )); + + it('should error reconnecting a div with children separated by different whitespace', () => + expectMarkupMismatch( +
, + // prettier-ignore +
, // eslint-disable-line no-multi-spaces + )); + + it('can distinguish an empty component from a dom node', () => + expectMarkupMismatch( +
, +
, + )); + + it('can distinguish an empty component from an empty text component', () => + expectMarkupMismatch(
,
{''}
)); + + it('should reconnect if component trees differ but resulting markup is the same', () => { + class Component1 extends React.Component { + render() { + return ; + } + } + class Component2 extends React.Component { + render() { + return ; + } + } + return expectMarkupMatch(, ); + }); + }); + + // Markup Mismatches: misc + it('should error reconnecting a div with different dangerouslySetInnerHTML', () => + expectMarkupMismatch( +
"}} />, +
"}} />, + )); + }); });