From 372cf23a397ebe6378a3b1e2a33c7239f5eb3a17 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 7 Mar 2023 10:45:45 -0500 Subject: [PATCH] Codemod act -> await act (2/?) Similar to the rationale for `waitFor` (see #26285), we should always await the result of an `act` call so that microtasks have a chance to fire. This only affects the internal `act` that we use in our repo, for now. In the public `act` API, we don't yet require this; however, we effectively will for any update that triggers suspense once `use` lands. So we likely will start warning in an upcoming minor. --- .../ReactDOMServerSuspense-test.internal.js | 2 +- .../ReactDOMSuspensePlaceholder-test.js | 12 +- .../__tests__/ReactFabric-test.internal.js | 120 +++++++++--------- .../ReactFabricHostComponent-test.internal.js | 72 ++++++----- .../ReactIncrementalScheduling-test.js | 2 +- .../__tests__/ReactNoopRendererAct-test.js | 2 +- .../src/__tests__/ReactOffscreen-test.js | 4 +- .../src/__tests__/ReactThenable-test.js | 2 +- .../__tests__/ReactTransitionTracing-test.js | 16 +-- .../useSyncExternalStoreNative-test.js | 8 +- .../useSyncExternalStoreShared-test.js | 96 +++++++------- 11 files changed, 168 insertions(+), 168 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerSuspense-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSuspense-test.internal.js index 535de142b36e2..78672c05f61c2 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerSuspense-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerSuspense-test.internal.js @@ -161,7 +161,7 @@ describe('ReactDOMServerSuspense', () => { expect(divB.tagName).toBe('DIV'); expect(divB.textContent).toBe('B'); - act(() => { + await act(async () => { ReactDOMClient.hydrateRoot(parent, example); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js index 3fc477416d8f4..129ae2746dc21 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js @@ -153,7 +153,7 @@ describe('ReactDOMSuspensePlaceholder', () => { ); } - act(() => { + await act(async () => { ReactDOM.render(, container); }); expect(container.innerHTML).toEqual( @@ -161,15 +161,7 @@ describe('ReactDOMSuspensePlaceholder', () => { '"display: none;">Loading...', ); - act(() => setIsVisible(true)); - expect(container.innerHTML).toEqual( - 'SiblingLoading...', - ); - - await advanceTimers(500); - - Scheduler.unstable_flushAll(); + await act(async () => setIsVisible(true)); expect(container.innerHTML).toEqual( 'SiblingAsync', 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 2e5d41afa0212..7b7731018eb83 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -44,13 +44,13 @@ describe('ReactFabric', () => { act = require('jest-react').act; }); - it('should be able to create and render a native component', () => { + it('should be able to create and render a native component', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', })); - act(() => { + await act(async () => { ReactFabric.render(, 1); }); expect(nativeFabricUIManager.createNode).toBeCalled(); @@ -58,7 +58,7 @@ describe('ReactFabric', () => { expect(nativeFabricUIManager.completeRoot).toBeCalled(); }); - it('should be able to create and update a native component', () => { + it('should be able to create and update a native component', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -68,13 +68,13 @@ describe('ReactFabric', () => { nativeFabricUIManager.createNode.mockReturnValue(firstNode); - act(() => { + await act(async () => { ReactFabric.render(, 11); }); expect(nativeFabricUIManager.createNode).toHaveBeenCalledTimes(1); - act(() => { + await act(async () => { ReactFabric.render(, 11); }); @@ -92,13 +92,13 @@ describe('ReactFabric', () => { }); }); - it('should not call FabricUIManager.cloneNode after render for properties that have not changed', () => { + it('should not call FabricUIManager.cloneNode after render for properties that have not changed', async () => { const Text = createReactNativeComponentClass('RCTText', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTText', })); - act(() => { + await act(async () => { ReactFabric.render(1, 11); }); expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); @@ -109,7 +109,7 @@ describe('ReactFabric', () => { ).not.toBeCalled(); // If no properties have changed, we shouldn't call cloneNode. - act(() => { + await act(async () => { ReactFabric.render(1, 11); }); expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); @@ -120,7 +120,7 @@ describe('ReactFabric', () => { ).not.toBeCalled(); // Only call cloneNode for the changed property (and not for text). - act(() => { + await act(async () => { ReactFabric.render(1, 11); }); expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); @@ -133,7 +133,7 @@ describe('ReactFabric', () => { ).not.toBeCalled(); // Only call cloneNode for the changed text (and no other properties). - act(() => { + await act(async () => { ReactFabric.render(2, 11); }); expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); @@ -148,7 +148,7 @@ describe('ReactFabric', () => { ).not.toBeCalled(); // Call cloneNode for both changed text and properties. - act(() => { + await act(async () => { ReactFabric.render(3, 11); }); expect(nativeFabricUIManager.cloneNode).not.toBeCalled(); @@ -163,13 +163,13 @@ describe('ReactFabric', () => { ).toHaveBeenCalledTimes(1); }); - it('should only pass props diffs to FabricUIManager.cloneNode', () => { + it('should only pass props diffs to FabricUIManager.cloneNode', async () => { const Text = createReactNativeComponentClass('RCTText', () => ({ validAttributes: {foo: true, bar: true}, uiViewClassName: 'RCTText', })); - act(() => { + await act(async () => { ReactFabric.render( 1 @@ -184,7 +184,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.cloneNodeWithNewChildrenAndProps, ).not.toBeCalled(); - act(() => { + await act(async () => { ReactFabric.render( 1 @@ -201,7 +201,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(), ).toMatchSnapshot(); - act(() => { + await act(async () => { ReactFabric.render( 2 @@ -219,7 +219,7 @@ describe('ReactFabric', () => { ).toMatchSnapshot(); }); - it('should call dispatchCommand for native refs', () => { + it('should call dispatchCommand for native refs', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -228,7 +228,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.dispatchCommand.mockClear(); let viewRef; - act(() => { + await act(async () => { ReactFabric.render( { @@ -249,7 +249,7 @@ describe('ReactFabric', () => { ); }); - it('should warn and no-op if calling dispatchCommand on non native refs', () => { + it('should warn and no-op if calling dispatchCommand on non native refs', async () => { class BasicClass extends React.Component { render() { return ; @@ -259,7 +259,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.dispatchCommand.mockReset(); let viewRef; - act(() => { + await act(async () => { ReactFabric.render( { @@ -280,7 +280,7 @@ describe('ReactFabric', () => { expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled(); }); - it('should call sendAccessibilityEvent for native refs', () => { + it('should call sendAccessibilityEvent for native refs', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -289,7 +289,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.sendAccessibilityEvent.mockClear(); let viewRef; - act(() => { + await act(async () => { ReactFabric.render( { @@ -311,7 +311,7 @@ describe('ReactFabric', () => { ); }); - it('should warn and no-op if calling sendAccessibilityEvent on non native refs', () => { + it('should warn and no-op if calling sendAccessibilityEvent on non native refs', async () => { class BasicClass extends React.Component { render() { return ; @@ -321,7 +321,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.sendAccessibilityEvent.mockReset(); let viewRef; - act(() => { + await act(async () => { ReactFabric.render( { @@ -363,7 +363,7 @@ describe('ReactFabric', () => { expect(a).toBe(c); }); - it('renders and reorders children', () => { + it('renders and reorders children', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {title: true}, uiViewClassName: 'RCTView', @@ -386,14 +386,14 @@ describe('ReactFabric', () => { const before = 'abcdefghijklmnopqrst'; const after = 'mxhpgwfralkeoivcstzy'; - act(() => { + await act(async () => { ReactFabric.render(, 11); }); expect( nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(), ).toMatchSnapshot(); - act(() => { + await act(async () => { ReactFabric.render(, 11); }); expect( @@ -401,7 +401,7 @@ describe('ReactFabric', () => { ).toMatchSnapshot(); }); - it('recreates host parents even if only children changed', () => { + it('recreates host parents even if only children changed', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {title: true}, uiViewClassName: 'RCTView', @@ -428,7 +428,7 @@ describe('ReactFabric', () => { const ref = React.createRef(); // Wrap in a host node. - act(() => { + await act(async () => { ReactFabric.render( @@ -450,7 +450,7 @@ describe('ReactFabric', () => { ).toMatchSnapshot(); }); - it('calls setState with no arguments', () => { + it('calls setState with no arguments', async () => { let mockArgs; class Component extends React.Component { componentDidMount() { @@ -461,13 +461,13 @@ describe('ReactFabric', () => { } } - act(() => { + await act(async () => { ReactFabric.render(, 11); }); expect(mockArgs.length).toEqual(0); }); - it('should call complete after inserting children', () => { + it('should call complete after inserting children', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -483,7 +483,7 @@ describe('ReactFabric', () => { ); }); - act(() => { + await act(async () => { ReactFabric.render( @@ -494,7 +494,7 @@ describe('ReactFabric', () => { expect(snapshots).toMatchSnapshot(); }); - it('should not throw when is used inside of a ancestor', () => { + it('should not throw when is used inside of a ancestor', async () => { const Image = createReactNativeComponentClass('RCTImage', () => ({ validAttributes: {}, uiViewClassName: 'RCTImage', @@ -508,7 +508,7 @@ describe('ReactFabric', () => { uiViewClassName: 'RCTView', })); - act(() => { + await act(async () => { ReactFabric.render( @@ -517,7 +517,7 @@ describe('ReactFabric', () => { ); }); - act(() => { + await act(async () => { ReactFabric.render( @@ -527,7 +527,7 @@ describe('ReactFabric', () => { }); }); - it('should console error for text not inside of a ancestor', () => { + it('should console error for text not inside of a ancestor', async () => { const ScrollView = createReactNativeComponentClass('RCTScrollView', () => ({ validAttributes: {}, uiViewClassName: 'RCTScrollView', @@ -541,14 +541,14 @@ describe('ReactFabric', () => { uiViewClassName: 'RCTView', })); - expect(() => { - act(() => { + await expect(async () => { + await act(async () => { ReactFabric.render(this should warn, 11); }); }).toErrorDev(['Text strings must be rendered within a component.']); - expect(() => { - act(() => { + await expect(async () => { + await act(async () => { ReactFabric.render( hi hello hi @@ -559,7 +559,7 @@ describe('ReactFabric', () => { }).toErrorDev(['Text strings must be rendered within a component.']); }); - it('should not throw for text inside of an indirect ancestor', () => { + it('should not throw for text inside of an indirect ancestor', async () => { const Text = createReactNativeComponentClass('RCTText', () => ({ validAttributes: {}, uiViewClassName: 'RCTText', @@ -567,7 +567,7 @@ describe('ReactFabric', () => { const Indirection = () => 'Hi'; - act(() => { + await act(async () => { ReactFabric.render( @@ -577,7 +577,7 @@ describe('ReactFabric', () => { }); }); - it('dispatches events to the last committed props', () => { + it('dispatches events to the last committed props', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {}, uiViewClassName: 'RCTView', @@ -591,7 +591,7 @@ describe('ReactFabric', () => { const touchStart = jest.fn(); const touchStart2 = jest.fn(); - act(() => { + await act(async () => { ReactFabric.render(, 11); }); @@ -617,7 +617,7 @@ describe('ReactFabric', () => { expect(touchStart).toBeCalled(); expect(touchStart2).not.toBeCalled(); - act(() => { + await act(async () => { ReactFabric.render(, 11); }); @@ -631,7 +631,7 @@ describe('ReactFabric', () => { }); describe('skipBubbling', () => { - it('should skip bubbling to ancestor if specified', () => { + it('should skip bubbling to ancestor if specified', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {}, uiViewClassName: 'RCTView', @@ -665,7 +665,7 @@ describe('ReactFabric', () => { const event = {}; - act(() => { + await act(async () => { ReactFabric.render( { }); }); - it('dispatches event with target as instance', () => { + it('dispatches event with target as instance', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: { id: true, @@ -752,7 +752,7 @@ describe('ReactFabric', () => { const ref1 = React.createRef(); const ref2 = React.createRef(); - act(() => { + await act(async () => { ReactFabric.render( { expect.assertions(6); }); - it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', () => { + it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -834,7 +834,7 @@ describe('ReactFabric', () => { } } - act(() => { + await act(async () => { ReactFabric.render( (parent = n)} />, 11, @@ -856,7 +856,7 @@ describe('ReactFabric', () => { expect(match).toBe(child); }); - it('findHostInstance_DEPRECATED should warn if passed a component that is inside StrictMode', () => { + it('findHostInstance_DEPRECATED should warn if passed a component that is inside StrictMode', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -871,7 +871,7 @@ describe('ReactFabric', () => { } } - act(() => { + await act(async () => { ReactFabric.render( (parent = n)} /> @@ -895,7 +895,7 @@ describe('ReactFabric', () => { expect(match).toBe(child); }); - it('findNodeHandle should warn if used to find a host component inside StrictMode', () => { + it('findNodeHandle should warn if used to find a host component inside StrictMode', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -914,7 +914,7 @@ describe('ReactFabric', () => { } } - act(() => { + await act(async () => { ReactFabric.render( (parent = n)} />, 11, @@ -934,7 +934,7 @@ describe('ReactFabric', () => { expect(match).toBe(child._nativeTag); }); - it('findNodeHandle should warn if passed a component that is inside StrictMode', () => { + it('findNodeHandle should warn if passed a component that is inside StrictMode', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -949,7 +949,7 @@ describe('ReactFabric', () => { } } - act(() => { + await act(async () => { ReactFabric.render( (parent = n)} /> @@ -971,7 +971,7 @@ describe('ReactFabric', () => { expect(match).toBe(child._nativeTag); }); - it('should no-op if calling sendAccessibilityEvent on unmounted refs', () => { + it('should no-op if calling sendAccessibilityEvent on unmounted refs', async () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, uiViewClassName: 'RCTView', @@ -980,7 +980,7 @@ describe('ReactFabric', () => { nativeFabricUIManager.sendAccessibilityEvent.mockReset(); let viewRef; - act(() => { + await act(async () => { ReactFabric.render( { @@ -991,7 +991,7 @@ describe('ReactFabric', () => { ); }); const dangerouslyRetainedViewRef = viewRef; - act(() => { + await act(async () => { ReactFabric.stopSurface(11); }); diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js index cace77f902482..d816471b3e8a8 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js @@ -30,7 +30,7 @@ beforeEach(() => { * If the corresponding array of keys is null, the returned element at that * index will also be null. */ -function mockRenderKeys(keyLists) { +async function mockRenderKeys(keyLists) { const ReactFabric = require('react-native-renderer/fabric'); const createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') @@ -43,10 +43,12 @@ function mockRenderKeys(keyLists) { uiViewClassName: 'RCTMockView', })); - return keyLists.map(keyList => { + const result = []; + for (let i = 0; i < keyLists.length; i++) { + const keyList = keyLists[i]; if (Array.isArray(keyList)) { const refs = keyList.map(key => undefined); - act(() => { + await act(async () => { ReactFabric.render( {keyList.map((key, index) => ( @@ -62,27 +64,31 @@ function mockRenderKeys(keyLists) { ); }); // Clone `refs` to ignore future passes. - return [...refs]; + result.push([...refs]); + continue; } if (keyList == null) { - act(() => { + await act(async () => { ReactFabric.stopSurface(mockContainerTag); }); - return null; + result.push(null); + continue; } throw new TypeError( `Invalid 'keyLists' element of type ${typeof keyList}.`, ); - }); + } + + return result; } describe('blur', () => { - test('blur() invokes TextInputState', () => { + test('blur() invokes TextInputState', async () => { const { TextInputState, } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - const [[fooRef]] = mockRenderKeys([['foo']]); + const [[fooRef]] = await mockRenderKeys([['foo']]); fooRef.blur(); @@ -91,12 +97,12 @@ describe('blur', () => { }); describe('focus', () => { - test('focus() invokes TextInputState', () => { + test('focus() invokes TextInputState', async () => { const { TextInputState, } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - const [[fooRef]] = mockRenderKeys([['foo']]); + const [[fooRef]] = await mockRenderKeys([['foo']]); fooRef.focus(); @@ -105,8 +111,8 @@ describe('focus', () => { }); describe('measure', () => { - test('component.measure(...) invokes callback', () => { - const [[fooRef]] = mockRenderKeys([['foo']]); + test('component.measure(...) invokes callback', async () => { + const [[fooRef]] = await mockRenderKeys([['foo']]); const callback = jest.fn(); fooRef.measure(callback); @@ -115,8 +121,8 @@ describe('measure', () => { expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]); }); - test('unmounted.measure(...) does nothing', () => { - const [[fooRef]] = mockRenderKeys([['foo'], null]); + test('unmounted.measure(...) does nothing', async () => { + const [[fooRef]] = await mockRenderKeys([['foo'], null]); const callback = jest.fn(); fooRef.measure(callback); @@ -127,8 +133,8 @@ describe('measure', () => { }); describe('measureInWindow', () => { - test('component.measureInWindow(...) invokes callback', () => { - const [[fooRef]] = mockRenderKeys([['foo']]); + test('component.measureInWindow(...) invokes callback', async () => { + const [[fooRef]] = await mockRenderKeys([['foo']]); const callback = jest.fn(); fooRef.measureInWindow(callback); @@ -137,8 +143,8 @@ describe('measureInWindow', () => { expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]); }); - test('unmounted.measureInWindow(...) does nothing', () => { - const [[fooRef]] = mockRenderKeys([['foo'], null]); + test('unmounted.measureInWindow(...) does nothing', async () => { + const [[fooRef]] = await mockRenderKeys([['foo'], null]); const callback = jest.fn(); fooRef.measureInWindow(callback); @@ -149,8 +155,8 @@ describe('measureInWindow', () => { }); describe('measureLayout', () => { - test('component.measureLayout(component, ...) invokes callback', () => { - const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar']]); + test('component.measureLayout(component, ...) invokes callback', async () => { + const [[fooRef, barRef]] = await mockRenderKeys([['foo', 'bar']]); const successCallback = jest.fn(); const failureCallback = jest.fn(); @@ -160,8 +166,8 @@ describe('measureLayout', () => { expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]); }); - test('unmounted.measureLayout(component, ...) does nothing', () => { - const [[fooRef, barRef]] = mockRenderKeys([ + test('unmounted.measureLayout(component, ...) does nothing', async () => { + const [[fooRef, barRef]] = await mockRenderKeys([ ['foo', 'bar'], ['foo', null], ]); @@ -174,8 +180,8 @@ describe('measureLayout', () => { expect(successCallback).not.toHaveBeenCalled(); }); - test('component.measureLayout(unmounted, ...) does nothing', () => { - const [[fooRef, barRef]] = mockRenderKeys([ + test('component.measureLayout(unmounted, ...) does nothing', async () => { + const [[fooRef, barRef]] = await mockRenderKeys([ ['foo', 'bar'], [null, 'bar'], ]); @@ -188,8 +194,8 @@ describe('measureLayout', () => { expect(successCallback).not.toHaveBeenCalled(); }); - test('unmounted.measureLayout(unmounted, ...) does nothing', () => { - const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar'], null]); + test('unmounted.measureLayout(unmounted, ...) does nothing', async () => { + const [[fooRef, barRef]] = await mockRenderKeys([['foo', 'bar'], null]); const successCallback = jest.fn(); const failureCallback = jest.fn(); @@ -201,8 +207,8 @@ describe('measureLayout', () => { }); describe('unstable_getBoundingClientRect', () => { - test('component.unstable_getBoundingClientRect() returns DOMRect', () => { - const [[fooRef]] = mockRenderKeys([['foo']]); + test('component.unstable_getBoundingClientRect() returns DOMRect', async () => { + const [[fooRef]] = await mockRenderKeys([['foo']]); const rect = fooRef.unstable_getBoundingClientRect(); @@ -217,8 +223,8 @@ describe('unstable_getBoundingClientRect', () => { }); }); - test('unmounted.unstable_getBoundingClientRect() returns empty DOMRect', () => { - const [[fooRef]] = mockRenderKeys([['foo'], null]); + test('unmounted.unstable_getBoundingClientRect() returns empty DOMRect', async () => { + const [[fooRef]] = await mockRenderKeys([['foo'], null]); const rect = fooRef.unstable_getBoundingClientRect(); @@ -228,12 +234,12 @@ describe('unstable_getBoundingClientRect', () => { }); describe('setNativeProps', () => { - test('setNativeProps(...) invokes setNativeProps on Fabric UIManager', () => { + test('setNativeProps(...) invokes setNativeProps on Fabric UIManager', async () => { const { UIManager, } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - const [[fooRef]] = mockRenderKeys([['foo']]); + const [[fooRef]] = await mockRenderKeys([['foo']]); fooRef.setNativeProps({foo: 'baz'}); expect(UIManager.updateView).not.toBeCalled(); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js index eb0044b30182e..1a76bacaa3b1e 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js @@ -102,7 +102,7 @@ describe('ReactIncrementalScheduling', () => { return text; } - act(() => { + await act(async () => { ReactNoop.renderToRootWithID(, 'a'); ReactNoop.renderToRootWithID(, 'b'); ReactNoop.renderToRootWithID(, 'c'); diff --git a/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js b/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js index 7be058b261e5f..8de0496bad9ed 100644 --- a/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js @@ -26,7 +26,7 @@ describe('internal act()', () => { } const calledLog = []; - act(() => { + await act(async () => { ReactNoop.render( { diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js index d35cacff3473c..937df702f1a84 100644 --- a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js @@ -1992,7 +1992,7 @@ describe('ReactOffscreen', () => { } const root = ReactNoop.createRoot(); - await act(() => { + await act(async () => { root.render(); }); @@ -2052,7 +2052,7 @@ describe('ReactOffscreen', () => { } const root = ReactNoop.createRoot(); - await act(() => { + await act(async () => { root.render(); }); assertLog(['attach child']); diff --git a/packages/react-reconciler/src/__tests__/ReactThenable-test.js b/packages/react-reconciler/src/__tests__/ReactThenable-test.js index ce5435d7fad06..fce0c0b173602 100644 --- a/packages/react-reconciler/src/__tests__/ReactThenable-test.js +++ b/packages/react-reconciler/src/__tests__/ReactThenable-test.js @@ -782,7 +782,7 @@ describe('ReactThenable', () => { } const root = ReactNoop.createRoot(); - await act(() => { + await act(async () => { root.render(); }); assertLog(['childShouldSuspend: false, showChild: true', 'Child']); diff --git a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js index 08c331aefb84b..1d240498acf94 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js @@ -2208,7 +2208,7 @@ describe('ReactInteractionTracing', () => { const root = ReactNoop.createRoot({ unstable_transitionCallbacks: transitionCallbacks, }); - await act(() => { + await act(async () => { startTransition(() => root.render(), {name: 'transition'}); ReactNoop.expire(1000); advanceTimers(1000); @@ -2221,7 +2221,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionStart(transition, 0)', ]); - await act(() => { + await act(async () => { resolveText('Text'); ReactNoop.expire(1000); advanceTimers(1000); @@ -2232,7 +2232,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionComplete(transition, 0, 2000)', ]); - await act(() => { + await act(async () => { resolveText('Hidden Text'); ReactNoop.expire(1000); advanceTimers(1000); @@ -2343,7 +2343,7 @@ describe('ReactInteractionTracing', () => { unstable_transitionCallbacks: transitionCallbacks, }); - await act(() => { + await act(async () => { startTransition(() => root.render(), {name: 'transition'}); ReactNoop.expire(1000); advanceTimers(1000); @@ -2361,7 +2361,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionProgress(transition, 0, 1000, [two])', ]); - await act(() => { + await act(async () => { resolveText('Text Two'); ReactNoop.expire(1000); advanceTimers(1000); @@ -2416,7 +2416,7 @@ describe('ReactInteractionTracing', () => { unstable_transitionCallbacks: getTransitionCallbacks('root two'), }); - await act(() => { + await act(async () => { startTransition(() => rootOne.render(), { name: 'transition one', }); @@ -2438,7 +2438,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionProgress(transition two, 0, 1000, [two]) /root two/', ]); - await act(() => { + await act(async () => { caches[0].resolve('Text one'); ReactNoop.expire(1000); advanceTimers(1000); @@ -2450,7 +2450,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionComplete(transition one, 0, 2000) /root one/', ]); - await act(() => { + await act(async () => { resolveText('Text two'); ReactNoop.expire(1000); advanceTimers(1000); diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js index ac4c0b8f4bb08..10e62b19a52c4 100644 --- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js +++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreNative-test.js @@ -117,7 +117,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => { } const root = ReactNoop.createRoot(); - await act(() => { + await act(async () => { root.render(); }); assertLog(['client']); @@ -161,13 +161,13 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => { } const root = ReactNoop.createRoot(); - act(() => root.render()); + await act(async () => root.render()); assertLog(['A0', 'B0']); expect(root).toMatchRenderedOutput('A0B0'); // Update b but not a - await act(() => { + await act(async () => { store.set({a: 0, b: 1}); }); // Only b re-renders @@ -175,7 +175,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => { expect(root).toMatchRenderedOutput('A0B1'); // Update a but not b - await act(() => { + await act(async () => { store.set({a: 1, b: 1}); }); // Only a re-renders diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js index 4c4bee7c05d45..f252711f62437 100644 --- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js +++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js @@ -142,19 +142,19 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => root.render()); + await act(async () => root.render()); assertLog(['Initial']); expect(container.textContent).toEqual('Initial'); - await act(() => { + await act(async () => { store.set('Updated'); }); assertLog(['Updated']); expect(container.textContent).toEqual('Updated'); }); - test('skips re-rendering if nothing changes', () => { + test('skips re-rendering if nothing changes', async () => { const store = createExternalStore('Initial'); function App() { @@ -164,13 +164,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['Initial']); expect(container.textContent).toEqual('Initial'); // Update to the same value - act(() => { + await act(async () => { store.set('Initial'); }); // Should not re-render @@ -192,19 +192,19 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => root.render()); + await act(async () => root.render()); assertLog([0]); expect(container.textContent).toEqual('0'); - await act(() => { + await act(async () => { storeA.set(1); }); assertLog([1]); expect(container.textContent).toEqual('1'); // Switch stores and update in the same batch - act(() => { + await act(async () => { ReactDOM.flushSync(() => { // This update will be disregarded storeA.set(2); @@ -216,7 +216,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('0'); // Update A - await act(() => { + await act(async () => { storeA.set(3); }); // Nothing happened, because we're no longer subscribed to A @@ -224,7 +224,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('0'); // Update B - await act(() => { + await act(async () => { storeB.set(1); }); assertLog([1]); @@ -254,13 +254,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['A0', 'B0']); expect(container.textContent).toEqual('A0B0'); // Update b but not a - await act(() => { + await act(async () => { store.set({a: 0, b: 1}); }); // Only b re-renders @@ -268,7 +268,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('A0B1'); // Update a but not b - await act(() => { + await act(async () => { store.set({a: 1, b: 1}); }); // Only a re-renders @@ -295,7 +295,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog([0, 'Passive effect: 0']); // Schedule an update. We'll intentionally not use `act` so that we can @@ -323,7 +323,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { }, ); - test('mutating the store in between render and commit when getSnapshot has changed', () => { + test('mutating the store in between render and commit when getSnapshot has changed', async () => { const store = createExternalStore({a: 1, b: 1}); const getSnapshotA = () => store.getState().a; @@ -365,11 +365,11 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['A1']); expect(container.textContent).toEqual('A1'); - act(() => { + await act(async () => { // Change getSnapshot and update the store in the same batch setStep(1); }); @@ -383,7 +383,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('B2'); }); - test('mutating the store in between render and commit when getSnapshot has _not_ changed', () => { + test('mutating the store in between render and commit when getSnapshot has _not_ changed', async () => { // Same as previous test, but `getSnapshot` does not change const store = createExternalStore({a: 1, b: 1}); @@ -423,13 +423,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['A1']); expect(container.textContent).toEqual('A1'); // This will cause a layout effect, and in the layout effect we'll update // the store - act(() => { + await act(async () => { setStep(1); }); assertLog([ @@ -463,7 +463,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => + await act(async () => root.render( <> @@ -474,14 +474,14 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { assertLog([0, 0]); expect(container.textContent).toEqual('00'); - await act(() => { + await act(async () => { store.set(1); }); assertLog([1, 1, 'Reset back to 0', 0, 0]); expect(container.textContent).toEqual('00'); }); - test('uses the latest getSnapshot, even if it changed in the same batch as a store update', () => { + test('uses the latest getSnapshot, even if it changed in the same batch as a store update', async () => { const store = createExternalStore({a: 0, b: 0}); const getSnapshotA = () => store.getState().a; @@ -497,11 +497,11 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog([0]); // Update the store and getSnapshot at the same time - act(() => { + await act(async () => { ReactDOM.flushSync(() => { setGetSnapshot(() => getSnapshotB); store.set({a: 1, b: 2}); @@ -546,7 +546,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const errorBoundary = React.createRef(null); const container = document.createElement('div'); const root = createRoot(container); - act(() => + await act(async () => root.render( @@ -557,7 +557,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('0'); // Update that throws in a getSnapshot. We can catch it with an error boundary. - await act(() => { + await act(async () => { store.set({value: 1, throwInGetSnapshot: true, throwInIsEqual: false}); }); if (gate(flags => !flags.enableUseSyncExternalStoreShim)) { @@ -573,7 +573,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('Error in getSnapshot'); }); - test('Infinite loop if getSnapshot keeps returning new reference', () => { + test('Infinite loop if getSnapshot keeps returning new reference', async () => { const store = createExternalStore({}); function App() { @@ -584,8 +584,10 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - expect(() => { - expect(() => act(() => root.render())).toThrow( + await expect(async () => { + expect(() => + ReactDOM.flushSync(async () => root.render()), + ).toThrow( 'Maximum update depth exceeded. This can happen when a component repeatedly ' + 'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' + 'the number of nested updates to prevent infinite loops.', @@ -610,15 +612,15 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { // Initial render that reads a snapshot of NaN. This is OK because we use // Object.is algorithm to compare values. - await act(() => root.render()); + await act(async () => root.render()); expect(container.textContent).toEqual('NaN'); // Update to real number - await act(() => store.set(123)); + await act(async () => store.set(123)); expect(container.textContent).toEqual('123'); // Update back to NaN - await act(() => store.set('not a number')); + await act(async () => store.set('not a number')); expect(container.textContent).toEqual('NaN'); }); @@ -646,13 +648,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['App', 'Selector', 'A0']); expect(container.textContent).toEqual('A0'); // Update the store - await act(() => { + await act(async () => { store.set({a: 1, b: 0}); }); assertLog([ @@ -705,13 +707,13 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - act(() => root.render()); + await act(async () => root.render()); assertLog(['A0', 'B0']); expect(container.textContent).toEqual('A0B0'); // Update b but not a - await act(() => { + await act(async () => { store.set({a: 0, b: 1}); }); // Only b re-renders @@ -719,7 +721,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('A0B1'); // Update a but not b - await act(() => { + await act(async () => { store.set({a: 1, b: 1}); }); // Only a re-renders @@ -752,7 +754,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const serverRenderedDiv = container.getElementsByTagName('div')[0]; if (gate(flags => !flags.enableUseSyncExternalStoreShim)) { - act(() => { + await act(async () => { ReactDOMClient.hydrateRoot(container, ); }); assertLog([ @@ -768,7 +770,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { // currently hydrating, so `getServerSnapshot` is not called on the // client. To avoid this server mismatch warning, user must account for // this themselves and return the correct value inside `getSnapshot`. - act(() => { + await act(async () => { expect(() => ReactDOM.hydrate(, container)).toErrorDev( 'Text content did not match', ); @@ -795,12 +797,12 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => root.render()); + await act(async () => root.render()); assertLog(['INITIAL']); expect(container.textContent).toEqual('INITIAL'); - await act(() => { + await act(async () => { store.set('Updated'); }); assertLog(['UPDATED']); @@ -858,12 +860,12 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => { + await act(async () => { root.render(); }); assertLog(['Inline selector', 'A', 'B', 'C', 'Sibling: 0']); - await act(() => { + await act(async () => { root.render(); }); assertLog([ @@ -918,7 +920,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => + await act(async () => root.render( @@ -963,7 +965,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); - await act(() => + await act(async () => root.render( @@ -974,7 +976,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('A'); await expect(async () => { - await act(() => { + await act(async () => { store.set({}); }); }).toWarnDev(