@samitouri / QOS-React / commits / 3788654d57

Adding some server rendering unit tests. (#9055)

* Added a handful of SSR unit tests, ported from a previous pull request. * Fixing linting errors * Fixed a test helper function to properly report errors. * Un-nested the new rendering tests. Updated the fiber test passing/not passing lists. * Edited to comply with the react/jsx-space-before-closing eslint rule, which will soon be merged into master. * Response to code review from @spicyj. Moved tests to separate file, reworked wording of test names, corrected use of canUseDom, and simplified tests against tagName. Thanks for the help, @spicyj!

Sasha Aickin committed Mar 1, 2017 at 13:39 UTC 3788654d57933461961a0296afd924493129e51d
3 files changed +186
scripts/fiber/tests-passing-except-dev.txt
+6
@@ -134,6 +134,12 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
134 * should warn about class (ssr)
135 * should suggest property name if available (ssr)
136
137 +src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
138 +* renders a blank div with client render on top of bad server markup
139 +* renders a div with inline styles with client render on top of bad server markup
140 +* renders a self-closing tag with client render on top of bad server markup
141 +* renders a self-closing tag as a child with client render on top of bad server markup
142 +
143 src/renderers/dom/shared/__tests__/ReactMount-test.js
144 * should warn if mounting into dirty rendered markup
145 * should account for escaping on a checksum mismatch
scripts/fiber/tests-passing.txt
+14
@@ -1116,6 +1116,20 @@ src/renderers/dom/shared/__tests__/ReactDOMSVG-test.js
1116 * can render SVG into a non-React SVG tree
1117 * can render HTML into a foreignObject in non-React SVG tree
1118
1119 +src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1120 +* renders a blank div with server string render
1121 +* renders a blank div with clean client render
1122 +* renders a blank div with client render on top of good server markup
1123 +* renders a div with inline styles with server string render
1124 +* renders a div with inline styles with clean client render
1125 +* renders a div with inline styles with client render on top of good server markup
1126 +* renders a self-closing tag with server string render
1127 +* renders a self-closing tag with clean client render
1128 +* renders a self-closing tag with client render on top of good server markup
1129 +* renders a self-closing tag as a child with server string render
1130 +* renders a self-closing tag as a child with clean client render
1131 +* renders a self-closing tag as a child with client render on top of good server markup
1132 +
1133 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
1134 * updates a mounted text component in place
1135 * can be toggled in and out of the markup
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js new
+166
@@ -0,0 +1,166 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +'use strict';
13 +
14 +let ExecutionEnvironment;
15 +let React;
16 +let ReactDOM;
17 +let ReactDOMServer;
18 +
19 +// Helper functions for rendering tests
20 +// ====================================
21 +
22 +// performs fn asynchronously and expects count errors logged to console.error.
23 +// will fail the test if the count of errors logged is not equal to count.
24 +function expectErrors(fn, count) {
25 + if (console.error.calls && console.error.calls.reset) {
26 + console.error.calls.reset();
27 + } else {
28 + spyOn(console, 'error');
29 + }
30 +
31 + return fn().then((result) => {
32 + if (console.error.calls.count() !== count) {
33 + console.log(`We expected ${count} warning(s), but saw ${console.error.calls.count()} warning(s).`);
34 + if (console.error.calls.count() > 0) {
35 + console.log(`We saw these warnings:`);
36 + for (var i = 0; i < console.error.calls.count(); i++) {
37 + console.log(console.error.calls.argsFor(i)[0]);
38 + }
39 + }
40 + }
41 + expectDev(console.error.calls.count()).toBe(count);
42 + return result;
43 + });
44 +}
45 +
46 +// renders the reactElement into domElement, and expects a certain number of errors.
47 +// returns a Promise that resolves when the render is complete.
48 +function renderIntoDom(reactElement, domElement, errorCount = 0) {
49 + return expectErrors(
50 + () => new Promise((resolve) => {
51 + ExecutionEnvironment.canUseDOM = true;
52 + ReactDOM.render(reactElement, domElement, () => {
53 + ExecutionEnvironment.canUseDOM = false;
54 + resolve(domElement.firstChild);
55 + });
56 + }),
57 + errorCount
58 + );
59 +}
60 +
61 +// Renders text using SSR and then stuffs it into a DOM node; returns the DOM
62 +// element that corresponds with the reactElement.
63 +// Does not render on client or perform client-side revival.
64 +function serverRender(reactElement, errorCount = 0) {
65 + return expectErrors(
66 + () => Promise.resolve(ReactDOMServer.renderToString(reactElement)),
67 + errorCount)
68 + .then((markup) => {
69 + var domElement = document.createElement('div');
70 + domElement.innerHTML = markup;
71 + return domElement.firstChild;
72 + });
73 +}
74 +
75 +const clientCleanRender = (element, errorCount = 0) => {
76 + const div = document.createElement('div');
77 + return renderIntoDom(element, div, errorCount);
78 +};
79 +
80 +const clientRenderOnServerString = (element, errorCount = 0) => {
81 + return serverRender(element, errorCount).then((markup) => {
82 + var domElement = document.createElement('div');
83 + domElement.innerHTML = markup;
84 + return renderIntoDom(element, domElement, errorCount);
85 + });
86 +};
87 +
88 +const clientRenderOnBadMarkup = (element, errorCount = 0) => {
89 + var domElement = document.createElement('div');
90 + domElement.innerHTML = '<div id="badIdWhichWillCauseMismatch" data-reactroot="" data-reactid="1"></div>';
91 + return renderIntoDom(element, domElement, errorCount + 1);
92 +};
93 +
94 +// runs a DOM rendering test as four different tests, with four different rendering
95 +// scenarios:
96 +// -- render to string on server
97 +// -- render on client without any server markup "clean client render"
98 +// -- render on client on top of good server-generated string markup
99 +// -- render on client on top of bad server-generated markup
100 +//
101 +// testFn is a test that has one arg, which is a render function. the render
102 +// function takes in a ReactElement and an optional expected error count and
103 +// returns a promise of a DOM Element.
104 +//
105 +// You should only perform tests that examine the DOM of the results of
106 +// render; you should not depend on the interactivity of the returned DOM element,
107 +// as that will not work in the server string scenario.
108 +function itRenders(desc, testFn) {
109 + it(`renders ${desc} with server string render`,
110 + () => testFn(serverRender));
111 + itClientRenders(desc, testFn);
112 +}
113 +
114 +// run testFn in three different rendering scenarios:
115 +// -- render on client without any server markup "clean client render"
116 +// -- render on client on top of good server-generated string markup
117 +// -- render on client on top of bad server-generated markup
118 +//
119 +// testFn is a test that has one arg, which is a render function. the render
120 +// function takes in a ReactElement and an optional expected error count and
121 +// returns a promise of a DOM Element.
122 +//
123 +// Since all of the renders in this function are on the client, you can test interactivity,
124 +// unlike with itRenders.
125 +function itClientRenders(desc, testFn) {
126 + it(`renders ${desc} with clean client render`,
127 + () => testFn(clientCleanRender));
128 + it(`renders ${desc} with client render on top of good server markup`,
129 + () => testFn(clientRenderOnServerString));
130 + it(`renders ${desc} with client render on top of bad server markup`,
131 + () => testFn(clientRenderOnBadMarkup));
132 +}
133 +
134 +describe('ReactDOMServerIntegration', () => {
135 + beforeEach(() => {
136 + jest.resetModuleRegistry();
137 + React = require('React');
138 + ReactDOM = require('ReactDOM');
139 + ReactDOMServer = require('ReactDOMServer');
140 +
141 + ExecutionEnvironment = require('ExecutionEnvironment');
142 + ExecutionEnvironment.canUseDOM = false;
143 + });
144 +
145 + describe('basic rendering', function() {
146 + itRenders('a blank div', render =>
147 + render(<div />).then(e => expect(e.tagName).toBe('DIV')));
148 +
149 + itRenders('a div with inline styles', render =>
150 + render(<div style={{color:'red', width:'30px'}} />).then(e => {
151 + expect(e.style.color).toBe('red');
152 + expect(e.style.width).toBe('30px');
153 + })
154 + );
155 +
156 + itRenders('a self-closing tag', render =>
157 + render(<br />).then(e => expect(e.tagName).toBe('BR')));
158 +
159 + itRenders('a self-closing tag as a child', render =>
160 + render(<div><br /></div>).then(e => {
161 + expect(e.childNodes.length).toBe(1);
162 + expect(e.firstChild.tagName).toBe('BR');
163 + })
164 + );
165 + });
166 +});