Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
* @jest-environment node
*/

'use strict';
Expand Down
255 changes: 1 addition & 254 deletions packages/react-dom/src/__tests__/ReactServerRendering-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
* @jest-environment node
*/

'use strict';

let React;
let ReactCallReturn;
let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;
let PropTypes;

function normalizeCodeLocInfo(str) {
Expand All @@ -25,8 +24,6 @@ describe('ReactDOMServer', () => {
jest.resetModules();
React = require('react');
ReactCallReturn = require('react-call-return');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
PropTypes = require('prop-types');
ReactDOMServer = require('react-dom/server');
});
Expand Down Expand Up @@ -157,243 +154,6 @@ describe('ReactDOMServer', () => {
runTest();
});

it('should have the correct mounting behavior (old hydrate API)', () => {
spyOnDev(console, 'warn');
spyOnDev(console, 'error');

let mountCount = 0;
let numClicks = 0;

class TestComponent extends React.Component {
componentDidMount() {
mountCount++;
}

click = () => {
numClicks++;
};

render() {
return (
<span ref="span" onClick={this.click}>
Name: {this.props.name}
</span>
);
}
}

const element = document.createElement('div');
ReactDOM.render(<TestComponent />, element);

let lastMarkup = element.innerHTML;

// Exercise the update path. Markup should not change,
// but some lifecycle methods should be run again.
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(1);

// Unmount and remount. We should get another mount event and
// we should get different markup, as the IDs are unique each time.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(2);
expect(element.innerHTML).not.toEqual(lastMarkup);

// Now kill the node and render it on top of server-rendered markup, as if
// we used server rendering. We should mount again, but the markup should
// be unchanged. We will append a sentinel at the end of innerHTML to be
// sure that innerHTML was not changed.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');

lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
element.innerHTML = lastMarkup;

let instance = ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(3);
if (__DEV__) {
expect(console.warn.calls.count()).toBe(1);
expect(console.warn.calls.argsFor(0)[0]).toContain(
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'will stop working in React v17. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
console.warn.calls.reset();
}
expect(element.innerHTML).toBe(lastMarkup);

// Ensure the events system works after mount into server markup
expect(numClicks).toEqual(0);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(1);

ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');

// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
instance = ReactDOM.render(<TestComponent name="y" />, element);
expect(mountCount).toEqual(4);
if (__DEV__) {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'Text content did not match. Server: "x" Client: "y"',
);
console.error.calls.reset();
}
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);

// Ensure the events system works after markup mismatch.
expect(numClicks).toEqual(1);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(2);
if (__DEV__) {
expect(console.warn.calls.count()).toBe(0);
expect(console.error.calls.count()).toBe(0);
}
});

it('should have the correct mounting behavior (new hydrate API)', () => {
spyOnDev(console, 'error');

let mountCount = 0;
let numClicks = 0;

class TestComponent extends React.Component {
componentDidMount() {
mountCount++;
}

click = () => {
numClicks++;
};

render() {
return (
<span ref="span" onClick={this.click}>
Name: {this.props.name}
</span>
);
}
}

const element = document.createElement('div');
ReactDOM.render(<TestComponent />, element);

let lastMarkup = element.innerHTML;

// Exercise the update path. Markup should not change,
// but some lifecycle methods should be run again.
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(1);

// Unmount and remount. We should get another mount event and
// we should get different markup, as the IDs are unique each time.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(2);
expect(element.innerHTML).not.toEqual(lastMarkup);

// Now kill the node and render it on top of server-rendered markup, as if
// we used server rendering. We should mount again, but the markup should
// be unchanged. We will append a sentinel at the end of innerHTML to be
// sure that innerHTML was not changed.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');

lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
element.innerHTML = lastMarkup;

let instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
expect(mountCount).toEqual(3);
expect(element.innerHTML).toBe(lastMarkup);

// Ensure the events system works after mount into server markup
expect(numClicks).toEqual(0);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(1);

ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');

// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
expect(mountCount).toEqual(4);
if (__DEV__) {
expect(console.error.calls.count()).toBe(1);
}
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);

// Ensure the events system works after markup mismatch.
expect(numClicks).toEqual(1);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(2);
});

// We have a polyfill for autoFocus on the client, but we intentionally don't
// want it to call focus() when hydrating because this can mess up existing
// focus before the JS has loaded.
it('should emit autofocus on the server but not focus() when hydrating', () => {
const element = document.createElement('div');
element.innerHTML = ReactDOMServer.renderToString(
<input autoFocus={true} />,
);
expect(element.firstChild.autofocus).toBe(true);

// It should not be called on mount.
element.firstChild.focus = jest.fn();
ReactDOM.hydrate(<input autoFocus={true} />, element);
expect(element.firstChild.focus).not.toHaveBeenCalled();

// Or during an update.
ReactDOM.render(<input autoFocus={true} />, element);
expect(element.firstChild.focus).not.toHaveBeenCalled();
});

it('should not focus on either server or client with autofocus={false}', () => {
const element = document.createElement('div');
element.innerHTML = ReactDOMServer.renderToString(
<input autoFocus={false} />,
);
expect(element.firstChild.autofocus).toBe(false);

element.firstChild.focus = jest.fn();
ReactDOM.hydrate(<input autoFocus={false} />, element);
expect(element.firstChild.focus).not.toHaveBeenCalled();

ReactDOM.render(<input autoFocus={false} />, element);
expect(element.firstChild.focus).not.toHaveBeenCalled();
});

// Regression test for https://github.com/facebook/react/issues/11726
it('should not focus on either server or client with autofocus={false} even if there is a markup mismatch', () => {
spyOnDev(console, 'error');

const element = document.createElement('div');
element.innerHTML = ReactDOMServer.renderToString(
<button autoFocus={false}>server</button>,
);
expect(element.firstChild.autofocus).toBe(false);

element.firstChild.focus = jest.fn();
ReactDOM.hydrate(<button autoFocus={false}>client</button>, element);

expect(element.firstChild.focus).not.toHaveBeenCalled();
if (__DEV__) {
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toBe(
'Warning: Text content did not match. Server: "server" Client: "client"',
);
}
});

it('should throw with silly args', () => {
expect(
ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),
Expand Down Expand Up @@ -797,20 +557,7 @@ describe('ReactDOMServer', () => {
}
});

it('should throw rendering portals on the server', () => {
const div = document.createElement('div');
expect(() => {
ReactDOMServer.renderToString(
<div>{ReactDOM.createPortal(<div />, div)}</div>,
);
}).toThrow(
'Portals are not currently supported by the server renderer. ' +
'Render them conditionally so that they only appear on the client render.',
);
});

it('should throw rendering call/return on the server', () => {
const div = document.createElement('div');
expect(() => {
ReactDOMServer.renderToString(
<div>{ReactCallReturn.unstable_createReturn(42)}</div>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
* @jest-environment node
*/

'use strict';
Expand Down
Loading