diff --git a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js index 5bb90c24e65..e4976f84da0 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -1506,4 +1506,59 @@ describe('ReactFabric', () => { expect(publicInstanceAfterUnmount).toBe(null); }); + + it('should call completeRoot on suspend', async () => { + const RCTText = createReactNativeComponentClass('RCTText', () => ({ + validAttributes: {}, + uiViewClassName: 'RCTText', + })); + + const promise = new Promise(r => {}); + + let setState; + class Suspending extends React.Component { + state = {text: 1}; + + componentDidMount() { + setState = this.setState.bind(this); + } + + render() { + if (this.state.text >= 2) { + // We need to render once to actually be able to capture the setState function + throw promise; + } + return Step: {this.state.text}; + } + } + + const ref = React.createRef(); + await act(() => { + ReactFabric.render( + + + , + /* containerId */ 1, + /* callback */ null, + /* concurrentRoot */ true, + ); + }); + + expect(nativeFabricUIManager.completeRoot).toHaveBeenCalledTimes(1); + + // Cause the component to suspend + await act(() => { + setState({text: 2}); + }); + + expect(nativeFabricUIManager.completeRoot).toHaveBeenCalledTimes(3); // note: suspending seems to induce two calls + + // Triggering a state update for a suspended component that has the same fallback, + // and otherwise no changes to the tree are happening shouldn't call completeRoot again! + await act(() => { + setState({text: 3}); + }); + + expect(nativeFabricUIManager.completeRoot).toHaveBeenCalledTimes(3); + }); });