Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Delete toFlushAndYield test helpers
Now that the tests have been codemodded, we can delete toFlushAndYield
et al from the codebase. See #26285 for full context.

The mock implementation of Scheduler still exposes some (unstable-
prefixed) APIs that can be used for some edge cases, like when testing
the mock Scheduler itself. There are only a few tests that do this, and
even some of those could be rewritten to use `act` or `waitFor`.
  • Loading branch information
acdlite committed Mar 4, 2023
commit 0cba581611bf587610624f23cde0e00cd4cc9cb7
160 changes: 160 additions & 0 deletions packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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
* @jest-environment node
*/

'use strict';

const React = require('react');
const {startTransition, useDeferredValue} = React;
const ReactNoop = require('react-noop-renderer');
const {
waitFor,
waitForAll,
waitForPaint,
waitForThrow,
assertLog,
} = require('internal-test-utils');
const act = require('jest-react').act;
const Scheduler = require('scheduler/unstable_mock');

describe('ReactInternalTestUtils', () => {
test('waitFor', async () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

const root = ReactNoop.createRoot();
startTransition(() => {
root.render(
<div>
<Yield id="foo" />
<Yield id="bar" />
<Yield id="baz" />
</div>
);
});

await waitFor(['foo', 'bar']);
expect(root).toMatchRenderedOutput(null);
await waitFor(['baz']);
expect(root).toMatchRenderedOutput(null);
await waitForAll([]);
expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
});

test('waitForAll', async () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

const root = ReactNoop.createRoot();
startTransition(() => {
root.render(
<div>
<Yield id="foo" />
<Yield id="bar" />
<Yield id="baz" />
</div>
);
});

await waitForAll(['foo', 'bar', 'baz']);
expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
});

test('waitForThrow', async () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

function BadRender() {
throw new Error('Oh no!');
}

function App() {
return (
<div>
<Yield id="A" />
<Yield id="B" />
<BadRender />
<Yield id="C" />
<Yield id="D" />
</div>
);
}

const root = ReactNoop.createRoot();
root.render(<App />);

await waitForThrow('Oh no!');
assertLog([
'A',
'B',
'C',
'D',
// React will try one more time before giving up.
'A',
'B',
'C',
'D',
]);
});

test('waitForPaint', async () => {
function App({prop}) {
const deferred = useDeferredValue(prop);
const text = `Urgent: ${prop}, Deferred: ${deferred}`;
Scheduler.unstable_yieldValue(text);
return text;
}

const root = ReactNoop.createRoot();
root.render(<App prop="A" />);

await waitForAll(['Urgent: A, Deferred: A']);
expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');

// This update will result in two separate paints: an urgent one, and a
// deferred one.
root.render(<App prop="B" />);
// Urgent paint
await waitForPaint(['Urgent: B, Deferred: A']);
expect(root).toMatchRenderedOutput('Urgent: B, Deferred: A');

// Deferred paint
await waitForPaint(['Urgent: B, Deferred: B']);
expect(root).toMatchRenderedOutput('Urgent: B, Deferred: B');
});

test('assertLog', async () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

function App() {
return (
<div>
<Yield id="A" />
<Yield id="B" />
<Yield id="C" />
</div>
);
}

const root = ReactNoop.createRoot();
await act(async () => {
root.render(<App />);
});
assertLog(['A', 'B', 'C']);
});
});
4 changes: 0 additions & 4 deletions packages/react-devtools-shared/src/__tests__/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,3 @@ afterEach(() => {
// so that we don't disconnect the ReactCurrentDispatcher ref.
jest.resetModules();
});

expect.extend({
...require('../../../../scripts/jest/matchers/schedulerTestMatchers'),
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ let ReactTestRenderer;
let Scheduler;
let waitForAll;
let waitFor;
let assertLog;

describe('ReactTestRendererAsync', () => {
beforeEach(() => {
Expand All @@ -28,7 +27,6 @@ describe('ReactTestRendererAsync', () => {
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
waitFor = InternalTestUtils.waitFor;
assertLog = InternalTestUtils.assertLog;
});

it('flushAll flushes all work', async () => {
Expand Down Expand Up @@ -155,132 +153,4 @@ describe('ReactTestRendererAsync', () => {
// Only the higher priority properties have been committed
expect(renderer.toJSON()).toEqual(['A:2', 'B:2']);
});

describe('Jest matchers', () => {
it('toFlushAndYieldThrough', () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

ReactTestRenderer.create(
<div>
<Yield id="foo" />
<Yield id="bar" />
<Yield id="baz" />
</div>,
{
unstable_isConcurrent: true,
},
);

expect(() =>
expect(Scheduler).toFlushAndYieldThrough(['foo', 'baz']),
).toThrow('// deep equality');
});

it('toFlushAndYield', () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

const renderer = ReactTestRenderer.create(
<div>
<Yield id="foo" />
<Yield id="bar" />
<Yield id="baz" />
</div>,
{
unstable_isConcurrent: true,
},
);

expect(() => expect(Scheduler).toFlushWithoutYielding()).toThrowError(
'// deep equality',
);

renderer.update(
<div>
<Yield id="foo" />
<Yield id="bar" />
<Yield id="baz" />
</div>,
);

expect(() => expect(Scheduler).toFlushAndYield(['foo', 'baz'])).toThrow(
'// deep equality',
);
});

it('toFlushAndThrow', () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

function BadRender() {
throw new Error('Oh no!');
}

function App() {
return (
<div>
<Yield id="A" />
<Yield id="B" />
<BadRender />
<Yield id="C" />
<Yield id="D" />
</div>
);
}

const renderer = ReactTestRenderer.create(<App />, {
unstable_isConcurrent: true,
});

expect(Scheduler).toFlushAndThrow('Oh no!');
assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);

renderer.update(<App />);

expect(Scheduler).toFlushAndThrow('Oh no!');
assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);

renderer.update(<App />);
expect(Scheduler).toFlushAndThrow('Oh no!');
});
});

it('toHaveYielded', () => {
const Yield = ({id}) => {
Scheduler.unstable_yieldValue(id);
return id;
};

function App() {
return (
<div>
<Yield id="A" />
<Yield id="B" />
<Yield id="C" />
</div>
);
}

ReactTestRenderer.create(<App />);
expect(() => expect(Scheduler).toHaveYielded(['A', 'B'])).toThrow(
'// deep equality',
);
});

it('flush methods throw if log is not empty', () => {
ReactTestRenderer.create(<div />, {
unstable_isConcurrent: true,
});
Scheduler.unstable_yieldValue('Something');
expect(() => expect(Scheduler).toFlushWithoutYielding()).toThrow(
'Log of yielded values is not empty.',
);
});
});
11 changes: 6 additions & 5 deletions scripts/jest/matchers/reactTestMatchers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';

const JestReact = require('jest-react');
const SchedulerMatchers = require('./schedulerTestMatchers');

// TODO: Move to ReactInternalTestUtils

function captureAssertion(fn) {
// Trick to use a Jest matcher inside another Jest matcher. `fn` contains an
Expand All @@ -22,10 +23,11 @@ function captureAssertion(fn) {
function assertYieldsWereCleared(Scheduler) {
const actualYields = Scheduler.unstable_clearYields();
if (actualYields.length !== 0) {
throw new Error(
'Log of yielded values is not empty. ' +
'Call expect(Scheduler).toHaveYielded(...) first.'
const error = Error(
'The event log is not empty. Call assertLog(...) first.'
);
Error.captureStackTrace(error, assertYieldsWereCleared);
throw error;
}
}

Expand All @@ -41,6 +43,5 @@ function toMatchRenderedOutput(ReactNoop, expectedJSX) {
}

module.exports = {
...SchedulerMatchers,
toMatchRenderedOutput,
};
Loading