mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Warn when rendering tests in concurrent/batched mode without a mocked scheduler (#16207)
Concurrent/Batched mode tests should always be run with a mocked scheduler (v17 or not). This PR adds a warning for the same. I'll put up a separate PR to the docs with a page detailing how to mock the scheduler.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let TestUtils;
|
||||
let TestRenderer;
|
||||
|
||||
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
||||
|
||||
expect.extend(require('../toWarnDev'));
|
||||
|
||||
describe('unmocked scheduler', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
});
|
||||
|
||||
it('flushes work only outside the outermost act() corresponding to its own renderer', () => {
|
||||
let log = [];
|
||||
function Effecty() {
|
||||
React.useEffect(() => {
|
||||
log.push('called');
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
// in legacy mode, this tests whether an act only flushes its own effects
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
|
||||
log = [];
|
||||
// for doublechecking, we flip it inside out, and assert on the outermost
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mocked scheduler', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.mock('scheduler', () =>
|
||||
require.requireActual('scheduler/unstable_mock')
|
||||
);
|
||||
React = require('react');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.unmock('scheduler');
|
||||
});
|
||||
|
||||
it('flushes work only outside the outermost act()', () => {
|
||||
let log = [];
|
||||
function Effecty() {
|
||||
React.useEffect(() => {
|
||||
log.push('called');
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
// with a mocked scheduler, this tests whether it flushes all work only on the outermost act
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
|
||||
log = [];
|
||||
// for doublechecking, we flip it inside out, and assert on the outermost
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactART;
|
||||
let ARTSVGMode;
|
||||
let ARTCurrentMode;
|
||||
let TestUtils;
|
||||
let TestRenderer;
|
||||
let ARTTest;
|
||||
|
||||
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
||||
|
||||
expect.extend(require('../toWarnDev'));
|
||||
|
||||
function App(props) {
|
||||
return 'hello world';
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactART = require('react-art');
|
||||
ARTSVGMode = require('art/modes/svg');
|
||||
ARTCurrentMode = require('art/modes/current');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
|
||||
ARTCurrentMode.setCurrent(ARTSVGMode);
|
||||
|
||||
ARTTest = function ARTTestComponent(props) {
|
||||
return (
|
||||
<ReactART.Surface width={150} height={200}>
|
||||
<ReactART.Group>
|
||||
<ReactART.Shape
|
||||
d="M0,0l50,0l0,50l-50,0z"
|
||||
fill={new ReactART.LinearGradient(['black', 'white'])}
|
||||
key="a"
|
||||
width={50}
|
||||
height={50}
|
||||
x={50}
|
||||
y={50}
|
||||
opacity={0.1}
|
||||
/>
|
||||
<ReactART.Shape
|
||||
fill="#3C5A99"
|
||||
key="b"
|
||||
scale={0.5}
|
||||
x={50}
|
||||
y={50}
|
||||
title="This is an F"
|
||||
cursor="pointer">
|
||||
M64.564,38.583H54l0.008-5.834c0-3.035,0.293-4.666,4.657-4.666
|
||||
h5.833V16.429h-9.33c-11.213,0-15.159,5.654-15.159,15.16v6.994
|
||||
h-6.99v11.652h6.99v33.815H54V50.235h9.331L64.564,38.583z
|
||||
</ReactART.Shape>
|
||||
</ReactART.Group>
|
||||
</ReactART.Surface>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: dom", () => {
|
||||
TestUtils.act(() => {
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: test", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
it('resets correctly across renderers', () => {
|
||||
function Effecty() {
|
||||
React.useEffect(() => {}, []);
|
||||
return null;
|
||||
}
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {});
|
||||
expect(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: render', () => {
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
TestUtils.renderIntoDocument(<Counter />);
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"]);
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .create()', () => {
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .update()', () => {
|
||||
const root = TestRenderer.create(<App key="one" />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
root.update(<App key="two" />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
TestRenderer.create(<Counter />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"]);
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-dom', () => {
|
||||
TestUtils.act(() => {
|
||||
TestUtils.renderIntoDocument(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-test-renderer', () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn if you use nested acts from different renderers", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using createRoot() + .render', () => {
|
||||
const root = ReactDOM.unstable_createRoot(document.createElement('div'));
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
root.render(<App />);
|
||||
});
|
||||
}).toWarnDev(
|
||||
[
|
||||
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked',
|
||||
"It looks like you're using the wrong act()",
|
||||
],
|
||||
{
|
||||
withoutStack: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1,233 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactART;
|
||||
let ARTSVGMode;
|
||||
let ARTCurrentMode;
|
||||
let TestUtils;
|
||||
let TestRenderer;
|
||||
let ARTTest;
|
||||
|
||||
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
||||
|
||||
expect.extend(require('./toWarnDev'));
|
||||
|
||||
function App(props) {
|
||||
return 'hello world';
|
||||
}
|
||||
|
||||
describe('legacy mode', () => {
|
||||
runTests();
|
||||
});
|
||||
|
||||
describe('mocked scheduler', () => {
|
||||
beforeEach(() => {
|
||||
jest.mock('scheduler', () =>
|
||||
require.requireActual('scheduler/unstable_mock')
|
||||
);
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.unmock('scheduler');
|
||||
});
|
||||
runTests();
|
||||
});
|
||||
|
||||
function runTests() {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactART = require('react-art');
|
||||
ARTSVGMode = require('art/modes/svg');
|
||||
ARTCurrentMode = require('art/modes/current');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
|
||||
ARTCurrentMode.setCurrent(ARTSVGMode);
|
||||
|
||||
ARTTest = function ARTTest(props) {
|
||||
return (
|
||||
<ReactART.Surface width={150} height={200}>
|
||||
<ReactART.Group>
|
||||
<ReactART.Shape
|
||||
d="M0,0l50,0l0,50l-50,0z"
|
||||
fill={new ReactART.LinearGradient(['black', 'white'])}
|
||||
key="a"
|
||||
width={50}
|
||||
height={50}
|
||||
x={50}
|
||||
y={50}
|
||||
opacity={0.1}
|
||||
/>
|
||||
<ReactART.Shape
|
||||
fill="#3C5A99"
|
||||
key="b"
|
||||
scale={0.5}
|
||||
x={50}
|
||||
y={50}
|
||||
title="This is an F"
|
||||
cursor="pointer">
|
||||
M64.564,38.583H54l0.008-5.834c0-3.035,0.293-4.666,4.657-4.666
|
||||
h5.833V16.429h-9.33c-11.213,0-15.159,5.654-15.159,15.16v6.994
|
||||
h-6.99v11.652h6.99v33.815H54V50.235h9.331L64.564,38.583z
|
||||
</ReactART.Shape>
|
||||
</ReactART.Group>
|
||||
</ReactART.Surface>
|
||||
);
|
||||
};
|
||||
});
|
||||
it("doesn't warn when you use the right act + renderer: dom", () => {
|
||||
TestUtils.act(() => {
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: test", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
it('resets correctly across renderers', () => {
|
||||
function Effecty() {
|
||||
React.useEffect(() => {}, []);
|
||||
return null;
|
||||
}
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {});
|
||||
expect(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using createRoot() + .render', () => {
|
||||
const root = ReactDOM.unstable_createRoot(document.createElement('div'));
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
root.render(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: render', () => {
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - test + dom: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
TestUtils.renderIntoDocument(<Counter />);
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"]);
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .create()', () => {
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: .update()', () => {
|
||||
const root = TestRenderer.create(<App key="one" />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
root.update(<App key="two" />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when using the wrong act version - dom + test: updates', () => {
|
||||
let setCtr;
|
||||
function Counter(props) {
|
||||
const [ctr, _setCtr] = React.useState(0);
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
const root = TestRenderer.create(<Counter />);
|
||||
expect(() => {
|
||||
TestUtils.act(() => {
|
||||
setCtr(1);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"]);
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-dom', () => {
|
||||
TestUtils.act(() => {
|
||||
TestUtils.renderIntoDocument(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not warn when nesting react-act inside react-test-renderer', () => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't warn if you use nested acts from different renderers", () => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<App />);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('flushes work only outside the outermost act(), even when nested from different renderers', () => {
|
||||
const log = [];
|
||||
function Effecty() {
|
||||
React.useEffect(() => {
|
||||
log.push('called');
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
// in legacy mode, this tests whether an act only flushes its own effects
|
||||
// with a mocked scheduler, this tests whether it flushes all work only on the outermost act
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
|
||||
log.splice(0);
|
||||
// for doublechecking, we flip it inside out, and assert on the outermost
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
});
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let ReactFeatureFlags;
|
||||
let act;
|
||||
describe('mocked scheduler', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutMissingMockScheduler = true;
|
||||
jest.unmock('scheduler');
|
||||
act = require('react-dom/test-utils').act;
|
||||
});
|
||||
it("should warn when the scheduler isn't mocked", () => {
|
||||
expect(() => act(() => {})).toWarnDev(
|
||||
[
|
||||
'Starting from React v17, the "scheduler" module will need to be mocked',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
function App() {
|
||||
return null;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.unmock('scheduler');
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutUnmockedScheduler = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ReactFeatureFlags.warnAboutUnmockedScheduler = false;
|
||||
});
|
||||
|
||||
it('should warn in sync mode', () => {
|
||||
expect(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
}).toWarnDev(
|
||||
['Starting from React v17, the "scheduler" module will need to be mocked'],
|
||||
{withoutStack: true},
|
||||
);
|
||||
// does not warn twice
|
||||
expect(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
}).toWarnDev([]);
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
|
||||
function App() {
|
||||
return null;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.unmock('scheduler');
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
});
|
||||
|
||||
it('does not warn when rendering in sync mode', () => {
|
||||
expect(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
}).toWarnDev([]);
|
||||
});
|
||||
|
||||
it('should warn when rendering in concurrent mode', () => {
|
||||
expect(() => {
|
||||
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
|
||||
}).toWarnDev(
|
||||
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
// does not warn twice
|
||||
expect(() => {
|
||||
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
|
||||
}).toWarnDev([]);
|
||||
});
|
||||
|
||||
it('should warn when rendering in batched mode', () => {
|
||||
expect(() => {
|
||||
ReactDOM.unstable_createSyncRoot(document.createElement('div')).render(
|
||||
<App />,
|
||||
);
|
||||
}).toWarnDev(
|
||||
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
// does not warn twice
|
||||
expect(() => {
|
||||
ReactDOM.unstable_createSyncRoot(document.createElement('div')).render(
|
||||
<App />,
|
||||
);
|
||||
}).toWarnDev([]);
|
||||
});
|
||||
@@ -12,7 +12,6 @@ import type {Thenable} from 'react-reconciler/src/ReactFiberWorkLoop';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {warnAboutMissingMockScheduler} from 'shared/ReactFeatureFlags';
|
||||
import enqueueTask from 'shared/enqueueTask';
|
||||
import * as Scheduler from 'scheduler';
|
||||
|
||||
@@ -43,27 +42,11 @@ const {IsSomeRendererActing} = ReactSharedInternals;
|
||||
// this implementation should be exactly the same in
|
||||
// ReactTestUtilsAct.js, ReactTestRendererAct.js, createReactNoop.js
|
||||
|
||||
let hasWarnedAboutMissingMockScheduler = false;
|
||||
const isSchedulerMocked =
|
||||
typeof Scheduler.unstable_flushAllWithoutAsserting === 'function';
|
||||
const flushWork =
|
||||
Scheduler.unstable_flushAllWithoutAsserting ||
|
||||
function() {
|
||||
if (warnAboutMissingMockScheduler === true) {
|
||||
if (hasWarnedAboutMissingMockScheduler === false) {
|
||||
warningWithoutStack(
|
||||
null,
|
||||
'Starting from React v17, the "scheduler" module will need to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers. To fix this, add the following ' +
|
||||
"to the top of your tests, or in your framework's global config file -\n\n" +
|
||||
'As an example, for jest - \n' +
|
||||
"jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));\n\n" +
|
||||
'For more info, visit https://fb.me/react-mock-scheduler',
|
||||
);
|
||||
hasWarnedAboutMissingMockScheduler = true;
|
||||
}
|
||||
}
|
||||
|
||||
let didFlushWork = false;
|
||||
while (flushPassiveEffects()) {
|
||||
didFlushWork = true;
|
||||
|
||||
@@ -27,7 +27,6 @@ import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
|
||||
import enqueueTask from 'shared/enqueueTask';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {warnAboutMissingMockScheduler} from 'shared/ReactFeatureFlags';
|
||||
import {ConcurrentRoot, BatchedRoot, LegacyRoot} from 'shared/ReactRootTags';
|
||||
|
||||
type Container = {
|
||||
@@ -599,27 +598,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
||||
// this act() implementation should be exactly the same in
|
||||
// ReactTestUtilsAct.js, ReactTestRendererAct.js, createReactNoop.js
|
||||
|
||||
let hasWarnedAboutMissingMockScheduler = false;
|
||||
const isSchedulerMocked =
|
||||
typeof Scheduler.unstable_flushAllWithoutAsserting === 'function';
|
||||
const flushWork =
|
||||
Scheduler.unstable_flushAllWithoutAsserting ||
|
||||
function() {
|
||||
if (warnAboutMissingMockScheduler === true) {
|
||||
if (hasWarnedAboutMissingMockScheduler === false) {
|
||||
warningWithoutStack(
|
||||
null,
|
||||
'Starting from React v17, the "scheduler" module will need to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers. To fix this, add the following ' +
|
||||
"to the top of your tests, or in your framework's global config file -\n\n" +
|
||||
'As an example, for jest - \n' +
|
||||
"jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));\n\n" +
|
||||
'For more info, visit https://fb.me/react-mock-scheduler',
|
||||
);
|
||||
hasWarnedAboutMissingMockScheduler = true;
|
||||
}
|
||||
}
|
||||
|
||||
let didFlushWork = false;
|
||||
while (flushPassiveEffects()) {
|
||||
didFlushWork = true;
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
flushDiscreteUpdates,
|
||||
flushPassiveEffects,
|
||||
warnIfNotScopedWithMatchingAct,
|
||||
warnIfUnmockedScheduler,
|
||||
IsThisRendererActing,
|
||||
} from './ReactFiberWorkLoop';
|
||||
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
|
||||
@@ -314,6 +315,7 @@ export function updateContainer(
|
||||
if (__DEV__) {
|
||||
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
|
||||
if ('undefined' !== typeof jest) {
|
||||
warnIfUnmockedScheduler(current);
|
||||
warnIfNotScopedWithMatchingAct(current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
enableProfilerTimer,
|
||||
enableSchedulerTracing,
|
||||
revertPassiveEffectsChange,
|
||||
warnAboutUnmockedScheduler,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import invariant from 'shared/invariant';
|
||||
@@ -47,6 +48,9 @@ import {
|
||||
scheduleSyncCallback,
|
||||
} from './SchedulerWithReactIntegration';
|
||||
|
||||
// The scheduler is imported here *only* to detect whether it's been mocked
|
||||
import * as Scheduler from 'scheduler';
|
||||
|
||||
import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
|
||||
|
||||
import {
|
||||
@@ -2522,6 +2526,41 @@ function warnIfNotCurrentlyActingUpdatesInDEV(fiber: Fiber): void {
|
||||
|
||||
export const warnIfNotCurrentlyActingUpdatesInDev = warnIfNotCurrentlyActingUpdatesInDEV;
|
||||
|
||||
// In tests, we want to enforce a mocked scheduler.
|
||||
let didWarnAboutUnmockedScheduler = false;
|
||||
// TODO Before we release concurrent mode, revisit this and decide whether a mocked
|
||||
// scheduler is the actual recommendation. The alternative could be a testing build,
|
||||
// a new lib, or whatever; we dunno just yet. This message is for early adopters
|
||||
// to get their tests right.
|
||||
|
||||
export function warnIfUnmockedScheduler(fiber: Fiber) {
|
||||
if (__DEV__) {
|
||||
if (didWarnAboutUnmockedScheduler === false) {
|
||||
if (fiber.mode & BatchedMode || fiber.mode & ConcurrentMode) {
|
||||
didWarnAboutUnmockedScheduler = true;
|
||||
warningWithoutStack(
|
||||
Scheduler.unstable_flushAllWithoutAsserting !== undefined,
|
||||
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers. ' +
|
||||
'For example, with jest: \n' +
|
||||
"jest.mock('scheduler', () => require('scheduler/unstable_mock'));\n\n" +
|
||||
'For more info, visit https://fb.me/react-mock-scheduler',
|
||||
);
|
||||
} else if (warnAboutUnmockedScheduler === true) {
|
||||
didWarnAboutUnmockedScheduler = true;
|
||||
warningWithoutStack(
|
||||
null,
|
||||
'Starting from React v17, the "scheduler" module will need to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers. ' +
|
||||
'For example, with jest: \n' +
|
||||
"jest.mock('scheduler', () => require('scheduler/unstable_mock'));\n\n" +
|
||||
'For more info, visit https://fb.me/react-mock-scheduler',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let componentsThatSuspendedAtHighPri = null;
|
||||
let componentsThatTriggeredHighPriSuspend = null;
|
||||
export function checkForWrongSuspensePriorityInDEV(sourceFiber: Fiber) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'react-reconciler/inline.test';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {warnAboutMissingMockScheduler} from 'shared/ReactFeatureFlags';
|
||||
import enqueueTask from 'shared/enqueueTask';
|
||||
import * as Scheduler from 'scheduler';
|
||||
|
||||
@@ -24,27 +23,11 @@ const {IsSomeRendererActing} = ReactSharedInternals;
|
||||
// this implementation should be exactly the same in
|
||||
// ReactTestUtilsAct.js, ReactTestRendererAct.js, createReactNoop.js
|
||||
|
||||
let hasWarnedAboutMissingMockScheduler = false;
|
||||
const isSchedulerMocked =
|
||||
typeof Scheduler.unstable_flushAllWithoutAsserting === 'function';
|
||||
const flushWork =
|
||||
Scheduler.unstable_flushAllWithoutAsserting ||
|
||||
function() {
|
||||
if (warnAboutMissingMockScheduler === true) {
|
||||
if (hasWarnedAboutMissingMockScheduler === false) {
|
||||
warningWithoutStack(
|
||||
null,
|
||||
'Starting from React v17, the "scheduler" module will need to be mocked ' +
|
||||
'to guarantee consistent behaviour across tests and browsers. To fix this, add the following ' +
|
||||
"to the top of your tests, or in your framework's global config file -\n\n" +
|
||||
'As an example, for jest - \n' +
|
||||
"jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));\n\n" +
|
||||
'For more info, visit https://fb.me/react-mock-scheduler',
|
||||
);
|
||||
hasWarnedAboutMissingMockScheduler = true;
|
||||
}
|
||||
}
|
||||
|
||||
let didFlushWork = false;
|
||||
while (flushPassiveEffects()) {
|
||||
didFlushWork = true;
|
||||
|
||||
@@ -70,7 +70,7 @@ export const enableJSXTransformAPI = false;
|
||||
|
||||
// We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?)
|
||||
// Till then, we warn about the missing mock, but still fallback to a sync mode compatible version
|
||||
export const warnAboutMissingMockScheduler = false;
|
||||
export const warnAboutUnmockedScheduler = false;
|
||||
// Temporary flag to revert the fix in #15650
|
||||
export const revertPassiveEffectsChange = false;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export const warnAboutDeprecatedSetNativeProps = true;
|
||||
export const enableFlareAPI = false;
|
||||
export const enableFundamentalAPI = false;
|
||||
export const enableJSXTransformAPI = false;
|
||||
export const warnAboutMissingMockScheduler = true;
|
||||
export const warnAboutUnmockedScheduler = true;
|
||||
export const revertPassiveEffectsChange = false;
|
||||
export const enableUserBlockingEvents = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
|
||||
export const enableFlareAPI = false;
|
||||
export const enableFundamentalAPI = false;
|
||||
export const enableJSXTransformAPI = false;
|
||||
export const warnAboutMissingMockScheduler = false;
|
||||
export const warnAboutUnmockedScheduler = false;
|
||||
export const revertPassiveEffectsChange = false;
|
||||
export const enableUserBlockingEvents = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
|
||||
export const enableFlareAPI = false;
|
||||
export const enableFundamentalAPI = false;
|
||||
export const enableJSXTransformAPI = false;
|
||||
export const warnAboutMissingMockScheduler = true;
|
||||
export const warnAboutUnmockedScheduler = true;
|
||||
export const revertPassiveEffectsChange = false;
|
||||
export const enableUserBlockingEvents = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
|
||||
export const enableFlareAPI = false;
|
||||
export const enableFundamentalAPI = false;
|
||||
export const enableJSXTransformAPI = false;
|
||||
export const warnAboutMissingMockScheduler = false;
|
||||
export const warnAboutUnmockedScheduler = false;
|
||||
export const revertPassiveEffectsChange = false;
|
||||
export const enableUserBlockingEvents = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
|
||||
@@ -30,7 +30,7 @@ export const disableJavaScriptURLs = false;
|
||||
export const enableFlareAPI = true;
|
||||
export const enableFundamentalAPI = false;
|
||||
export const enableJSXTransformAPI = true;
|
||||
export const warnAboutMissingMockScheduler = true;
|
||||
export const warnAboutUnmockedScheduler = true;
|
||||
export const enableUserBlockingEvents = false;
|
||||
export const enableSuspenseCallback = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
|
||||
@@ -74,7 +74,7 @@ export const enableFundamentalAPI = false;
|
||||
|
||||
export const enableJSXTransformAPI = true;
|
||||
|
||||
export const warnAboutMissingMockScheduler = true;
|
||||
export const warnAboutUnmockedScheduler = true;
|
||||
|
||||
export const enableSuspenseCallback = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user