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!
This commit is contained in:
Sasha Aickin 2017-03-01 13:39:47 -08:00 committed by Ben Alpert
parent d1ee199492
commit 3788654d57
3 changed files with 186 additions and 0 deletions

View File

@ -134,6 +134,12 @@ src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
* should warn about class (ssr)
* should suggest property name if available (ssr)
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
* renders a blank div with client render on top of bad server markup
* renders a div with inline styles with client render on top of bad server markup
* renders a self-closing tag with client render on top of bad server markup
* renders a self-closing tag as a child 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
* should account for escaping on a checksum mismatch

View File

@ -1116,6 +1116,20 @@ src/renderers/dom/shared/__tests__/ReactDOMSVG-test.js
* can render SVG into a non-React SVG tree
* can render HTML into a foreignObject in non-React SVG tree
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
* renders a blank div with server string render
* renders a blank div with clean client render
* renders a blank div with client render on top of good server markup
* renders a div with inline styles with server string render
* renders a div with inline styles with clean client render
* renders a div with inline styles with client render on top of good server markup
* renders a self-closing tag with server string render
* renders a self-closing tag with clean client render
* renders a self-closing tag with client render on top of good server markup
* renders a self-closing tag as a child with server string render
* renders a self-closing tag as a child with clean client render
* renders a self-closing tag as a child with client render on top of good server markup
src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
* updates a mounted text component in place
* can be toggled in and out of the markup

View File

@ -0,0 +1,166 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
let ExecutionEnvironment;
let React;
let ReactDOM;
let ReactDOMServer;
// Helper functions for rendering tests
// ====================================
// performs fn asynchronously and expects count errors logged to console.error.
// will fail the test if the count of errors logged is not equal to count.
function expectErrors(fn, count) {
if (console.error.calls && console.error.calls.reset) {
console.error.calls.reset();
} else {
spyOn(console, 'error');
}
return fn().then((result) => {
if (console.error.calls.count() !== count) {
console.log(`We expected ${count} warning(s), but saw ${console.error.calls.count()} warning(s).`);
if (console.error.calls.count() > 0) {
console.log(`We saw these warnings:`);
for (var i = 0; i < console.error.calls.count(); i++) {
console.log(console.error.calls.argsFor(i)[0]);
}
}
}
expectDev(console.error.calls.count()).toBe(count);
return result;
});
}
// renders the reactElement into domElement, and expects a certain number of errors.
// returns a Promise that resolves when the render is complete.
function renderIntoDom(reactElement, domElement, errorCount = 0) {
return expectErrors(
() => new Promise((resolve) => {
ExecutionEnvironment.canUseDOM = true;
ReactDOM.render(reactElement, domElement, () => {
ExecutionEnvironment.canUseDOM = false;
resolve(domElement.firstChild);
});
}),
errorCount
);
}
// Renders text using SSR and then stuffs it into a DOM node; returns the DOM
// element that corresponds with the reactElement.
// Does not render on client or perform client-side revival.
function serverRender(reactElement, errorCount = 0) {
return expectErrors(
() => Promise.resolve(ReactDOMServer.renderToString(reactElement)),
errorCount)
.then((markup) => {
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return domElement.firstChild;
});
}
const clientCleanRender = (element, errorCount = 0) => {
const div = document.createElement('div');
return renderIntoDom(element, div, errorCount);
};
const clientRenderOnServerString = (element, errorCount = 0) => {
return serverRender(element, errorCount).then((markup) => {
var domElement = document.createElement('div');
domElement.innerHTML = markup;
return renderIntoDom(element, domElement, errorCount);
});
};
const clientRenderOnBadMarkup = (element, errorCount = 0) => {
var domElement = document.createElement('div');
domElement.innerHTML = '<div id="badIdWhichWillCauseMismatch" data-reactroot="" data-reactid="1"></div>';
return renderIntoDom(element, domElement, errorCount + 1);
};
// runs a DOM rendering test as four different tests, with four different rendering
// scenarios:
// -- render to string on server
// -- render on client without any server markup "clean client render"
// -- render on client on top of good server-generated string markup
// -- render on client on top of bad server-generated markup
//
// testFn is a test that has one arg, which is a render function. the render
// function takes in a ReactElement and an optional expected error count and
// returns a promise of a DOM Element.
//
// You should only perform tests that examine the DOM of the results of
// render; you should not depend on the interactivity of the returned DOM element,
// as that will not work in the server string scenario.
function itRenders(desc, testFn) {
it(`renders ${desc} with server string render`,
() => testFn(serverRender));
itClientRenders(desc, testFn);
}
// run testFn in three different rendering scenarios:
// -- render on client without any server markup "clean client render"
// -- render on client on top of good server-generated string markup
// -- render on client on top of bad server-generated markup
//
// testFn is a test that has one arg, which is a render function. the render
// function takes in a ReactElement and an optional expected error count and
// returns a promise of a DOM Element.
//
// Since all of the renders in this function are on the client, you can test interactivity,
// unlike with itRenders.
function itClientRenders(desc, testFn) {
it(`renders ${desc} with clean client render`,
() => testFn(clientCleanRender));
it(`renders ${desc} with client render on top of good server markup`,
() => testFn(clientRenderOnServerString));
it(`renders ${desc} with client render on top of bad server markup`,
() => testFn(clientRenderOnBadMarkup));
}
describe('ReactDOMServerIntegration', () => {
beforeEach(() => {
jest.resetModuleRegistry();
React = require('React');
ReactDOM = require('ReactDOM');
ReactDOMServer = require('ReactDOMServer');
ExecutionEnvironment = require('ExecutionEnvironment');
ExecutionEnvironment.canUseDOM = false;
});
describe('basic rendering', function() {
itRenders('a blank div', render =>
render(<div />).then(e => expect(e.tagName).toBe('DIV')));
itRenders('a div with inline styles', render =>
render(<div style={{color:'red', width:'30px'}} />).then(e => {
expect(e.style.color).toBe('red');
expect(e.style.width).toBe('30px');
})
);
itRenders('a self-closing tag', render =>
render(<br />).then(e => expect(e.tagName).toBe('BR')));
itRenders('a self-closing tag as a child', render =>
render(<div><br /></div>).then(e => {
expect(e.childNodes.length).toBe(1);
expect(e.firstChild.tagName).toBe('BR');
})
);
});
});