Skip to content

act should work without mock Scheduler #19789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 74 additions & 44 deletions packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ afterEach(() => {
document.body.removeChild(container);
});

// @gate __DEV__
it('can use act to flush effects', () => {
function App() {
React.useEffect(() => {
Expand All @@ -62,6 +63,7 @@ it('can use act to flush effects', () => {
expect(clearYields()).toEqual([100]);
});

// @gate __DEV__
it('flushes effects on every call', () => {
function App() {
const [ctr, setCtr] = React.useState(0);
Expand Down Expand Up @@ -100,6 +102,7 @@ it('flushes effects on every call', () => {
expect(button.innerHTML).toEqual('5');
});

// @gate __DEV__
it("should keep flushing effects until they're done", () => {
function App() {
const [ctr, setCtr] = React.useState(0);
Expand All @@ -118,6 +121,7 @@ it("should keep flushing effects until they're done", () => {
expect(container.innerHTML).toEqual('5');
});

// @gate __DEV__
it('should flush effects only on exiting the outermost act', () => {
function App() {
React.useEffect(() => {
Expand All @@ -138,6 +142,7 @@ it('should flush effects only on exiting the outermost act', () => {
expect(clearYields()).toEqual([0]);
});

// @gate __DEV__
it('can handle cascading promises', async () => {
// this component triggers an effect, that waits a tick,
// then sets state. repeats this 5 times.
Expand Down

This file was deleted.

170 changes: 128 additions & 42 deletions packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,142 @@
* @emails react-core
*/

let React;
let ReactDOM;

function App() {
return null;
}

beforeEach(() => {
jest.resetModules();
jest.unmock('scheduler');
React = require('react');
ReactDOM = require('react-dom');

// Unmock the Scheduler, which is mocked by default in our test setup
jest.mock('scheduler', () => require.requireActual('scheduler'));
jest.mock('scheduler/src/SchedulerHostConfig', () =>
require.requireActual('scheduler/src/forks/SchedulerHostConfig.default.js'),
);
});

// @gate __DEV__
it('public implementation of `act` works without mocking scheduler (DOM)', async () => {
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-dom/test-utils');

const log = [];

function App() {
React.useEffect(() => {
log.push('Did mount');
}, []);
return 'App';
}

const container = document.createElement('div');
await TestUtils.act(async () => {
ReactDOM.render(<App />, container);
});
expect(container.textContent).toEqual('App');
expect(log).toEqual(['Did mount']);
});

it('does not warn when rendering in legacy mode', () => {
expect(() => {
ReactDOM.render(<App />, document.createElement('div'));
}).toErrorDev([]);
it('internal implementation of `act` throws if Scheduler is not mocked (DOM)', async () => {
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-dom/test-utils');

const log = [];

function App() {
React.useEffect(() => {
log.push('Did mount');
}, []);
return 'App';
}

const container = document.createElement('div');
let error = null;
try {
await TestUtils.unstable_concurrentAct(async () => {
ReactDOM.render(<App />, container);
});
} catch (e) {
error = e;
}
expect(error).not.toBe(null);
expect(error.message).toEqual(
'This version of `act` requires a special mock build of Scheduler.',
);
});

// @gate experimental
it('should warn when rendering in concurrent mode', () => {
expect(() => {
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
}).toErrorDev(
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
'to guarantee consistent behaviour across tests and browsers.',
{withoutStack: true},
it('internal implementation of `act` throws if Scheduler is not mocked (noop)', async () => {
const React = require('react');
const ReactNoop = require('react-noop-renderer');

const log = [];

function App() {
React.useEffect(() => {
log.push('Did mount');
}, []);
return 'App';
}

const root = ReactNoop.createRoot();
let error = null;
try {
await ReactNoop.act(async () => {
root.render(<App />);
});
} catch (e) {
error = e;
}
expect(error).not.toBe(null);
expect(error.message).toEqual(
'This version of `act` requires a special mock build of Scheduler.',
);
// does not warn twice
expect(() => {
ReactDOM.unstable_createRoot(document.createElement('div')).render(<App />);
}).toErrorDev([]);
});

// @gate experimental
it('should warn when rendering in blocking mode', () => {
expect(() => {
ReactDOM.unstable_createBlockingRoot(document.createElement('div')).render(
<App />,
);
}).toErrorDev(
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked ' +
'to guarantee consistent behaviour across tests and browsers.',
{withoutStack: true},
// @gate __DEV__
it('public implementation of `act` works without mocking scheduler (test renderer)', async () => {
const React = require('react');
const ReactTestRenderer = require('react-test-renderer');

const log = [];

function App() {
React.useEffect(() => {
log.push('Did mount');
}, []);
return 'App';
}

const root = ReactTestRenderer.create(null);
await ReactTestRenderer.act(async () => {
root.update(<App />);
});
expect(root.toJSON()).toEqual('App');
expect(log).toEqual(['Did mount']);
});

it('internal implementation of `act` throws if Scheduler is not mocked (test renderer)', async () => {
const React = require('react');
const ReactTestRenderer = require('react-test-renderer');

const log = [];

function App() {
React.useEffect(() => {
log.push('Did mount');
}, []);
return 'App';
}

const root = ReactTestRenderer.create(null);
let error = null;
try {
await ReactTestRenderer.unstable_concurrentAct(async () => {
root.update(<App />);
});
} catch (e) {
error = e;
}
expect(error).not.toBe(null);
expect(error.message).toEqual(
'This version of `act` requires a special mock build of Scheduler.',
);
// does not warn twice
expect(() => {
ReactDOM.unstable_createBlockingRoot(document.createElement('div')).render(
<App />,
);
}).toErrorDev([]);
});
2 changes: 0 additions & 2 deletions packages/react-reconciler/src/ReactFiberReconciler.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import {
flushDiscreteUpdates,
flushPassiveEffects,
warnIfNotScopedWithMatchingAct,
warnIfUnmockedScheduler,
IsThisRendererActing,
act,
} from './ReactFiberWorkLoop.new';
Expand Down Expand Up @@ -261,7 +260,6 @@ 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);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/react-reconciler/src/ReactFiberReconciler.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import {
flushDiscreteUpdates,
flushPassiveEffects,
warnIfNotScopedWithMatchingAct,
warnIfUnmockedScheduler,
IsThisRendererActing,
act,
} from './ReactFiberWorkLoop.old';
Expand Down Expand Up @@ -261,7 +260,6 @@ 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);
}
}
Expand Down
Loading