diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js
index 94e170e3346e6..38b74563c30a7 100644
--- a/packages/react-refresh/src/__tests__/ReactFresh-test.js
+++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js
@@ -17,13 +17,13 @@ let ReactDOMClient;
let ReactFreshRuntime;
let Scheduler;
let act;
-let internalAct;
let createReactClass;
let waitFor;
let assertLog;
describe('ReactFresh', () => {
let container;
+ let root;
beforeEach(() => {
if (__DEV__) {
@@ -34,8 +34,7 @@ describe('ReactFresh', () => {
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
- act = React.act;
- internalAct = require('internal-test-utils').act;
+ act = require('internal-test-utils').act;
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
@@ -47,6 +46,7 @@ describe('ReactFresh', () => {
new React.Component().updater,
);
container = document.createElement('div');
+ root = ReactDOMClient.createRoot(container);
document.body.appendChild(container);
}
});
@@ -63,10 +63,10 @@ describe('ReactFresh', () => {
return Component;
}
- function render(version, props) {
+ async function render(version, props) {
const Component = version();
- act(() => {
- ReactDOM.render(, container);
+ await act(() => {
+ root.render();
});
return Component;
}
@@ -99,9 +99,9 @@ describe('ReactFresh', () => {
);
}
- it('can preserve state for compatible types', () => {
+ it('can preserve state for compatible types', async () => {
if (__DEV__) {
- const HelloV1 = render(() => {
+ const HelloV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -118,7 +118,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -143,7 +143,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -153,15 +153,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => HelloV1);
- render(() => HelloV2);
- render(() => HelloV1);
+ await render(() => HelloV1);
+ await render(() => HelloV2);
+ await render(() => HelloV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -169,7 +169,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -189,9 +189,9 @@ describe('ReactFresh', () => {
}
});
- it('can preserve state for forwardRef', () => {
+ it('can preserve state for forwardRef', async () => {
if (__DEV__) {
- const OuterV1 = render(() => {
+ const OuterV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -211,7 +211,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -239,7 +239,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -249,15 +249,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => OuterV1);
- render(() => OuterV2);
- render(() => OuterV1);
+ await render(() => OuterV1);
+ await render(() => OuterV2);
+ await render(() => OuterV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -279,9 +279,9 @@ describe('ReactFresh', () => {
}
});
- it('should not consider two forwardRefs around the same type to be equivalent', () => {
+ it('should not consider two forwardRefs around the same type to be equivalent', async () => {
if (__DEV__) {
- const ParentV1 = render(
+ const ParentV1 = await render(
() => {
function Hello() {
const [val, setVal] = React.useState(0);
@@ -317,32 +317,32 @@ describe('ReactFresh', () => {
let el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
// Switching up the inner types should reset the state.
- render(() => ParentV1, {cond: false});
+ await render(() => ParentV1, {cond: false});
expect(el).not.toBe(container.firstChild);
el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
// Switch them up back again.
- render(() => ParentV1, {cond: true});
+ await render(() => ParentV1, {cond: true});
expect(el).not.toBe(container.firstChild);
el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
// Now bump up the state to prepare for patching.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -383,14 +383,14 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Switching up the condition should still reset the state.
- render(() => ParentV2, {cond: false});
+ await render(() => ParentV2, {cond: false});
expect(el).not.toBe(container.firstChild);
el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('red');
// Now bump up the state to prepare for top-level renders.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el).toBe(container.firstChild);
@@ -398,18 +398,18 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Finally, verify using top-level render with stale type keeps state.
- render(() => ParentV1);
- render(() => ParentV2);
- render(() => ParentV1);
+ await render(() => ParentV1);
+ await render(() => ParentV2);
+ await render(() => ParentV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('1');
expect(el.style.color).toBe('red');
}
});
- it('can update forwardRef render function with its wrapper', () => {
+ it('can update forwardRef render function with its wrapper', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello({color}) {
const [val, setVal] = React.useState(0);
return (
@@ -429,7 +429,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -458,9 +458,9 @@ describe('ReactFresh', () => {
}
});
- it('can update forwardRef render function in isolation', () => {
+ it('can update forwardRef render function in isolation', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello({color}) {
const [val, setVal] = React.useState(0);
return (
@@ -483,7 +483,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -515,9 +515,9 @@ describe('ReactFresh', () => {
}
});
- it('can preserve state for simple memo', () => {
+ it('can preserve state for simple memo', async () => {
if (__DEV__) {
- const OuterV1 = render(() => {
+ const OuterV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -537,7 +537,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -565,7 +565,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -575,15 +575,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => OuterV1);
- render(() => OuterV2);
- render(() => OuterV1);
+ await render(() => OuterV1);
+ await render(() => OuterV2);
+ await render(() => OuterV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -605,9 +605,9 @@ describe('ReactFresh', () => {
}
});
- it('can preserve state for memo with custom comparison', () => {
+ it('can preserve state for memo with custom comparison', async () => {
if (__DEV__) {
- const OuterV1 = render(() => {
+ const OuterV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -626,7 +626,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -653,7 +653,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -663,15 +663,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => OuterV1);
- render(() => OuterV2);
- render(() => OuterV1);
+ await render(() => OuterV1);
+ await render(() => OuterV2);
+ await render(() => OuterV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -693,9 +693,9 @@ describe('ReactFresh', () => {
}
});
- it('can update simple memo function in isolation', () => {
+ it('can update simple memo function in isolation', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -713,7 +713,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -740,9 +740,9 @@ describe('ReactFresh', () => {
}
});
- it('can preserve state for memo(forwardRef)', () => {
+ it('can preserve state for memo(forwardRef)', async () => {
if (__DEV__) {
- const OuterV1 = render(() => {
+ const OuterV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -762,7 +762,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -790,7 +790,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -800,15 +800,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => OuterV1);
- render(() => OuterV2);
- render(() => OuterV1);
+ await render(() => OuterV1);
+ await render(() => OuterV2);
+ await render(() => OuterV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -832,7 +832,8 @@ describe('ReactFresh', () => {
it('can preserve state for lazy after resolution', async () => {
if (__DEV__) {
- const AppV1 = render(() => {
+ let resolve;
+ const AppV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -845,8 +846,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -865,7 +866,7 @@ describe('ReactFresh', () => {
expect(container.textContent).toBe('Loading');
await act(() => {
- jest.runAllTimers();
+ resolve();
});
expect(container.textContent).toBe('0');
@@ -873,7 +874,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -892,8 +893,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -916,7 +917,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump the state again.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -926,15 +927,15 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => AppV1);
- render(() => AppV2);
- render(() => AppV1);
+ await render(() => AppV1);
+ await render(() => AppV2);
+ await render(() => AppV1);
expect(container.firstChild).toBe(el);
expect(el.textContent).toBe('2');
expect(el.style.color).toBe('red');
// Finally, a render with incompatible type should reset it.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -968,7 +969,8 @@ describe('ReactFresh', () => {
it('can patch lazy before resolution', async () => {
if (__DEV__) {
- render(() => {
+ let resolve;
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -981,8 +983,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -1014,7 +1016,7 @@ describe('ReactFresh', () => {
});
await act(() => {
- jest.runAllTimers();
+ resolve();
});
// Expect different color on initial mount.
@@ -1023,7 +1025,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump state.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -1050,7 +1052,8 @@ describe('ReactFresh', () => {
it('can patch lazy(forwardRef) before resolution', async () => {
if (__DEV__) {
- render(() => {
+ let resolve;
+ await render(() => {
function renderHello() {
const [val, setVal] = React.useState(0);
return (
@@ -1064,8 +1067,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -1098,7 +1101,7 @@ describe('ReactFresh', () => {
});
await act(() => {
- jest.runAllTimers();
+ resolve();
});
// Expect different color on initial mount.
@@ -1107,7 +1110,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump state.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -1135,7 +1138,8 @@ describe('ReactFresh', () => {
it('can patch lazy(memo) before resolution', async () => {
if (__DEV__) {
- render(() => {
+ let resolve;
+ await render(() => {
function renderHello() {
const [val, setVal] = React.useState(0);
return (
@@ -1149,8 +1153,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -1183,7 +1187,7 @@ describe('ReactFresh', () => {
});
await act(() => {
- jest.runAllTimers();
+ resolve();
});
// Expect different color on initial mount.
@@ -1192,7 +1196,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump state.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -1220,7 +1224,8 @@ describe('ReactFresh', () => {
it('can patch lazy(memo(forwardRef)) before resolution', async () => {
if (__DEV__) {
- render(() => {
+ let resolve;
+ await render(() => {
function renderHello() {
const [val, setVal] = React.useState(0);
return (
@@ -1234,8 +1239,8 @@ describe('ReactFresh', () => {
const Outer = React.lazy(
() =>
- new Promise(resolve => {
- setTimeout(() => resolve({default: Hello}), 100);
+ new Promise(_resolve => {
+ resolve = () => _resolve({default: Hello});
}),
);
$RefreshReg$(Outer, 'Outer');
@@ -1268,7 +1273,7 @@ describe('ReactFresh', () => {
});
await act(() => {
- jest.runAllTimers();
+ resolve();
});
// Expect different color on initial mount.
@@ -1277,7 +1282,7 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('red');
// Bump state.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.firstChild).toBe(el);
@@ -1303,9 +1308,9 @@ describe('ReactFresh', () => {
}
});
- it('can patch both trees while suspense is displaying the fallback', async () => {
+ it('only patches the fallback tree while suspended', async () => {
if (__DEV__) {
- const AppV1 = render(
+ const AppV1 = await render(
() => {
function Hello({children}) {
const [val, setVal] = React.useState(0);
@@ -1343,7 +1348,7 @@ describe('ReactFresh', () => {
expect(primaryChild.style.display).toBe('');
// Bump primary content state.
- act(() => {
+ await act(() => {
primaryChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.childNodes.length).toBe(1);
@@ -1371,7 +1376,7 @@ describe('ReactFresh', () => {
expect(primaryChild.style.display).toBe('');
// Now force the tree to suspend.
- render(() => AppV1, {shouldSuspend: true});
+ await render(() => AppV1, {shouldSuspend: true});
// Expect to see two trees, one of them is hidden.
expect(container.childNodes.length).toBe(2);
@@ -1385,7 +1390,7 @@ describe('ReactFresh', () => {
expect(fallbackChild.style.display).toBe('');
// Bump fallback state.
- act(() => {
+ await act(() => {
fallbackChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(container.childNodes.length).toBe(2);
@@ -1411,19 +1416,19 @@ describe('ReactFresh', () => {
$RefreshReg$(Hello, 'Hello');
});
- // Colors inside both trees should change:
+ // Only update color in the visible child
expect(container.childNodes.length).toBe(2);
expect(container.childNodes[0]).toBe(primaryChild);
expect(container.childNodes[1]).toBe(fallbackChild);
expect(primaryChild.textContent).toBe('Content 1');
- expect(primaryChild.style.color).toBe('red');
+ expect(primaryChild.style.color).toBe('green');
expect(primaryChild.style.display).toBe('none');
expect(fallbackChild.textContent).toBe('Fallback 1');
expect(fallbackChild.style.color).toBe('red');
expect(fallbackChild.style.display).toBe('');
// Only primary tree should exist now:
- render(() => AppV1, {shouldSuspend: false});
+ await render(() => AppV1, {shouldSuspend: false});
expect(container.childNodes.length).toBe(1);
expect(container.childNodes[0]).toBe(primaryChild);
expect(primaryChild.textContent).toBe('Content 1');
@@ -1450,11 +1455,11 @@ describe('ReactFresh', () => {
}
});
- it('does not re-render ancestor components unnecessarily during a hot update', () => {
+ it('does not re-render ancestor components unnecessarily during a hot update', async () => {
if (__DEV__) {
let appRenders = 0;
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -1478,7 +1483,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -1508,7 +1513,7 @@ describe('ReactFresh', () => {
expect(appRenders).toBe(1);
// Bump the state.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('2');
@@ -1518,11 +1523,11 @@ describe('ReactFresh', () => {
}
});
- it('batches re-renders during a hot update', () => {
+ it('batches re-renders during a hot update', async () => {
if (__DEV__) {
let helloRenders = 0;
- render(() => {
+ await render(() => {
function Hello({children}) {
helloRenders++;
return
X{children}X
;
@@ -1559,9 +1564,9 @@ describe('ReactFresh', () => {
}
});
- it('does not leak state between components', () => {
+ it('does not leak state between components', async () => {
if (__DEV__) {
- const AppV1 = render(
+ const AppV1 = await render(
() => {
function Hello1() {
const [val, setVal] = React.useState(0);
@@ -1594,21 +1599,21 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
// Switch the condition, flipping inner content.
// This should reset the state.
- render(() => AppV1, {cond: true});
+ await render(() => AppV1, {cond: true});
const el2 = container.firstChild;
expect(el2).not.toBe(el);
expect(el2.textContent).toBe('0');
expect(el2.style.color).toBe('blue');
// Bump it again.
- act(() => {
+ await act(() => {
el2.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el2.textContent).toBe('1');
@@ -1641,7 +1646,7 @@ describe('ReactFresh', () => {
expect(el2.style.color).toBe('red');
// Flip the condition again.
- render(() => AppV1, {cond: false});
+ await render(() => AppV1, {cond: false});
const el3 = container.firstChild;
expect(el3).not.toBe(el2);
expect(el3.textContent).toBe('0');
@@ -1649,9 +1654,9 @@ describe('ReactFresh', () => {
}
});
- it('can force remount by changing signature', () => {
+ it('can force remount by changing signature', async () => {
if (__DEV__) {
- const HelloV1 = render(() => {
+ const HelloV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -1670,7 +1675,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -1719,7 +1724,7 @@ describe('ReactFresh', () => {
expect(newEl.style.color).toBe('yellow');
// Bump state again.
- act(() => {
+ await act(() => {
newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(newEl.textContent).toBe('1');
@@ -1728,11 +1733,11 @@ describe('ReactFresh', () => {
// Perform top-down renders with both fresh and stale types.
// Neither should change the state or color.
// They should always resolve to the latest version.
- render(() => HelloV1);
- render(() => HelloV2);
- render(() => HelloV3);
- render(() => HelloV2);
- render(() => HelloV1);
+ await render(() => HelloV1);
+ await render(() => HelloV2);
+ await render(() => HelloV3);
+ await render(() => HelloV2);
+ await render(() => HelloV1);
expect(container.firstChild).toBe(newEl);
expect(newEl.textContent).toBe('1');
expect(newEl.style.color).toBe('yellow');
@@ -1780,7 +1785,7 @@ describe('ReactFresh', () => {
}
});
- it('keeps a valid tree when forcing remount', () => {
+ it('keeps a valid tree when forcing remount', async () => {
if (__DEV__) {
const HelloV1 = prepare(() => {
function Hello() {
@@ -1910,26 +1915,31 @@ describe('ReactFresh', () => {
,
];
- // First, check that each tree handles remounts in isolation.
- ReactDOM.render(null, container);
+ await act(() => {
+ root.render(null);
+ });
+
for (let i = 0; i < trees.length; i++) {
- runRemountingStressTest(trees[i]);
+ await runRemountingStressTest(trees[i]);
}
// Then check that each tree is resilient to updates from another tree.
for (let i = 0; i < trees.length; i++) {
for (let j = 0; j < trees.length; j++) {
- ReactDOM.render(null, container);
+ await act(() => {
+ root.render(null);
+ });
+
// Intentionally don't clean up between the tests:
- runRemountingStressTest(trees[i]);
- runRemountingStressTest(trees[j]);
- runRemountingStressTest(trees[i]);
+ await runRemountingStressTest(trees[i]);
+ await runRemountingStressTest(trees[j]);
+ await runRemountingStressTest(trees[i]);
}
}
}
- });
+ }, 10000);
- function runRemountingStressTest(tree) {
+ async function runRemountingStressTest(tree) {
patch(() => {
function Hello({children}) {
return ;
@@ -1939,7 +1949,10 @@ describe('ReactFresh', () => {
return Hello;
});
- ReactDOM.render(tree, container);
+ await act(() => {
+ root.render(tree);
+ });
+
const elements = container.querySelectorAll('section');
// Each tree above produces exactly three elements:
expect(elements.length).toBe(3);
@@ -2003,8 +2016,10 @@ describe('ReactFresh', () => {
expect(el.dataset.color).toBe('black');
});
- // Do another render just in case.
- ReactDOM.render(tree, container);
+ await act(() => {
+ root.render(tree);
+ });
+
expect(container.querySelectorAll('section').length).toBe(3);
container.querySelectorAll('section').forEach((el, index) => {
expect(el).toBe(elementsAfterRemount[index]);
@@ -2012,21 +2027,21 @@ describe('ReactFresh', () => {
});
}
- it('can remount on signature change within a wrapper', () => {
+ it('can remount on signature change within a wrapper', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => Hello);
+ await testRemountingWithWrapper(Hello => Hello);
}
});
- it('can remount on signature change within a simple memo wrapper', () => {
+ it('can remount on signature change within a simple memo wrapper', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => React.memo(Hello));
+ await testRemountingWithWrapper(Hello => React.memo(Hello));
}
});
- it('can remount on signature change within a lazy simple memo wrapper', () => {
+ it('can remount on signature change within a lazy simple memo wrapper', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello =>
+ await testRemountingWithWrapper(Hello =>
React.lazy(() => ({
then(cb) {
cb({default: React.memo(Hello)});
@@ -2036,35 +2051,37 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within forwardRef', () => {
+ it('can remount on signature change within forwardRef', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => React.forwardRef(Hello));
+ await testRemountingWithWrapper(Hello => React.forwardRef(Hello));
}
});
- it('can remount on signature change within forwardRef render function', () => {
+ it('can remount on signature change within forwardRef render function', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => React.forwardRef(() => ));
+ await testRemountingWithWrapper(Hello =>
+ React.forwardRef(() => ),
+ );
}
});
- it('can remount on signature change within nested memo', () => {
+ it('can remount on signature change within nested memo', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello =>
+ await testRemountingWithWrapper(Hello =>
React.memo(React.memo(React.memo(Hello))),
);
}
});
- it('can remount on signature change within a memo wrapper and custom comparison', () => {
+ it('can remount on signature change within a memo wrapper and custom comparison', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => React.memo(Hello, () => true));
+ await testRemountingWithWrapper(Hello => React.memo(Hello, () => true));
}
});
- it('can remount on signature change within a class', () => {
+ it('can remount on signature change within a class', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const child = ;
return class Wrapper extends React.PureComponent {
render() {
@@ -2075,9 +2092,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a context provider', () => {
+ it('can remount on signature change within a context provider', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const Context = React.createContext();
const child = (
@@ -2091,9 +2108,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a context consumer', () => {
+ it('can remount on signature change within a context consumer', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const Context = React.createContext();
const child = {() => };
return function Wrapper() {
@@ -2103,9 +2120,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a suspense node', () => {
+ it('can remount on signature change within a suspense node', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
// TODO: we'll probably want to test fallback trees too.
const child = (
@@ -2119,9 +2136,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a mode node', () => {
+ it('can remount on signature change within a mode node', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const child = (
@@ -2134,9 +2151,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a fragment node', () => {
+ it('can remount on signature change within a fragment node', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const child = (
<>
@@ -2149,9 +2166,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within multiple siblings', () => {
+ it('can remount on signature change within multiple siblings', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const child = (
<>
<>
@@ -2168,9 +2185,9 @@ describe('ReactFresh', () => {
}
});
- it('can remount on signature change within a profiler node', () => {
+ it('can remount on signature change within a profiler node', async () => {
if (__DEV__) {
- testRemountingWithWrapper(Hello => {
+ await testRemountingWithWrapper(Hello => {
const child = ;
return function Wrapper() {
return (
@@ -2183,8 +2200,8 @@ describe('ReactFresh', () => {
}
});
- function testRemountingWithWrapper(wrap) {
- render(() => {
+ async function testRemountingWithWrapper(wrap) {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -2206,7 +2223,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -2255,7 +2272,7 @@ describe('ReactFresh', () => {
expect(newEl.style.color).toBe('yellow');
// Bump state again.
- act(() => {
+ await act(() => {
newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(newEl.textContent).toBe('1');
@@ -2303,11 +2320,11 @@ describe('ReactFresh', () => {
expect(finalEl.style.color).toBe('orange');
}
- it('resets hooks with dependencies on hot reload', () => {
+ it('resets hooks with dependencies on hot reload', async () => {
if (__DEV__) {
let useEffectWithEmptyArrayCalls = 0;
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
const tranformed = React.useMemo(() => val * 2, [val]);
@@ -2332,33 +2349,31 @@ describe('ReactFresh', () => {
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
expect(useEffectWithEmptyArrayCalls).toBe(1); // useEffect ran
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('2'); // val * 2
expect(useEffectWithEmptyArrayCalls).toBe(1); // useEffect didn't re-run
// Perform a hot update.
- act(() => {
- patch(() => {
- function Hello() {
- const [val, setVal] = React.useState(0);
- const tranformed = React.useMemo(() => val * 10, [val]);
- const handleClick = React.useCallback(() => setVal(v => v - 1), []);
+ patch(() => {
+ function Hello() {
+ const [val, setVal] = React.useState(0);
+ const tranformed = React.useMemo(() => val * 10, [val]);
+ const handleClick = React.useCallback(() => setVal(v => v - 1), []);
- React.useEffect(() => {
- useEffectWithEmptyArrayCalls++;
- }, []);
+ React.useEffect(() => {
+ useEffectWithEmptyArrayCalls++;
+ }, []);
- return (
-
- {tranformed}
-
- );
- }
- $RefreshReg$(Hello, 'Hello');
- return Hello;
- });
+ return (
+
+ {tranformed}
+
+ );
+ }
+ $RefreshReg$(Hello, 'Hello');
+ return Hello;
});
// Assert the state was preserved but memo was evicted.
@@ -2368,7 +2383,7 @@ describe('ReactFresh', () => {
expect(useEffectWithEmptyArrayCalls).toBe(2); // useEffect re-ran
// This should fire the new callback which decreases the counter.
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('0');
@@ -2378,9 +2393,9 @@ describe('ReactFresh', () => {
});
// This pattern is inspired by useSubscription and similar mechanisms.
- it('does not get into infinite loops during render phase updates', () => {
+ it('does not get into infinite loops during render phase updates', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
const source = React.useMemo(() => ({value: 10}), []);
const [state, setState] = React.useState({value: null});
@@ -2398,20 +2413,18 @@ describe('ReactFresh', () => {
expect(el.style.color).toBe('blue');
// Perform a hot update.
- act(() => {
- patch(() => {
- function Hello() {
- const source = React.useMemo(() => ({value: 20}), []);
- const [state, setState] = React.useState({value: null});
- if (state !== source) {
- // This should perform a single render-phase update.
- setState(source);
- }
- return {state.value}
;
+ patch(() => {
+ function Hello() {
+ const source = React.useMemo(() => ({value: 20}), []);
+ const [state, setState] = React.useState({value: null});
+ if (state !== source) {
+ // This should perform a single render-phase update.
+ setState(source);
}
- $RefreshReg$(Hello, 'Hello');
- return Hello;
- });
+ return {state.value}
;
+ }
+ $RefreshReg$(Hello, 'Hello');
+ return Hello;
});
expect(container.firstChild).toBe(el);
@@ -2448,7 +2461,6 @@ describe('ReactFresh', () => {
};
});
- const root = ReactDOMClient.createRoot(container);
root.render();
await waitFor(['App#layout']);
const el = container.firstChild;
@@ -2482,7 +2494,7 @@ describe('ReactFresh', () => {
expect(el.firstChild.textContent).toBe('0');
expect(el.firstChild.style.color).toBe('red');
- await internalAct(() => {
+ await act(() => {
el.firstChild.dispatchEvent(
new MouseEvent('click', {
bubbles: true,
@@ -2522,9 +2534,9 @@ describe('ReactFresh', () => {
expect(el.firstChild.style.color).toBe('orange');
});
- it('remounts failed error boundaries (componentDidCatch)', () => {
+ it('remounts failed error boundaries (componentDidCatch)', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
return Hi
;
}
@@ -2600,9 +2612,9 @@ describe('ReactFresh', () => {
}
});
- it('remounts failed error boundaries (getDerivedStateFromError)', () => {
+ it('remounts failed error boundaries (getDerivedStateFromError)', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
return Hi
;
}
@@ -2678,9 +2690,9 @@ describe('ReactFresh', () => {
}
});
- it('remounts error boundaries that failed asynchronously after hot update', () => {
+ it('remounts error boundaries that failed asynchronously after hot update', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
const [x] = React.useState('');
React.useEffect(() => {}, []);
@@ -2722,26 +2734,25 @@ describe('ReactFresh', () => {
const secondP = firstP.nextSibling.nextSibling;
// Perform a hot update that fails.
- act(() => {
- patch(() => {
- function Hello() {
- const [x, setX] = React.useState('');
- React.useEffect(() => {
- setTimeout(() => {
- setX(42); // This will crash next render.
- }, 1);
- }, []);
- x.slice();
- return Hi
;
- }
- $RefreshReg$(Hello, 'Hello');
- });
+ let crash;
+ patch(() => {
+ function Hello() {
+ const [x, setX] = React.useState('');
+ React.useEffect(() => {
+ crash = () => {
+ setX(42); // This will crash next render.
+ };
+ }, []);
+ x.slice();
+ return Hi
;
+ }
+ $RefreshReg$(Hello, 'Hello');
});
expect(container.innerHTML).toBe('A
Hi
B
');
// Run timeout inside effect:
- act(() => {
- jest.runAllTimers();
+ await act(() => {
+ crash();
});
expect(container.innerHTML).toBe(
'A
Oops: x.slice is not a function
B
',
@@ -2750,16 +2761,14 @@ describe('ReactFresh', () => {
expect(container.firstChild.nextSibling.nextSibling).toBe(secondP);
// Perform a hot update that fixes the error.
- act(() => {
- patch(() => {
- function Hello() {
- const [x] = React.useState('');
- React.useEffect(() => {}, []); // Removes the bad effect code.
- x.slice(); // Doesn't throw initially.
- return Fixed!
;
- }
- $RefreshReg$(Hello, 'Hello');
- });
+ patch(() => {
+ function Hello() {
+ const [x] = React.useState('');
+ React.useEffect(() => {}, []); // Removes the bad effect code.
+ x.slice(); // Doesn't throw initially.
+ return Fixed!
;
+ }
+ $RefreshReg$(Hello, 'Hello');
});
// This should remount the error boundary (but not anything above it).
@@ -2769,16 +2778,14 @@ describe('ReactFresh', () => {
// Verify next hot reload doesn't remount anything.
const helloNode = container.firstChild.nextSibling;
- act(() => {
- patch(() => {
- function Hello() {
- const [x] = React.useState('');
- React.useEffect(() => {}, []);
- x.slice();
- return Nice.
;
- }
- $RefreshReg$(Hello, 'Hello');
- });
+ patch(() => {
+ function Hello() {
+ const [x] = React.useState('');
+ React.useEffect(() => {}, []);
+ x.slice();
+ return Nice.
;
+ }
+ $RefreshReg$(Hello, 'Hello');
});
expect(container.firstChild.nextSibling).toBe(helloNode);
@@ -2786,9 +2793,9 @@ describe('ReactFresh', () => {
}
});
- it('remounts a failed root on mount', () => {
+ it('remounts a failed root on mount', async () => {
if (__DEV__) {
- expect(() => {
+ await expect(
render(() => {
function Hello() {
throw new Error('No');
@@ -2796,8 +2803,8 @@ describe('ReactFresh', () => {
$RefreshReg$(Hello, 'Hello');
return Hello;
- });
- }).toThrow('No');
+ }),
+ ).rejects.toThrow('No');
expect(container.innerHTML).toBe('');
// A bad retry
@@ -2849,7 +2856,9 @@ describe('ReactFresh', () => {
expect(container.innerHTML).toBe('Fixed 2!
');
// Updates after intentional unmount are ignored.
- ReactDOM.unmountComponentAtNode(container);
+ await act(() => {
+ root.unmount();
+ });
patch(() => {
function Hello() {
throw new Error('Ignored');
@@ -2867,9 +2876,9 @@ describe('ReactFresh', () => {
}
});
- it('does not retry an intentionally unmounted failed root', () => {
+ it('does not retry an intentionally unmounted failed root', async () => {
if (__DEV__) {
- expect(() => {
+ await expect(
render(() => {
function Hello() {
throw new Error('No');
@@ -2877,12 +2886,14 @@ describe('ReactFresh', () => {
$RefreshReg$(Hello, 'Hello');
return Hello;
- });
- }).toThrow('No');
+ }),
+ ).rejects.toThrow('No');
expect(container.innerHTML).toBe('');
// Intentional unmount.
- ReactDOM.unmountComponentAtNode(container);
+ await act(() => {
+ root.unmount();
+ });
// Perform a hot update that fixes the error.
patch(() => {
@@ -2896,9 +2907,9 @@ describe('ReactFresh', () => {
}
});
- it('remounts a failed root on update', () => {
+ it('remounts a failed root on update', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Hello() {
return Hi
;
}
@@ -2974,7 +2985,9 @@ describe('ReactFresh', () => {
expect(container.innerHTML).toBe('At last.
');
// Check we don't attempt to reverse an intentional unmount.
- ReactDOM.unmountComponentAtNode(container);
+ await act(() => {
+ root.unmount();
+ });
expect(container.innerHTML).toBe('');
patch(() => {
function Hello() {
@@ -2985,7 +2998,8 @@ describe('ReactFresh', () => {
expect(container.innerHTML).toBe('');
// Mount a new container.
- render(() => {
+ root = ReactDOMClient.createRoot(container);
+ await render(() => {
function Hello() {
return Hi
;
}
@@ -3007,7 +3021,9 @@ describe('ReactFresh', () => {
expect(container.innerHTML).toBe('');
// Check we don't attempt to reverse an intentional unmount, even after an error.
- ReactDOM.unmountComponentAtNode(container);
+ await act(() => {
+ root.unmount();
+ });
expect(container.innerHTML).toBe('');
patch(() => {
function Hello() {
@@ -3019,10 +3035,12 @@ describe('ReactFresh', () => {
}
});
- it('regression test: does not get into an infinite loop', () => {
+ it('regression test: does not get into an infinite loop', async () => {
if (__DEV__) {
const containerA = document.createElement('div');
const containerB = document.createElement('div');
+ const rootA = ReactDOMClient.createRoot(containerA);
+ const rootB = ReactDOMClient.createRoot(containerB);
// Initially, nothing interesting.
const RootAV1 = () => {
@@ -3034,9 +3052,9 @@ describe('ReactFresh', () => {
};
$RefreshReg$(RootBV1, 'RootB');
- act(() => {
- ReactDOM.render(, containerA);
- ReactDOM.render(, containerB);
+ await act(() => {
+ rootA.render();
+ rootB.render();
});
expect(containerA.innerHTML).toBe('A1');
expect(containerB.innerHTML).toBe('B1');
@@ -3046,7 +3064,11 @@ describe('ReactFresh', () => {
throw new Error('A2!');
};
$RefreshReg$(RootAV2, 'RootA');
- expect(() => ReactFreshRuntime.performReactRefresh()).toThrow('A2!');
+ await expect(
+ act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ }),
+ ).rejects.toThrow('A2!');
expect(containerA.innerHTML).toBe('');
expect(containerB.innerHTML).toBe('B1');
@@ -3060,7 +3082,11 @@ describe('ReactFresh', () => {
return 'A3';
};
$RefreshReg$(RootAV3, 'RootA');
- expect(() => ReactFreshRuntime.performReactRefresh()).toThrow('A3!');
+ await expect(
+ act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ }),
+ ).rejects.toThrow('A3!');
expect(containerA.innerHTML).toBe('');
expect(containerB.innerHTML).toBe('B1');
@@ -3068,15 +3094,17 @@ describe('ReactFresh', () => {
return 'A4';
};
$RefreshReg$(RootAV4, 'RootA');
- ReactFreshRuntime.performReactRefresh();
+ await act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ });
expect(containerA.innerHTML).toBe('A4');
expect(containerB.innerHTML).toBe('B1');
}
});
- it('remounts classes on every edit', () => {
+ it('remounts classes on every edit', async () => {
if (__DEV__) {
- const HelloV1 = render(() => {
+ const HelloV1 = await render(() => {
class Hello extends React.Component {
state = {count: 0};
handleClick = () => {
@@ -3105,7 +3133,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -3136,14 +3164,14 @@ describe('ReactFresh', () => {
const newEl = container.firstChild;
expect(newEl.textContent).toBe('0');
expect(newEl.style.color).toBe('red');
- act(() => {
+ await act(() => {
newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(newEl.textContent).toBe('1');
// Now top-level renders of both types resolve to latest.
- render(() => HelloV1);
- render(() => HelloV2);
+ await render(() => HelloV1);
+ await render(() => HelloV2);
expect(container.firstChild).toBe(newEl);
expect(newEl.style.color).toBe('red');
expect(newEl.textContent).toBe('1');
@@ -3173,24 +3201,24 @@ describe('ReactFresh', () => {
const finalEl = container.firstChild;
expect(finalEl.textContent).toBe('0');
expect(finalEl.style.color).toBe('orange');
- act(() => {
+ await act(() => {
finalEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(finalEl.textContent).toBe('1');
- render(() => HelloV3);
- render(() => HelloV2);
- render(() => HelloV1);
+ await render(() => HelloV3);
+ await render(() => HelloV2);
+ await render(() => HelloV1);
expect(container.firstChild).toBe(finalEl);
expect(finalEl.style.color).toBe('orange');
expect(finalEl.textContent).toBe('1');
}
});
- it('updates refs when remounting', () => {
+ it('updates refs when remounting', async () => {
if (__DEV__) {
const testRef = React.createRef();
- render(
+ await render(
() => {
class Hello extends React.Component {
getColor() {
@@ -3261,9 +3289,9 @@ describe('ReactFresh', () => {
}
});
- it('remounts on conversion from class to function and back', () => {
+ it('remounts on conversion from class to function and back', async () => {
if (__DEV__) {
- const HelloV1 = render(() => {
+ const HelloV1 = await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -3280,7 +3308,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -3311,14 +3339,14 @@ describe('ReactFresh', () => {
const newEl = container.firstChild;
expect(newEl.textContent).toBe('0');
expect(newEl.style.color).toBe('red');
- act(() => {
+ await act(() => {
newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(newEl.textContent).toBe('1');
// Now top-level renders of both types resolve to latest.
- render(() => HelloV1);
- render(() => HelloV2);
+ await render(() => HelloV1);
+ await render(() => HelloV2);
expect(container.firstChild).toBe(newEl);
expect(newEl.style.color).toBe('red');
expect(newEl.textContent).toBe('1');
@@ -3342,14 +3370,14 @@ describe('ReactFresh', () => {
const finalEl = container.firstChild;
expect(finalEl.textContent).toBe('0');
expect(finalEl.style.color).toBe('orange');
- act(() => {
+ await act(() => {
finalEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(finalEl.textContent).toBe('1');
- render(() => HelloV3);
- render(() => HelloV2);
- render(() => HelloV1);
+ await render(() => HelloV3);
+ await render(() => HelloV2);
+ await render(() => HelloV1);
expect(container.firstChild).toBe(finalEl);
expect(finalEl.style.color).toBe('orange');
expect(finalEl.textContent).toBe('1');
@@ -3373,9 +3401,9 @@ describe('ReactFresh', () => {
}
});
- it('can find host instances for a family', () => {
+ it('can find host instances for a family', async () => {
if (__DEV__) {
- render(() => {
+ await render(() => {
function Child({children}) {
return {children}
;
}
@@ -3481,7 +3509,7 @@ describe('ReactFresh', () => {
});
}
- it('can update multiple roots independently', () => {
+ it('can update multiple roots independently', async () => {
if (__DEV__) {
// Declare the first version.
const HelloV1 = () => {
@@ -3504,7 +3532,9 @@ describe('ReactFresh', () => {
);
};
$RefreshReg$(HelloV2, 'Hello');
- ReactFreshRuntime.performReactRefresh();
+ await act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ });
// Mount three roots.
const cont1 = document.createElement('div');
@@ -3513,10 +3543,19 @@ describe('ReactFresh', () => {
document.body.appendChild(cont1);
document.body.appendChild(cont2);
document.body.appendChild(cont3);
+ const root1 = ReactDOMClient.createRoot(cont1);
+ const root2 = ReactDOMClient.createRoot(cont2);
+ const root3 = ReactDOMClient.createRoot(cont3);
try {
- ReactDOM.render(, cont1);
- ReactDOM.render(, cont2);
- ReactDOM.render(, cont3);
+ await act(() => {
+ root1.render();
+ });
+ await act(() => {
+ root2.render();
+ });
+ await act(() => {
+ root3.render();
+ });
// Expect we see the V2 color.
expect(cont1.firstChild.style.color).toBe('red');
@@ -3527,7 +3566,7 @@ describe('ReactFresh', () => {
expect(cont3.firstChild.textContent).toBe('0');
// Bump the state for each of them.
- act(() => {
+ await act(() => {
cont1.firstChild.dispatchEvent(
new MouseEvent('click', {bubbles: true}),
);
@@ -3555,7 +3594,9 @@ describe('ReactFresh', () => {
);
};
$RefreshReg$(HelloV3, 'Hello');
- ReactFreshRuntime.performReactRefresh();
+ await act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ });
// It should affect all roots.
expect(cont1.firstChild.style.color).toBe('green');
@@ -3566,7 +3607,9 @@ describe('ReactFresh', () => {
expect(cont3.firstChild.textContent).toBe('1');
// Unmount the second root.
- ReactDOM.unmountComponentAtNode(cont2);
+ await act(() => {
+ root2.unmount();
+ });
// Make the first root throw and unmount on hot update.
const HelloV4 = ({id}) => {
if (id === 1) {
@@ -3580,9 +3623,11 @@ describe('ReactFresh', () => {
);
};
$RefreshReg$(HelloV4, 'Hello');
- expect(() => {
- ReactFreshRuntime.performReactRefresh();
- }).toThrow('Oops.');
+ await expect(
+ act(() => {
+ ReactFreshRuntime.performReactRefresh();
+ }),
+ ).rejects.toThrow('Oops.');
// Still, we expect the last root to be updated.
expect(cont1.innerHTML).toBe('');
@@ -3791,18 +3836,20 @@ describe('ReactFresh', () => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
+ ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
- act = React.act;
- internalAct = require('internal-test-utils').act;
+ act = require('internal-test-utils').act;
// Important! Inject into the global hook *after* ReactDOM runs:
ReactFreshRuntime = require('react-refresh/runtime');
ReactFreshRuntime.injectIntoGlobalHook(global);
+ root = ReactDOMClient.createRoot(container);
+
// We're verifying that we're able to track roots mounted after this point.
// The rest of this test is taken from the simplest first test case.
- render(() => {
+ await render(() => {
function Hello() {
const [val, setVal] = React.useState(0);
return (
@@ -3819,7 +3866,7 @@ describe('ReactFresh', () => {
const el = container.firstChild;
expect(el.textContent).toBe('0');
expect(el.style.color).toBe('blue');
- act(() => {
+ await act(() => {
el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(el.textContent).toBe('1');
@@ -3846,7 +3893,7 @@ describe('ReactFresh', () => {
});
// This simulates the scenario in https://github.com/facebook/react/issues/20100
- it('does not block DevTools when an unsupported renderer is injected', () => {
+ it('does not block DevTools when an unsupported legacy renderer is injected', () => {
if (__DEV__) {
initFauxDevToolsHook();
@@ -3872,13 +3919,12 @@ describe('ReactFresh', () => {
ReactFreshRuntime = require('react-refresh/runtime');
ReactFreshRuntime.injectIntoGlobalHook(global);
- render(() => {
- function Hello() {
- return Hi!
;
- }
- $RefreshReg$(Hello, 'Hello');
- return Hello;
- });
+ const Hello = () => {
+ return Hi!
;
+ };
+ $RefreshReg$(Hello, 'Hello');
+ const Component = Hello;
+ ReactDOM.render(, container);
expect(onCommitFiberRoot).toHaveBeenCalled();
}