mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Codemod act -> await act (2/?) (#26335)
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.
This commit is contained in:
@@ -161,7 +161,7 @@ describe('ReactDOMServerSuspense', () => {
|
||||
expect(divB.tagName).toBe('DIV');
|
||||
expect(divB.textContent).toBe('B');
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactDOMClient.hydrateRoot(parent, example);
|
||||
});
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
|
||||
);
|
||||
}
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
expect(container.innerHTML).toEqual(
|
||||
@@ -161,15 +161,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
|
||||
'"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(500);
|
||||
|
||||
Scheduler.unstable_flushAll();
|
||||
await act(async () => setIsVisible(true));
|
||||
|
||||
expect(container.innerHTML).toEqual(
|
||||
'<span style="display: inline;">Sibling</span><span style="">Async</span>',
|
||||
|
||||
@@ -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(<View foo="test" />, 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(<View foo="foo" />, 11);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.createNode).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(<View foo="bar" />, 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(<Text foo="a">1</Text>, 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(<Text foo="a">1</Text>, 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(<Text foo="b">1</Text>, 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(<Text foo="b">2</Text>, 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(<Text foo="c">3</Text>, 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(
|
||||
<Text foo="a" bar="a">
|
||||
1
|
||||
@@ -184,7 +184,7 @@ describe('ReactFabric', () => {
|
||||
nativeFabricUIManager.cloneNodeWithNewChildrenAndProps,
|
||||
).not.toBeCalled();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text foo="a" bar="b">
|
||||
1
|
||||
@@ -201,7 +201,7 @@ describe('ReactFabric', () => {
|
||||
nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
|
||||
).toMatchSnapshot();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text foo="b" bar="b">
|
||||
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(
|
||||
<View
|
||||
ref={ref => {
|
||||
@@ -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 <React.Fragment />;
|
||||
@@ -259,7 +259,7 @@ describe('ReactFabric', () => {
|
||||
nativeFabricUIManager.dispatchCommand.mockReset();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<BasicClass
|
||||
ref={ref => {
|
||||
@@ -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(
|
||||
<View
|
||||
ref={ref => {
|
||||
@@ -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 <React.Fragment />;
|
||||
@@ -321,7 +321,7 @@ describe('ReactFabric', () => {
|
||||
nativeFabricUIManager.sendAccessibilityEvent.mockReset();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<BasicClass
|
||||
ref={ref => {
|
||||
@@ -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(<Component chars={before} />, 11);
|
||||
});
|
||||
expect(
|
||||
nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
|
||||
).toMatchSnapshot();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(<Component chars={after} />, 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(
|
||||
<View>
|
||||
<Component ref={ref} />
|
||||
@@ -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(<Component />, 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(
|
||||
<View foo="a">
|
||||
<View foo="b" />
|
||||
@@ -494,7 +494,7 @@ describe('ReactFabric', () => {
|
||||
expect(snapshots).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not throw when <View> is used inside of a <Text> ancestor', () => {
|
||||
it('should not throw when <View> is used inside of a <Text> ancestor', async () => {
|
||||
const Image = createReactNativeComponentClass('RCTImage', () => ({
|
||||
validAttributes: {},
|
||||
uiViewClassName: 'RCTImage',
|
||||
@@ -508,7 +508,7 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text>
|
||||
<View />
|
||||
@@ -517,7 +517,7 @@ describe('ReactFabric', () => {
|
||||
);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text>
|
||||
<Image />
|
||||
@@ -527,7 +527,7 @@ describe('ReactFabric', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should console error for text not inside of a <Text> ancestor', () => {
|
||||
it('should console error for text not inside of a <Text> 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(<View>this should warn</View>, 11);
|
||||
});
|
||||
}).toErrorDev(['Text strings must be rendered within a <Text> component.']);
|
||||
|
||||
expect(() => {
|
||||
act(() => {
|
||||
await expect(async () => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text>
|
||||
<ScrollView>hi hello hi</ScrollView>
|
||||
@@ -559,7 +559,7 @@ describe('ReactFabric', () => {
|
||||
}).toErrorDev(['Text strings must be rendered within a <Text> component.']);
|
||||
});
|
||||
|
||||
it('should not throw for text inside of an indirect <Text> ancestor', () => {
|
||||
it('should not throw for text inside of an indirect <Text> ancestor', async () => {
|
||||
const Text = createReactNativeComponentClass('RCTText', () => ({
|
||||
validAttributes: {},
|
||||
uiViewClassName: 'RCTText',
|
||||
@@ -567,7 +567,7 @@ describe('ReactFabric', () => {
|
||||
|
||||
const Indirection = () => 'Hi';
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(
|
||||
<Text>
|
||||
<Indirection />
|
||||
@@ -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(<View onTouchStart={touchStart} />, 11);
|
||||
});
|
||||
|
||||
@@ -617,7 +617,7 @@ describe('ReactFabric', () => {
|
||||
expect(touchStart).toBeCalled();
|
||||
expect(touchStart2).not.toBeCalled();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.render(<View onTouchStart={touchStart2} />, 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(
|
||||
<View
|
||||
onSkippedBubblingEventCapture={ancestorCapture}
|
||||
@@ -724,7 +724,7 @@ describe('ReactFabric', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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(
|
||||
<View id="parent">
|
||||
<View
|
||||
@@ -815,7 +815,7 @@ describe('ReactFabric', () => {
|
||||
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(
|
||||
<ContainsStrictModeChild ref={n => (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(
|
||||
<StrictMode>
|
||||
<IsInStrictMode ref={n => (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(
|
||||
<ContainsStrictModeChild ref={n => (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(
|
||||
<StrictMode>
|
||||
<IsInStrictMode ref={n => (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(
|
||||
<View
|
||||
ref={ref => {
|
||||
@@ -991,7 +991,7 @@ describe('ReactFabric', () => {
|
||||
);
|
||||
});
|
||||
const dangerouslyRetainedViewRef = viewRef;
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactFabric.stopSurface(11);
|
||||
});
|
||||
|
||||
|
||||
@@ -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(
|
||||
<MockView>
|
||||
{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();
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('ReactIncrementalScheduling', () => {
|
||||
return text;
|
||||
}
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactNoop.renderToRootWithID(<Text text="a:1" />, 'a');
|
||||
ReactNoop.renderToRootWithID(<Text text="b:1" />, 'b');
|
||||
ReactNoop.renderToRootWithID(<Text text="c:1" />, 'c');
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('internal act()', () => {
|
||||
}
|
||||
|
||||
const calledLog = [];
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
ReactNoop.render(
|
||||
<App
|
||||
callback={() => {
|
||||
|
||||
@@ -1992,7 +1992,7 @@ describe('ReactOffscreen', () => {
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
root.render(<App />);
|
||||
});
|
||||
|
||||
@@ -2052,7 +2052,7 @@ describe('ReactOffscreen', () => {
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
root.render(<App />);
|
||||
});
|
||||
assertLog(['attach child']);
|
||||
|
||||
@@ -782,7 +782,7 @@ describe('ReactThenable', () => {
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
root.render(<Parent />);
|
||||
});
|
||||
assertLog(['childShouldSuspend: false, showChild: true', 'Child']);
|
||||
|
||||
@@ -2208,7 +2208,7 @@ describe('ReactInteractionTracing', () => {
|
||||
const root = ReactNoop.createRoot({
|
||||
unstable_transitionCallbacks: transitionCallbacks,
|
||||
});
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
startTransition(() => root.render(<App />), {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(<App />), {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(<App name="one" />), {
|
||||
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);
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
root.render(<App />);
|
||||
});
|
||||
assertLog(['client']);
|
||||
@@ -161,13 +161,13 @@ describe('useSyncExternalStore (userspace shim, server rendering)', () => {
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
act(() => root.render(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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
|
||||
|
||||
@@ -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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
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(
|
||||
<>
|
||||
<Child1 />
|
||||
@@ -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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
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(
|
||||
<ErrorBoundary ref={errorBoundary}>
|
||||
<App />
|
||||
@@ -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(<App />))).toThrow(
|
||||
await expect(async () => {
|
||||
expect(() =>
|
||||
ReactDOM.flushSync(async () => root.render(<App />)),
|
||||
).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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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, <App />);
|
||||
});
|
||||
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(<App />, 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(<App />));
|
||||
await act(async () => root.render(<App />));
|
||||
|
||||
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(<App step={0} />);
|
||||
});
|
||||
assertLog(['Inline selector', 'A', 'B', 'C', 'Sibling: 0']);
|
||||
|
||||
await act(() => {
|
||||
await act(async () => {
|
||||
root.render(<App step={1} />);
|
||||
});
|
||||
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(
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
@@ -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(
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user