arkit
Version:
Visualises JavaScript, TypeScript and Flow codebases as meaningful and committable architecture diagrams
451 lines (389 loc) • 13.5 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
;
let React;
let ReactDOM;
let ReactDOMServer;
function getTestDocument(markup) {
const doc = document.implementation.createHTMLDocument('');
doc.open();
doc.write(
markup ||
'<!doctype html><html><meta charset=utf-8><title>test doc</title>',
);
doc.close();
return doc;
}
describe('rendering React components at document', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
});
describe('with old implicit hydration API', () => {
function expectDeprecationWarningWithFiber(callback) {
expect(callback).toLowPriorityWarnDev(
'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.',
{withoutStack: true},
);
}
it('should be able to adopt server markup', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{'Hello ' + this.props.hello}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Root hello="world" />);
const testDocument = getTestDocument(markup);
const body = testDocument.body;
expectDeprecationWarningWithFiber(() =>
ReactDOM.render(<Root hello="world" />, testDocument),
);
expect(testDocument.body.innerHTML).toBe('Hello world');
ReactDOM.render(<Root hello="moon" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello moon');
expect(body === testDocument.body).toBe(true);
});
it('should not be able to unmount component from document node', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Root />);
const testDocument = getTestDocument(markup);
expectDeprecationWarningWithFiber(() =>
ReactDOM.render(<Root />, testDocument),
);
expect(testDocument.body.innerHTML).toBe('Hello world');
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
});
it('should not be able to switch root constructors', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
}
}
class Component2 extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Goodbye world</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Component />);
const testDocument = getTestDocument(markup);
expectDeprecationWarningWithFiber(() =>
ReactDOM.render(<Component />, testDocument),
);
expect(testDocument.body.innerHTML).toBe('Hello world');
// This works but is probably a bad idea.
ReactDOM.render(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
});
it('should be able to mount into document', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(
<Component text="Hello world" />,
);
const testDocument = getTestDocument(markup);
expectDeprecationWarningWithFiber(() =>
ReactDOM.render(<Component text="Hello world" />, testDocument),
);
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('renders over an existing text child without throwing', () => {
const container = document.createElement('div');
container.textContent = 'potato';
ReactDOM.render(<div>parsnip</div>, container);
expect(container.textContent).toBe('parsnip');
// We don't expect a warning about new hydration API here because
// we aren't sure if the user meant to hydrate or replace a stub node.
// We would see a warning if the container had React-rendered HTML in it.
});
it('should give helpful errors on state desync', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(
<Component text="Goodbye world" />,
);
const testDocument = getTestDocument(markup);
expect(() => {
expect(() =>
ReactDOM.render(<Component text="Hello world" />, testDocument),
).toLowPriorityWarnDev(
'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.',
{withoutStack: true},
);
}).toWarnDev('Warning: Text content did not match.', {
withoutStack: true,
});
});
it('should throw on full document render w/ no markup', () => {
const testDocument = getTestDocument();
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
ReactDOM.render(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// We don't expect a warning about new hydration API here because
// we aren't sure if the user meant to hydrate or replace the document.
// We would see a warning if the document had React-rendered HTML in it.
});
it('supports findDOMNode on full-page components', () => {
const tree = (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
const markup = ReactDOMServer.renderToString(tree);
const testDocument = getTestDocument(markup);
let component;
expectDeprecationWarningWithFiber(() => {
component = ReactDOM.render(tree, testDocument);
});
expect(testDocument.body.innerHTML).toBe('Hello world');
expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
});
});
describe('with new explicit hydration API', () => {
it('should be able to adopt server markup', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{'Hello ' + this.props.hello}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Root hello="world" />);
const testDocument = getTestDocument(markup);
const body = testDocument.body;
ReactDOM.hydrate(<Root hello="world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
ReactDOM.hydrate(<Root hello="moon" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello moon');
expect(body === testDocument.body).toBe(true);
});
it('should not be able to unmount component from document node', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Root />);
const testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
});
it('should not be able to switch root constructors', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
}
}
class Component2 extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>Goodbye world</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(<Component />);
const testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// This works but is probably a bad idea.
ReactDOM.hydrate(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
});
it('should be able to mount into document', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(
<Component text="Hello world" />,
);
const testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('renders over an existing text child without throwing', () => {
const container = document.createElement('div');
container.textContent = 'potato';
expect(() => ReactDOM.hydrate(<div>parsnip</div>, container)).toWarnDev(
'Expected server HTML to contain a matching <div> in <div>.',
{withoutStack: true},
);
expect(container.textContent).toBe('parsnip');
});
it('should give helpful errors on state desync', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
const markup = ReactDOMServer.renderToString(
<Component text="Goodbye world" />,
);
const testDocument = getTestDocument(markup);
expect(() =>
ReactDOM.hydrate(<Component text="Hello world" />, testDocument),
).toWarnDev('Warning: Text content did not match.', {
withoutStack: true,
});
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('should render w/ no markup to full document', () => {
const testDocument = getTestDocument();
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{this.props.text}</body>
</html>
);
}
}
// getTestDocument() has an extra <meta> that we didn't render.
expect(() =>
ReactDOM.hydrate(<Component text="Hello world" />, testDocument),
).toWarnDev('Did not expect server HTML to contain a <meta> in <head>.', {
withoutStack: true,
});
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('supports findDOMNode on full-page components', () => {
const tree = (
<html>
<head>
<title>Hello World</title>
</head>
<body>Hello world</body>
</html>
);
const markup = ReactDOMServer.renderToString(tree);
const testDocument = getTestDocument(markup);
const component = ReactDOM.hydrate(tree, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
});
});
});