'use strict';
let React;
let ReactNoop;
let Scheduler;
let waitFor;
let waitForAll;
describe('ReactIncrementalReflection', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
waitForAll = InternalTestUtils.waitForAll;
});
function div(...children) {
children = children.map(c =>
typeof c === 'string' ? {text: c, hidden: false} : c,
);
return {type: 'div', children, prop: undefined, hidden: false};
}
function span(prop) {
return {type: 'span', children: [], prop, hidden: false};
}
it('finds no node before insertion and correct node before deletion', async () => {
let classInstance = null;
function findInstance(inst) {
const oldConsoleError = console.error;
console.error = jest.fn();
try {
return ReactNoop.findInstance(inst);
} finally {
console.error = oldConsoleError;
}
}
class Component extends React.Component {
UNSAFE_componentWillMount() {
classInstance = this;
Scheduler.log(['componentWillMount', findInstance(this)]);
}
componentDidMount() {
Scheduler.log(['componentDidMount', findInstance(this)]);
}
UNSAFE_componentWillUpdate() {
Scheduler.log(['componentWillUpdate', findInstance(this)]);
}
componentDidUpdate() {
Scheduler.log(['componentDidUpdate', findInstance(this)]);
}
componentWillUnmount() {
Scheduler.log(['componentWillUnmount', findInstance(this)]);
}
render() {
Scheduler.log('render');
return this.props.step < 2 ? (
<span ref={ref => (this.span = ref)} />
) : this.props.step === 2 ? (
<div ref={ref => (this.div = ref)} />
) : this.props.step === 3 ? null : this.props.step === 4 ? (
<div ref={ref => (this.span = ref)} />
) : null;
}
}
function Sibling() {
Scheduler.log('render sibling');
return <span />;
}
function Foo(props) {
return [<Component key="a" step={props.step} />, <Sibling key="b" />];
}
React.startTransition(() => {
ReactNoop.render(<Foo step={0} />);
});
await waitFor([['componentWillMount', null], 'render', 'render sibling']);
expect(classInstance).toBeDefined();
expect(findInstance(classInstance)).toBe(null);
await waitForAll([['componentDidMount', span()]]);
const hostSpan = classInstance.span;
expect(hostSpan).toBeDefined();
expect(findInstance(classInstance)).toBe(hostSpan);
ReactNoop.render(<Foo step={1} />);
await waitForAll([
['componentWillUpdate', hostSpan],
'render',
'render sibling',
['componentDidUpdate', hostSpan],
]);
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
React.startTransition(() => {
ReactNoop.render(<Foo step={2} />);
});
await waitFor([
['componentWillUpdate', hostSpan],
'render',
'render sibling',
]);
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
await waitForAll([['componentDidUpdate', div()]]);
const hostDiv = classInstance.div;
expect(hostDiv).toBeDefined();
expect(hostSpan).not.toBe(hostDiv);
expect(ReactNoop.findInstance(classInstance)).toBe(hostDiv);
React.startTransition(() => {
ReactNoop.render(<Foo step={3} />);
});
await waitFor([
['componentWillUpdate', hostDiv],
'render',
'render sibling',
]);
expect(ReactNoop.findInstance(classInstance)).toBe(hostDiv);
await waitForAll([['componentDidUpdate', null]]);
expect(ReactNoop.findInstance(classInstance)).toBe(null);
ReactNoop.render(<Foo step={4} />);
await waitForAll([
['componentWillUpdate', null],
'render',
'render sibling',
['componentDidUpdate', div()],
]);
ReactNoop.render([]);
await waitForAll([['componentWillUnmount', hostDiv]]);
});
});