Repository: facebook/react
Pull request: Use `act()` in ReactFabric tests

## Summary

Refactors `ReactFabric-test.internal.js` to use `act()` when invoking `ReactFabric.render`, where possible. This ensures that effects and reconciliation are fully operational before making assertions.

## Test Plan

Run `yarn test ReactFabric-test.internal.js`.

Changed files:
filename: packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js
patch: @@ -16,6 +16,7 @@ let createReactNativeComponentClass;
 let UIManager;
 let StrictMode;
 let TextInputState;
+let act;
 
 const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
   'Warning: setNativeProps is not currently supported in Fabric';
@@ -47,6 +48,9 @@ describe('ReactFabric', () => {
       .ReactNativeViewConfigRegistry.register;
     TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
       .TextInputState;
+
+    const ReactTestRenderer = require('react-test-renderer');
+    act = ReactTestRenderer.act;
   });
 
   it('should be able to create and render a native component', () => {
@@ -55,7 +59,9 @@ describe('ReactFabric', () => {
       uiViewClassName: 'RCTView',
     }));
 
-    ReactFabric.render(<View foo="test" />, 1);
+    act(() => {
+      ReactFabric.render(<View foo="test" />, 1);
+    });
     expect(nativeFabricUIManager.createNode).toBeCalled();
     expect(nativeFabricUIManager.appendChild).not.toBeCalled();
     expect(nativeFabricUIManager.completeRoot).toBeCalled();
@@ -71,11 +77,15 @@ describe('ReactFabric', () => {
 
     nativeFabricUIManager.createNode.mockReturnValue(firstNode);
 
-    ReactFabric.render(<View foo="foo" />, 11);
+    act(() => {
+      ReactFabric.render(<View foo="foo" />, 11);
+    });
 
     expect(nativeFabricUIManager.createNode).toHaveBeenCalledTimes(1);
 
-    ReactFabric.render(<View foo="bar" />, 11);
+    act(() => {
+      ReactFabric.render(<View foo="bar" />, 11);
+    });
 
     expect(nativeFabricUIManager.createNode).toHaveBeenCalledTimes(1);
     expect(nativeFabricUIManager.cloneNodeWithNewProps).toHaveBeenCalledTimes(
@@ -97,7 +107,9 @@ describe('ReactFabric', () => {
       uiViewClassName: 'RCTText',
     }));
 
-    ReactFabric.render(<Text foo="a">1</Text>, 11);
+    act(() => {
+      ReactFabric.render(<Text foo="a">1</Text>, 11);
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewChildren).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewProps).not.toBeCalled();
@@ -106,7 +118,9 @@ describe('ReactFabric', () => {
     ).not.toBeCalled();
 
     // If no properties have changed, we shouldn't call cloneNode.
-    ReactFabric.render(<Text foo="a">1</Text>, 11);
+    act(() => {
+      ReactFabric.render(<Text foo="a">1</Text>, 11);
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewChildren).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewProps).not.toBeCalled();
@@ -115,7 +129,9 @@ describe('ReactFabric', () => {
     ).not.toBeCalled();
 
     // Only call cloneNode for the changed property (and not for text).
-    ReactFabric.render(<Text foo="b">1</Text>, 11);
+    act(() => {
+      ReactFabric.render(<Text foo="b">1</Text>, 11);
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewChildren).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewProps).toHaveBeenCalledTimes(
@@ -126,7 +142,9 @@ describe('ReactFabric', () => {
     ).not.toBeCalled();
 
     // Only call cloneNode for the changed text (and no other properties).
-    ReactFabric.render(<Text foo="b">2</Text>, 11);
+    act(() => {
+      ReactFabric.render(<Text foo="b">2</Text>, 11);
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(
       nativeFabricUIManager.cloneNodeWithNewChildren,
@@ -139,7 +157,9 @@ describe('ReactFabric', () => {
     ).not.toBeCalled();
 
     // Call cloneNode for both changed text and properties.
-    ReactFabric.render(<Text foo="c">3</Text>, 11);
+    act(() => {
+      ReactFabric.render(<Text foo="c">3</Text>, 11);
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(
       nativeFabricUIManager.cloneNodeWithNewChildren,
@@ -158,25 +178,29 @@ describe('ReactFabric', () => {
       uiViewClassName: 'RCTText',
     }));
 
-    ReactFabric.render(
-      <Text foo="a" bar="a">
-        1
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text foo="a" bar="a">
+          1
+        </Text>,
+        11,
+      );
+    });
     expect(nativeFabricUIManager.cloneNode).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewChildren).not.toBeCalled();
     expect(nativeFabricUIManager.cloneNodeWithNewProps).not.toBeCalled();
     expect(
       nativeFabricUIManager.cloneNodeWithNewChildrenAndProps,
     ).not.toBeCalled();
 
-    ReactFabric.render(
-      <Text foo="a" bar="b">
-        1
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text foo="a" bar="b">
+          1
+        </Text>,
+        11,
+      );
+    });
     expect(
       nativeFabricUIManager.cloneNodeWithNewProps.mock.calls[0][1],
     ).toEqual({
@@ -186,12 +210,14 @@ describe('ReactFabric', () => {
       nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
     ).toMatchSnapshot();
 
-    ReactFabric.render(
-      <Text foo="b" bar="b">
-        2
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text foo="b" bar="b">
+          2
+        </Text>,
+        11,
+      );
+    });
     expect(
       nativeFabricUIManager.cloneNodeWithNewChildrenAndProps.mock.calls[0][1],
     ).toEqual({
@@ -211,15 +237,17 @@ describe('ReactFabric', () => {
     UIManager.updateView.mockReset();
 
     let viewRef;
-    ReactFabric.render(
-      <View
-        foo="bar"
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View
+          foo="bar"
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
     expect(UIManager.updateView).not.toBeCalled();
 
     expect(() => {
@@ -247,14 +275,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.dispatchCommand.mockClear();
 
     let viewRef;
-    ReactFabric.render(
-      <View
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
     ReactFabric.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
@@ -274,14 +304,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.dispatchCommand.mockReset();
 
     let viewRef;
-    ReactFabric.render(
-      <BasicClass
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <BasicClass
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
     expect(() => {
@@ -302,14 +334,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.sendAccessibilityEvent.mockClear();
 
     let viewRef;
-    ReactFabric.render(
-      <View
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled();
     ReactFabric.sendAccessibilityEvent(viewRef, 'focus');
@@ -332,14 +366,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.sendAccessibilityEvent.mockReset();
 
     let viewRef;
-    ReactFabric.render(
-      <BasicClass
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <BasicClass
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled();
     expect(() => {
@@ -360,14 +396,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.measure.mockClear();
 
     let viewRef;
-    ReactFabric.render(
-      <View
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.measure).not.toBeCalled();
     const successCallback = jest.fn();
@@ -386,14 +424,16 @@ describe('ReactFabric', () => {
     nativeFabricUIManager.measureInWindow.mockClear();
 
     let viewRef;
-    ReactFabric.render(
-      <View
-        ref={ref => {
-          viewRef = ref;
-        }}
-      />,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View
+          ref={ref => {
+            viewRef = ref;
+          }}
+        />,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
     const successCallback = jest.fn();
@@ -413,22 +453,24 @@ describe('ReactFabric', () => {
 
     let viewRef;
     let otherRef;
-    ReactFabric.render(
-      <View>
-        <View
-          foo="bar"
-          ref={ref => {
-            viewRef = ref;
-          }}
-        />
-        <View
-          ref={ref => {
-            otherRef = ref;
-          }}
-        />
-      </View>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View>
+          <View
+            foo="bar"
+            ref={ref => {
+              viewRef = ref;
+            }}
+          />
+          <View
+            ref={ref => {
+              otherRef = ref;
+            }}
+          />
+        </View>,
+        11,
+      );
+    });
 
     expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
     const successCallback = jest.fn();
@@ -483,12 +525,16 @@ describe('ReactFabric', () => {
     const before = 'abcdefghijklmnopqrst';
     const after = 'mxhpgwfralkeoivcstzy';
 
-    ReactFabric.render(<Component chars={before} />, 11);
+    act(() => {
+      ReactFabric.render(<Component chars={before} />, 11);
+    });
     expect(
       nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
     ).toMatchSnapshot();
 
-    ReactFabric.render(<Component chars={after} />, 11);
+    act(() => {
+      ReactFabric.render(<Component chars={after} />, 11);
+    });
     expect(
       nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
     ).toMatchSnapshot();
@@ -521,12 +567,14 @@ describe('ReactFabric', () => {
 
     const ref = React.createRef();
     // Wrap in a host node.
-    ReactFabric.render(
-      <View>
-        <Component ref={ref} />
-      </View>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View>
+          <Component ref={ref} />
+        </View>,
+        11,
+      );
+    });
     expect(
       nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
     ).toMatchSnapshot();
@@ -552,7 +600,9 @@ describe('ReactFabric', () => {
       }
     }
 
-    ReactFabric.render(<Component />, 11);
+    act(() => {
+      ReactFabric.render(<Component />, 11);
+    });
     expect(mockArgs.length).toEqual(0);
   });
 
@@ -572,12 +622,14 @@ describe('ReactFabric', () => {
       );
     });
 
-    ReactFabric.render(
-      <View foo="a">
-        <View foo="b" />
-      </View>,
-      22,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View foo="a">
+          <View foo="b" />
+        </View>,
+        22,
+      );
+    });
     expect(snapshots).toMatchSnapshot();
   });
 
@@ -595,19 +647,23 @@ describe('ReactFabric', () => {
       uiViewClassName: 'RCTView',
     }));
 
-    ReactFabric.render(
-      <Text>
-        <View />
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text>
+          <View />
+        </Text>,
+        11,
+      );
+    });
 
-    ReactFabric.render(
-      <Text>
-        <Image />
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text>
+          <Image />
+        </Text>,
+        11,
+      );
+    });
   });
 
   it('should throw for text not inside of a <Text> ancestor', () => {
@@ -624,18 +680,22 @@ describe('ReactFabric', () => {
       uiViewClassName: 'RCTView',
     }));
 
-    expect(() => ReactFabric.render(<View>this should warn</View>, 11)).toThrow(
-      'Text strings must be rendered within a <Text> component.',
-    );
+    expect(() => {
+      act(() => {
+        ReactFabric.render(<View>this should warn</View>, 11);
+      });
+    }).toThrow('Text strings must be rendered within a <Text> component.');
 
-    expect(() =>
-      ReactFabric.render(
-        <Text>
-          <ScrollView>hi hello hi</ScrollView>
-        </Text>,
-        11,
-      ),
-    ).toThrow('Text strings must be rendered within a <Text> component.');
+    expect(() => {
+      act(() => {
+        ReactFabric.render(
+          <Text>
+            <ScrollView>hi hello hi</ScrollView>
+          </Text>,
+          11,
+        );
+      });
+    }).toThrow('Text strings must be rendered within a <Text> component.');
   });
 
   it('should not throw for text inside of an indirect <Text> ancestor', () => {
@@ -646,12 +706,14 @@ describe('ReactFabric', () => {
 
     const Indirection = () => 'Hi';
 
-    ReactFabric.render(
-      <Text>
-        <Indirection />
-      </Text>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <Text>
+          <Indirection />
+        </Text>,
+        11,
+      );
+    });
   });
 
   it('dispatches events to the last committed props', () => {
@@ -668,7 +730,9 @@ describe('ReactFabric', () => {
     const touchStart = jest.fn();
     const touchStart2 = jest.fn();
 
-    ReactFabric.render(<View onTouchStart={touchStart} />, 11);
+    act(() => {
+      ReactFabric.render(<View onTouchStart={touchStart} />, 11);
+    });
 
     expect(nativeFabricUIManager.createNode.mock.calls.length).toBe(1);
     expect(nativeFabricUIManager.registerEventHandler.mock.calls.length).toBe(
@@ -698,7 +762,9 @@ describe('ReactFabric', () => {
     expect(touchStart).toBeCalled();
     expect(touchStart2).not.toBeCalled();
 
-    ReactFabric.render(<View onTouchStart={touchStart2} />, 11);
+    act(() => {
+      ReactFabric.render(<View onTouchStart={touchStart2} />, 11);
+    });
 
     // Intentionally dispatch to the same instanceHandle again.
     dispatchEvent(instanceHandle, 'topTouchStart', touchEvent);
@@ -742,33 +808,35 @@ describe('ReactFabric', () => {
     const ref1 = React.createRef();
     const ref2 = React.createRef();
 
-    ReactFabric.render(
-      <View id="parent">
-        <View
-          ref={ref1}
-          id="one"
-          onResponderStart={event => {
-            expect(ref1.current).not.toBeNull();
-            // Check for referential equality
-            expect(ref1.current).toBe(event.target);
-            expect(ref1.current).toBe(event.currentTarget);
-          }}
-          onStartShouldSetResponder={() => true}
-        />
-        <View
-          ref={ref2}
-          id="two"
-          onResponderStart={event => {
-            expect(ref2.current).not.toBeNull();
-            // Check for referential equality
-            expect(ref2.current).toBe(event.target);
-            expect(ref2.current).toBe(event.currentTarget);
-          }}
-          onStartShouldSetResponder={() => true}
-        />
-      </View>,
-      1,
-    );
+    act(() => {
+      ReactFabric.render(
+        <View id="parent">
+          <View
+            ref={ref1}
+            id="one"
+            onResponderStart={event => {
+              expect(ref1.current).not.toBeNull();
+              // Check for referential equality
+              expect(ref1.current).toBe(event.target);
+              expect(ref1.current).toBe(event.currentTarget);
+            }}
+            onStartShouldSetResponder={() => true}
+          />
+          <View
+            ref={ref2}
+            id="two"
+            onResponderStart={event => {
+              expect(ref2.current).not.toBeNull();
+              // Check for referential equality
+              expect(ref2.current).toBe(event.target);
+              expect(ref2.current).toBe(event.currentTarget);
+            }}
+            onStartShouldSetResponder={() => true}
+          />
+        </View>,
+        1,
+      );
+    });
 
     const [
       dispatchEvent,
@@ -823,7 +891,12 @@ describe('ReactFabric', () => {
       }
     }
 
-    ReactFabric.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
+    act(() => {
+      ReactFabric.render(
+        <ContainsStrictModeChild ref={n => (parent = n)} />,
+        11,
+      );
+    });
 
     let match;
     expect(
@@ -855,12 +928,14 @@ describe('ReactFabric', () => {
       }
     }
 
-    ReactFabric.render(
-      <StrictMode>
-        <IsInStrictMode ref={n => (parent = n)} />
-      </StrictMode>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <StrictMode>
+          <IsInStrictMode ref={n => (parent = n)} />
+        </StrictMode>,
+        11,
+      );
+    });
 
     let match;
     expect(
@@ -896,7 +971,12 @@ describe('ReactFabric', () => {
       }
     }
 
-    ReactFabric.render(<ContainsStrictModeChild ref={n => (parent = n)} />, 11);
+    act(() => {
+      ReactFabric.render(
+        <ContainsStrictModeChild ref={n => (parent = n)} />,
+        11,
+      );
+    });
 
     let match;
     expect(() => (match = ReactFabric.findNodeHandle(parent))).toErrorDev([
@@ -926,12 +1006,14 @@ describe('ReactFabric', () => {
       }
     }
 
-    ReactFabric.render(
-      <StrictMode>
-        <IsInStrictMode ref={n => (parent = n)} />
-      </StrictMode>,
-      11,
-    );
+    act(() => {
+      ReactFabric.render(
+        <StrictMode>
+          <IsInStrictMode ref={n => (parent = n)} />
+        </StrictMode>,
+        11,
+      );
+    });
 
     let match;
     expect(() => (match = ReactFabric.findNodeHandle(parent))).toErrorDev([
@@ -953,7 +1035,9 @@ describe('ReactFabric', () => {
     }));
 
     const viewRef = React.createRef();
-    ReactFabric.render(<View ref={viewRef} />, 11);
+    act(() => {
+      ReactFabric.render(<View ref={viewRef} />, 11);
+    });
 
     expect(TextInputState.blurTextInput).not.toBeCalled();
 
@@ -970,7 +1054,9 @@ describe('ReactFabric', () => {
     }));
 
     const viewRef = React.createRef();
-    ReactFabric.render(<View ref={viewRef} />, 11);
+    act(() => {
+      ReactFabric.render(<View ref={viewRef} />, 11);
+    });
 
     expect(TextInputState.focusTextInput).not.toBeCalled();