@samitouri / QOS-React-2 / commits / c51411c812

Adding new SSR tests for context, refs, and reviving markup. (#9257)

Sasha Aickin committed Mar 26, 2017 at 13:32 UTC c51411c812d60072a1e5f1b654d7408727093821
4 files changed +549
scripts/fiber/tests-failing.txt
+11
@@ -102,6 +102,17 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
102 * renders >,<, and & as single child with client render on top of good server markup
103 * renders >,<, and & as multiple children with server string render
104 * renders >,<, and & as multiple children with client render on top of good server markup
105 +* renders class child with context with client render on top of good server markup
106 +* renders stateless child with context with client render on top of good server markup
107 +* renders class child without context with client render on top of good server markup
108 +* renders stateless child without context with client render on top of good server markup
109 +* renders class child with wrong context with client render on top of good server markup
110 +* renders stateless child with wrong context with client render on top of good server markup
111 +* renders with context passed through to a grandchild with client render on top of good server markup
112 +* renders a child context overriding a parent context with client render on top of good server markup
113 +* renders a child context merged with a parent context with client render on top of good server markup
114 +* renders with a call to componentWillMount before getChildContext with client render on top of good server markup
115 +* should send the correct element to ref functions on client
116
117 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
118 * can reconcile text merged by Node.normalize() alongside other elements
scripts/fiber/tests-passing-except-dev.txt
+29
@@ -95,6 +95,35 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
95 * throws when rendering an undefined component with client render on top of bad server markup
96 * throws when rendering a number component with clean client render
97 * throws when rendering a number component with client render on top of bad server markup
98 +* renders class child with context with client render on top of bad server markup
99 +* renders stateless child with context with client render on top of bad server markup
100 +* renders class child without context with client render on top of bad server markup
101 +* renders stateless child without context with client render on top of bad server markup
102 +* renders class child with wrong context with client render on top of bad server markup
103 +* renders stateless child with wrong context with client render on top of bad server markup
104 +* renders with context passed through to a grandchild with client render on top of bad server markup
105 +* renders a child context overriding a parent context with client render on top of bad server markup
106 +* renders a child context merged with a parent context with client render on top of bad server markup
107 +* renders with a call to componentWillMount before getChildContext with client render on top of bad server markup
108 +* should error reconnecting different element types
109 +* should error reconnecting missing attributes
110 +* should error reconnecting added attributes
111 +* should error reconnecting different attribute values
112 +* should error reconnecting different text
113 +* should error reconnecting different numbers
114 +* should error reconnecting different number from text
115 +* should error reconnecting different text in two code blocks
116 +* should error reconnecting missing children
117 +* should error reconnecting added children
118 +* should error reconnecting more children
119 +* should error reconnecting fewer children
120 +* should error reconnecting reordered children
121 +* should error reconnecting a div with children separated by whitespace on the client
122 +* should error reconnecting a div with children separated by different whitespace on the server
123 +* should error reconnecting a div with children separated by different whitespace
124 +* can distinguish an empty component from a dom node
125 +* can distinguish an empty component from an empty text component
126 +* should error reconnecting a div with different dangerouslySetInnerHTML
127
128 src/renderers/dom/shared/__tests__/ReactMount-test.js
129 * should warn if mounting into dirty rendered markup
scripts/fiber/tests-passing.txt
+40
@@ -1154,6 +1154,46 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1154 * throws when rendering string with server string render
1155 * throws when rendering string with clean client render
1156 * throws when rendering string with client render on top of bad server markup
1157 +* renders class child with context with server string render
1158 +* renders class child with context with clean client render
1159 +* renders stateless child with context with server string render
1160 +* renders stateless child with context with clean client render
1161 +* renders class child without context with server string render
1162 +* renders class child without context with clean client render
1163 +* renders stateless child without context with server string render
1164 +* renders stateless child without context with clean client render
1165 +* renders class child with wrong context with server string render
1166 +* renders class child with wrong context with clean client render
1167 +* renders stateless child with wrong context with server string render
1168 +* renders stateless child with wrong context with clean client render
1169 +* renders with context passed through to a grandchild with server string render
1170 +* renders with context passed through to a grandchild with clean client render
1171 +* renders a child context overriding a parent context with server string render
1172 +* renders a child context overriding a parent context with clean client render
1173 +* renders a child context merged with a parent context with server string render
1174 +* renders a child context merged with a parent context with clean client render
1175 +* renders with a call to componentWillMount before getChildContext with server string render
1176 +* renders with a call to componentWillMount before getChildContext with clean client render
1177 +* throws when rendering if getChildContext exists without childContextTypes with server string render
1178 +* throws when rendering if getChildContext exists without childContextTypes with clean client render
1179 +* throws when rendering if getChildContext exists without childContextTypes with client render on top of bad server markup
1180 +* throws when rendering if getChildContext returns a value not in childContextTypes with server string render
1181 +* throws when rendering if getChildContext returns a value not in childContextTypes with clean client render
1182 +* throws when rendering if getChildContext returns a value not in childContextTypes with client render on top of bad server markup
1183 +* should not run ref code on server
1184 +* should run ref code on client
1185 +* should have string refs on client when rendered over server markup
1186 +* should reconnect ES6 Class to ES6 Class
1187 +* should reconnect Pure Component to ES6 Class
1188 +* should reconnect Bare Element to ES6 Class
1189 +* should reconnect ES6 Class to Pure Component
1190 +* should reconnect Pure Component to Pure Component
1191 +* should reconnect Bare Element to Pure Component
1192 +* should reconnect ES6 Class to Bare Element
1193 +* should reconnect Pure Component to Bare Element
1194 +* should reconnect Bare Element to Bare Element
1195 +* should reconnect a div with a number and string version of number
1196 +* should reconnect if component trees differ but resulting markup is the same
1197
1198 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
1199 * updates a mounted text component in place
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+469
@@ -174,6 +174,31 @@ function itThrowsWhenRendering(desc, testFn) {
174 );
175 }
176
177 +// renders serverElement to a string, sticks it into a DOM element, and then
178 +// tries to render clientElement on top of it. shouldMatch is a boolean
179 +// telling whether we should expect the markup to match or not.
180 +async function testMarkupMatch(serverElement, clientElement, shouldMatch) {
181 + const domElement = await serverRender(serverElement);
182 + resetModules();
183 + return renderIntoDom(
184 + clientElement,
185 + domElement.parentNode,
186 + shouldMatch ? 0 : 1,
187 + );
188 +}
189 +
190 +// expects that rendering clientElement on top of a server-rendered
191 +// serverElement does NOT raise a markup mismatch warning.
192 +function expectMarkupMatch(serverElement, clientElement) {
193 + return testMarkupMatch(serverElement, clientElement, true);
194 +}
195 +
196 +// expects that rendering clientElement on top of a server-rendered
197 +// serverElement DOES raise a markup mismatch warning.
198 +function expectMarkupMismatch(serverElement, clientElement) {
199 + return testMarkupMatch(serverElement, clientElement, false);
200 +}
201 +
202 // When there is a test that renders on server and then on client and expects a logged
203 // error, you want to see the error show up both on server and client. Unfortunately,
204 // React refuses to issue the same error twice to avoid clogging up the console.
@@ -996,4 +1021,448 @@ describe('ReactDOMServerIntegration', () => {
1021 itThrowsWhenRendering('string', render => render('foo'));
1022 });
1023 });
1024 +
1025 + describe('context', function() {
1026 + let PurpleContext, RedContext;
1027 + beforeEach(() => {
1028 + class Parent extends React.Component {
1029 + getChildContext() {
1030 + return {text: this.props.text};
1031 + }
1032 + render() {
1033 + return this.props.children;
1034 + }
1035 + }
1036 + Parent.childContextTypes = {text: React.PropTypes.string};
1037 +
1038 + PurpleContext = props => <Parent text="purple">{props.children}</Parent>;
1039 + RedContext = props => <Parent text="red">{props.children}</Parent>;
1040 + });
1041 +
1042 + itRenders('class child with context', async render => {
1043 + class ClassChildWithContext extends React.Component {
1044 + render() {
1045 + return <div>{this.context.text}</div>;
1046 + }
1047 + }
1048 + ClassChildWithContext.contextTypes = {text: React.PropTypes.string};
1049 +
1050 + const e = await render(
1051 + <PurpleContext><ClassChildWithContext /></PurpleContext>,
1052 + );
1053 + expect(e.textContent).toBe('purple');
1054 + });
1055 +
1056 + itRenders('stateless child with context', async render => {
1057 + function StatelessChildWithContext(props, context) {
1058 + return <div>{context.text}</div>;
1059 + }
1060 + StatelessChildWithContext.contextTypes = {text: React.PropTypes.string};
1061 +
1062 + const e = await render(
1063 + <PurpleContext><StatelessChildWithContext /></PurpleContext>,
1064 + );
1065 + expect(e.textContent).toBe('purple');
1066 + });
1067 +
1068 + itRenders('class child without context', async render => {
1069 + class ClassChildWithoutContext extends React.Component {
1070 + render() {
1071 + // this should render blank; context isn't passed to this component.
1072 + return <div>{this.context.text}</div>;
1073 + }
1074 + }
1075 +
1076 + const e = await render(
1077 + <PurpleContext><ClassChildWithoutContext /></PurpleContext>,
1078 + );
1079 + expect(e.textContent).toBe('');
1080 + });
1081 +
1082 + itRenders('stateless child without context', async render => {
1083 + function StatelessChildWithoutContext(props, context) {
1084 + // this should render blank; context isn't passed to this component.
1085 + return <div>{context.text}</div>;
1086 + }
1087 +
1088 + const e = await render(
1089 + <PurpleContext><StatelessChildWithoutContext /></PurpleContext>,
1090 + );
1091 + expect(e.textContent).toBe('');
1092 + });
1093 +
1094 + itRenders('class child with wrong context', async render => {
1095 + class ClassChildWithWrongContext extends React.Component {
1096 + render() {
1097 + // this should render blank; context.text isn't passed to this component.
1098 + return <div id="classWrongChild">{this.context.text}</div>;
1099 + }
1100 + }
1101 + ClassChildWithWrongContext.contextTypes = {foo: React.PropTypes.string};
1102 +
1103 + const e = await render(
1104 + <PurpleContext><ClassChildWithWrongContext /></PurpleContext>,
1105 + );
1106 + expect(e.textContent).toBe('');
1107 + });
1108 +
1109 + itRenders('stateless child with wrong context', async render => {
1110 + function StatelessChildWithWrongContext(props, context) {
1111 + // this should render blank; context.text isn't passed to this component.
1112 + return <div id="statelessWrongChild">{context.text}</div>;
1113 + }
1114 + StatelessChildWithWrongContext.contextTypes = {
1115 + foo: React.PropTypes.string,
1116 + };
1117 +
1118 + const e = await render(
1119 + <PurpleContext><StatelessChildWithWrongContext /></PurpleContext>,
1120 + );
1121 + expect(e.textContent).toBe('');
1122 + });
1123 +
1124 + itRenders('with context passed through to a grandchild', async render => {
1125 + function Grandchild(props, context) {
1126 + return <div>{context.text}</div>;
1127 + }
1128 + Grandchild.contextTypes = {text: React.PropTypes.string};
1129 +
1130 + const Child = props => <Grandchild />;
1131 +
1132 + const e = await render(<PurpleContext><Child /></PurpleContext>);
1133 + expect(e.textContent).toBe('purple');
1134 + });
1135 +
1136 + itRenders('a child context overriding a parent context', async render => {
1137 + const Grandchild = (props, context) => {
1138 + return <div>{context.text}</div>;
1139 + };
1140 + Grandchild.contextTypes = {text: React.PropTypes.string};
1141 +
1142 + const e = await render(
1143 + <PurpleContext><RedContext><Grandchild /></RedContext></PurpleContext>,
1144 + );
1145 + expect(e.textContent).toBe('red');
1146 + });
1147 +
1148 + itRenders('a child context merged with a parent context', async render => {
1149 + class Parent extends React.Component {
1150 + getChildContext() {
1151 + return {text1: 'purple'};
1152 + }
1153 + render() {
1154 + return <Child />;
1155 + }
1156 + }
1157 + Parent.childContextTypes = {text1: React.PropTypes.string};
1158 +
1159 + class Child extends React.Component {
1160 + getChildContext() {
1161 + return {text2: 'red'};
1162 + }
1163 + render() {
1164 + return <Grandchild />;
1165 + }
1166 + }
1167 + Child.childContextTypes = {text2: React.PropTypes.string};
1168 +
1169 + const Grandchild = (props, context) => {
1170 + return (
1171 + <div>
1172 + <div id="first">{context.text1}</div>
1173 + <div id="second">{context.text2}</div>
1174 + </div>
1175 + );
1176 + };
1177 + Grandchild.contextTypes = {
1178 + text1: React.PropTypes.string,
1179 + text2: React.PropTypes.string,
1180 + };
1181 +
1182 + const e = await render(<Parent />);
1183 + expect(e.querySelector('#first').textContent).toBe('purple');
1184 + expect(e.querySelector('#second').textContent).toBe('red');
1185 + });
1186 +
1187 + itRenders(
1188 + 'with a call to componentWillMount before getChildContext',
1189 + async render => {
1190 + class WillMountContext extends React.Component {
1191 + getChildContext() {
1192 + return {text: this.state.text};
1193 + }
1194 + componentWillMount() {
1195 + this.setState({text: 'foo'});
1196 + }
1197 + render() {
1198 + return <Child />;
1199 + }
1200 + }
1201 + WillMountContext.childContextTypes = {text: React.PropTypes.string};
1202 +
1203 + const Child = (props, context) => {
1204 + return <div>{context.text}</div>;
1205 + };
1206 + Child.contextTypes = {text: React.PropTypes.string};
1207 +
1208 + const e = await render(<WillMountContext />);
1209 + expect(e.textContent).toBe('foo');
1210 + },
1211 + );
1212 +
1213 + itThrowsWhenRendering(
1214 + 'if getChildContext exists without childContextTypes',
1215 + render => {
1216 + class Component extends React.Component {
1217 + render() {
1218 + return <div />;
1219 + }
1220 + getChildContext() {
1221 + return {foo: 'bar'};
1222 + }
1223 + }
1224 + return render(<Component />);
1225 + },
1226 + );
1227 +
1228 + itThrowsWhenRendering(
1229 + 'if getChildContext returns a value not in childContextTypes',
1230 + render => {
1231 + class Component extends React.Component {
1232 + render() {
1233 + return <div />;
1234 + }
1235 + getChildContext() {
1236 + return {value1: 'foo', value2: 'bar'};
1237 + }
1238 + }
1239 + Component.childContextTypes = {value1: React.PropTypes.string};
1240 + return render(<Component />);
1241 + },
1242 + );
1243 + });
1244 +
1245 + describe('refs', function() {
1246 + it('should not run ref code on server', async () => {
1247 + let refCount = 0;
1248 + class RefsComponent extends React.Component {
1249 + render() {
1250 + return <div ref={e => refCount++} />;
1251 + }
1252 + }
1253 + await expectMarkupMatch(<RefsComponent />, <div />);
1254 + expect(refCount).toBe(0);
1255 + });
1256 +
1257 + it('should run ref code on client', async () => {
1258 + let refCount = 0;
1259 + class RefsComponent extends React.Component {
1260 + render() {
1261 + return <div ref={e => refCount++} />;
1262 + }
1263 + }
1264 + await expectMarkupMatch(<div />, <RefsComponent />);
1265 + expect(refCount).toBe(1);
1266 + });
1267 +
1268 + it('should send the correct element to ref functions on client', async () => {
1269 + let refElement = null;
1270 + class RefsComponent extends React.Component {
1271 + render() {
1272 + return <div ref={e => refElement = e} />;
1273 + }
1274 + }
1275 + const e = await clientRenderOnServerString(<RefsComponent />);
1276 + expect(refElement).not.toBe(null);
1277 + expect(refElement).toBe(e);
1278 + });
1279 +
1280 + it('should have string refs on client when rendered over server markup', async () => {
1281 + class RefsComponent extends React.Component {
1282 + render() {
1283 + return <div ref="myDiv" />;
1284 + }
1285 + }
1286 +
1287 + const markup = ReactDOMServer.renderToString(<RefsComponent />);
1288 + const root = document.createElement('div');
1289 + root.innerHTML = markup;
1290 + let component = null;
1291 + resetModules();
1292 + await asyncReactDOMRender(
1293 + <RefsComponent ref={e => component = e} />,
1294 + root,
1295 + );
1296 + expect(component.refs.myDiv).toBe(root.firstChild);
1297 + });
1298 + });
1299 +
1300 + describe('reconnecting to server markup', function() {
1301 + var EmptyComponent;
1302 + beforeEach(() => {
1303 + EmptyComponent = class extends React.Component {
1304 + render() {
1305 + return null;
1306 + }
1307 + };
1308 + });
1309 +
1310 + describe('elements', function() {
1311 + describe('reconnecting different component implementations', function() {
1312 + let ES6ClassComponent, PureComponent, bareElement;
1313 + beforeEach(() => {
1314 + // try each type of component on client and server.
1315 + ES6ClassComponent = class extends React.Component {
1316 + render() {
1317 + return <div id={this.props.id} />;
1318 + }
1319 + };
1320 + PureComponent = props => <div id={props.id} />;
1321 + bareElement = <div id="foobarbaz" />;
1322 + });
1323 +
1324 + it('should reconnect ES6 Class to ES6 Class', () =>
1325 + expectMarkupMatch(
1326 + <ES6ClassComponent id="foobarbaz" />,
1327 + <ES6ClassComponent id="foobarbaz" />,
1328 + ));
1329 +
1330 + it('should reconnect Pure Component to ES6 Class', () =>
1331 + expectMarkupMatch(
1332 + <ES6ClassComponent id="foobarbaz" />,
1333 + <PureComponent id="foobarbaz" />,
1334 + ));
1335 +
1336 + it('should reconnect Bare Element to ES6 Class', () =>
1337 + expectMarkupMatch(<ES6ClassComponent id="foobarbaz" />, bareElement));
1338 +
1339 + it('should reconnect ES6 Class to Pure Component', () =>
1340 + expectMarkupMatch(
1341 + <PureComponent id="foobarbaz" />,
1342 + <ES6ClassComponent id="foobarbaz" />,
1343 + ));
1344 +
1345 + it('should reconnect Pure Component to Pure Component', () =>
1346 + expectMarkupMatch(
1347 + <PureComponent id="foobarbaz" />,
1348 + <PureComponent id="foobarbaz" />,
1349 + ));
1350 +
1351 + it('should reconnect Bare Element to Pure Component', () =>
1352 + expectMarkupMatch(<PureComponent id="foobarbaz" />, bareElement));
1353 +
1354 + it('should reconnect ES6 Class to Bare Element', () =>
1355 + expectMarkupMatch(bareElement, <ES6ClassComponent id="foobarbaz" />));
1356 +
1357 + it('should reconnect Pure Component to Bare Element', () =>
1358 + expectMarkupMatch(bareElement, <PureComponent id="foobarbaz" />));
1359 +
1360 + it('should reconnect Bare Element to Bare Element', () =>
1361 + expectMarkupMatch(bareElement, bareElement));
1362 + });
1363 +
1364 + it('should error reconnecting different element types', () =>
1365 + expectMarkupMismatch(<div />, <span />));
1366 +
1367 + it('should error reconnecting missing attributes', () =>
1368 + expectMarkupMismatch(<div id="foo" />, <div />));
1369 +
1370 + it('should error reconnecting added attributes', () =>
1371 + expectMarkupMismatch(<div />, <div id="foo" />));
1372 +
1373 + it('should error reconnecting different attribute values', () =>
1374 + expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
1375 + });
1376 +
1377 + describe('text nodes', function() {
1378 + it('should error reconnecting different text', () =>
1379 + expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
1380 +
1381 + it('should reconnect a div with a number and string version of number', () =>
1382 + expectMarkupMatch(<div>{2}</div>, <div>2</div>));
1383 +
1384 + it('should error reconnecting different numbers', () =>
1385 + expectMarkupMismatch(<div>{2}</div>, <div>{3}</div>));
1386 +
1387 + it('should error reconnecting different number from text', () =>
1388 + expectMarkupMismatch(<div>{2}</div>, <div>3</div>));
1389 +
1390 + it('should error reconnecting different text in two code blocks', () =>
1391 + expectMarkupMismatch(
1392 + <div>{'Text1'}{'Text2'}</div>,
1393 + <div>{'Text1'}{'Text3'}</div>,
1394 + ));
1395 + });
1396 +
1397 + describe('element trees and children', function() {
1398 + it('should error reconnecting missing children', () =>
1399 + expectMarkupMismatch(<div><div /></div>, <div />));
1400 +
1401 + it('should error reconnecting added children', () =>
1402 + expectMarkupMismatch(<div />, <div><div /></div>));
1403 +
1404 + it('should error reconnecting more children', () =>
1405 + expectMarkupMismatch(<div><div /></div>, <div><div /><div /></div>));
1406 +
1407 + it('should error reconnecting fewer children', () =>
1408 + expectMarkupMismatch(<div><div /><div /></div>, <div><div /></div>));
1409 +
1410 + it('should error reconnecting reordered children', () =>
1411 + expectMarkupMismatch(
1412 + <div><div /><span /></div>,
1413 + <div><span /><div /></div>,
1414 + ));
1415 +
1416 + it('should error reconnecting a div with children separated by whitespace on the client', () =>
1417 + expectMarkupMismatch(
1418 + <div id="parent"><div id="child1" /><div id="child2" /></div>,
1419 + // prettier-ignore
1420 + <div id="parent"><div id="child1" /> <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
1421 + ));
1422 +
1423 + it('should error reconnecting a div with children separated by different whitespace on the server', () =>
1424 + expectMarkupMismatch(
1425 + // prettier-ignore
1426 + <div id="parent"><div id="child1" /> <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
1427 + <div id="parent"><div id="child1" /><div id="child2" /></div>,
1428 + ));
1429 +
1430 + it('should error reconnecting a div with children separated by different whitespace', () =>
1431 + expectMarkupMismatch(
1432 + <div id="parent"><div id="child1" /> <div id="child2" /></div>,
1433 + // prettier-ignore
1434 + <div id="parent"><div id="child1" /> <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
1435 + ));
1436 +
1437 + it('can distinguish an empty component from a dom node', () =>
1438 + expectMarkupMismatch(
1439 + <div><span /></div>,
1440 + <div><EmptyComponent /></div>,
1441 + ));
1442 +
1443 + it('can distinguish an empty component from an empty text component', () =>
1444 + expectMarkupMismatch(<div><EmptyComponent /></div>, <div>{''}</div>));
1445 +
1446 + it('should reconnect if component trees differ but resulting markup is the same', () => {
1447 + class Component1 extends React.Component {
1448 + render() {
1449 + return <span id="foobar" />;
1450 + }
1451 + }
1452 + class Component2 extends React.Component {
1453 + render() {
1454 + return <span id="foobar" />;
1455 + }
1456 + }
1457 + return expectMarkupMatch(<Component1 />, <Component2 />);
1458 + });
1459 + });
1460 +
1461 + // Markup Mismatches: misc
1462 + it('should error reconnecting a div with different dangerouslySetInnerHTML', () =>
1463 + expectMarkupMismatch(
1464 + <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
1465 + <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
1466 + ));
1467 + });
1468 });