arkit
Version:
Visualises JavaScript, TypeScript and Flow codebases as meaningful and committable architecture diagrams
229 lines (200 loc) • 6.12 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 Suspense;
let ReactCache;
let ReactTestUtils;
let TextResource;
let act;
describe('ReactDOMSuspensePlaceholder', () => {
let container;
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactCache = require('react-cache');
ReactTestUtils = require('react-dom/test-utils');
act = ReactTestUtils.act;
Suspense = React.Suspense;
container = document.createElement('div');
document.body.appendChild(container);
TextResource = ReactCache.unstable_createResource(([text, ms = 0]) => {
return new Promise((resolve, reject) =>
setTimeout(() => {
resolve(text);
}, ms),
);
}, ([text, ms]) => text);
});
afterEach(() => {
document.body.removeChild(container);
});
function advanceTimers(ms) {
// Note: This advances Jest's virtual time but not React's. Use
// ReactNoop.expire for that.
if (typeof ms !== 'number') {
throw new Error('Must specify ms');
}
jest.advanceTimersByTime(ms);
// Wait until the end of the current tick
// We cannot use a timer since we're faking them
return Promise.resolve().then(() => {});
}
function Text(props) {
return props.text;
}
function AsyncText(props) {
const text = props.text;
TextResource.read([props.text, props.ms]);
return text;
}
it('hides and unhides timed out DOM elements', async () => {
let divs = [
React.createRef(null),
React.createRef(null),
React.createRef(null),
];
function App() {
return (
<Suspense maxDuration={500} fallback={<Text text="Loading..." />}>
<div ref={divs[0]}>
<Text text="A" />
</div>
<div ref={divs[1]}>
<AsyncText ms={1000} text="B" />
</div>
<div style={{display: 'block'}} ref={divs[2]}>
<Text text="C" />
</div>
</Suspense>
);
}
ReactDOM.render(<App />, container);
expect(divs[0].current.style.display).toEqual('none');
expect(divs[1].current.style.display).toEqual('none');
expect(divs[2].current.style.display).toEqual('none');
await advanceTimers(1000);
expect(divs[0].current.style.display).toEqual('');
expect(divs[1].current.style.display).toEqual('');
// This div's display was set with a prop.
expect(divs[2].current.style.display).toEqual('block');
});
it('hides and unhides timed out text nodes', async () => {
function App() {
return (
<Suspense maxDuration={500} fallback={<Text text="Loading..." />}>
<Text text="A" />
<AsyncText ms={1000} text="B" />
<Text text="C" />
</Suspense>
);
}
ReactDOM.render(<App />, container);
expect(container.textContent).toEqual('Loading...');
await advanceTimers(1000);
expect(container.textContent).toEqual('ABC');
});
it(
'outside concurrent mode, re-hides children if their display is updated ' +
'but the boundary is still showing the fallback',
async () => {
const {useState} = React;
let setIsVisible;
function Sibling({children}) {
const [isVisible, _setIsVisible] = useState(false);
setIsVisible = _setIsVisible;
return (
<span style={{display: isVisible ? 'inline' : 'none'}}>
{children}
</span>
);
}
function App() {
return (
<Suspense maxDuration={500} fallback={<Text text="Loading..." />}>
<Sibling>Sibling</Sibling>
<span>
<AsyncText ms={1000} text="Async" />
</span>
</Suspense>
);
}
act(() => {
ReactDOM.render(<App />, container);
});
expect(container.innerHTML).toEqual(
'<span style="display: none;">Sibling</span><span style="display: none;"></span>Loading...',
);
act(() => setIsVisible(true));
expect(container.innerHTML).toEqual(
'<span style="display: none;">Sibling</span><span style="display: none;"></span>Loading...',
);
await advanceTimers(1000);
expect(container.innerHTML).toEqual(
'<span style="display: inline;">Sibling</span><span style="">Async</span>',
);
},
);
// Regression test for https://github.com/facebook/react/issues/14188
it('can call findDOMNode() in a suspended component commit phase', async () => {
const log = [];
const Lazy = React.lazy(
() =>
new Promise(resolve =>
resolve({
default() {
return 'lazy';
},
}),
),
);
class Child extends React.Component {
componentDidMount() {
log.push('cDM ' + this.props.id);
ReactDOM.findDOMNode(this);
}
componentDidUpdate() {
log.push('cDU ' + this.props.id);
ReactDOM.findDOMNode(this);
}
render() {
return 'child';
}
}
const buttonRef = React.createRef();
class App extends React.Component {
state = {
suspend: false,
};
handleClick = () => {
this.setState({suspend: true});
};
render() {
return (
<React.Suspense fallback="Loading">
<Child id="first" />
<button ref={buttonRef} onClick={this.handleClick}>
Suspend
</button>
<Child id="second" />
{this.state.suspend && <Lazy />}
</React.Suspense>
);
}
}
ReactDOM.render(<App />, container);
expect(log).toEqual(['cDM first', 'cDM second']);
log.length = 0;
buttonRef.current.dispatchEvent(new MouseEvent('click', {bubbles: true}));
await Lazy;
expect(log).toEqual(['cDU first', 'cDU second']);
});
});