Skip to content

Commit 0cba581

Browse files
committed
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`.
1 parent 25685d8 commit 0cba581

File tree

5 files changed

+166
-235
lines changed

5 files changed

+166
-235
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails react-core
8+
* @jest-environment node
9+
*/
10+
11+
'use strict';
12+
13+
const React = require('react');
14+
const {startTransition, useDeferredValue} = React;
15+
const ReactNoop = require('react-noop-renderer');
16+
const {
17+
waitFor,
18+
waitForAll,
19+
waitForPaint,
20+
waitForThrow,
21+
assertLog,
22+
} = require('internal-test-utils');
23+
const act = require('jest-react').act;
24+
const Scheduler = require('scheduler/unstable_mock');
25+
26+
describe('ReactInternalTestUtils', () => {
27+
test('waitFor', async () => {
28+
const Yield = ({id}) => {
29+
Scheduler.unstable_yieldValue(id);
30+
return id;
31+
};
32+
33+
const root = ReactNoop.createRoot();
34+
startTransition(() => {
35+
root.render(
36+
<div>
37+
<Yield id="foo" />
38+
<Yield id="bar" />
39+
<Yield id="baz" />
40+
</div>
41+
);
42+
});
43+
44+
await waitFor(['foo', 'bar']);
45+
expect(root).toMatchRenderedOutput(null);
46+
await waitFor(['baz']);
47+
expect(root).toMatchRenderedOutput(null);
48+
await waitForAll([]);
49+
expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
50+
});
51+
52+
test('waitForAll', async () => {
53+
const Yield = ({id}) => {
54+
Scheduler.unstable_yieldValue(id);
55+
return id;
56+
};
57+
58+
const root = ReactNoop.createRoot();
59+
startTransition(() => {
60+
root.render(
61+
<div>
62+
<Yield id="foo" />
63+
<Yield id="bar" />
64+
<Yield id="baz" />
65+
</div>
66+
);
67+
});
68+
69+
await waitForAll(['foo', 'bar', 'baz']);
70+
expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
71+
});
72+
73+
test('waitForThrow', async () => {
74+
const Yield = ({id}) => {
75+
Scheduler.unstable_yieldValue(id);
76+
return id;
77+
};
78+
79+
function BadRender() {
80+
throw new Error('Oh no!');
81+
}
82+
83+
function App() {
84+
return (
85+
<div>
86+
<Yield id="A" />
87+
<Yield id="B" />
88+
<BadRender />
89+
<Yield id="C" />
90+
<Yield id="D" />
91+
</div>
92+
);
93+
}
94+
95+
const root = ReactNoop.createRoot();
96+
root.render(<App />);
97+
98+
await waitForThrow('Oh no!');
99+
assertLog([
100+
'A',
101+
'B',
102+
'C',
103+
'D',
104+
// React will try one more time before giving up.
105+
'A',
106+
'B',
107+
'C',
108+
'D',
109+
]);
110+
});
111+
112+
test('waitForPaint', async () => {
113+
function App({prop}) {
114+
const deferred = useDeferredValue(prop);
115+
const text = `Urgent: ${prop}, Deferred: ${deferred}`;
116+
Scheduler.unstable_yieldValue(text);
117+
return text;
118+
}
119+
120+
const root = ReactNoop.createRoot();
121+
root.render(<App prop="A" />);
122+
123+
await waitForAll(['Urgent: A, Deferred: A']);
124+
expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
125+
126+
// This update will result in two separate paints: an urgent one, and a
127+
// deferred one.
128+
root.render(<App prop="B" />);
129+
// Urgent paint
130+
await waitForPaint(['Urgent: B, Deferred: A']);
131+
expect(root).toMatchRenderedOutput('Urgent: B, Deferred: A');
132+
133+
// Deferred paint
134+
await waitForPaint(['Urgent: B, Deferred: B']);
135+
expect(root).toMatchRenderedOutput('Urgent: B, Deferred: B');
136+
});
137+
138+
test('assertLog', async () => {
139+
const Yield = ({id}) => {
140+
Scheduler.unstable_yieldValue(id);
141+
return id;
142+
};
143+
144+
function App() {
145+
return (
146+
<div>
147+
<Yield id="A" />
148+
<Yield id="B" />
149+
<Yield id="C" />
150+
</div>
151+
);
152+
}
153+
154+
const root = ReactNoop.createRoot();
155+
await act(async () => {
156+
root.render(<App />);
157+
});
158+
assertLog(['A', 'B', 'C']);
159+
});
160+
});

packages/react-devtools-shared/src/__tests__/setupTests.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,3 @@ afterEach(() => {
177177
// so that we don't disconnect the ReactCurrentDispatcher ref.
178178
jest.resetModules();
179179
});
180-
181-
expect.extend({
182-
...require('../../../../scripts/jest/matchers/schedulerTestMatchers'),
183-
});

packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ let ReactTestRenderer;
1515
let Scheduler;
1616
let waitForAll;
1717
let waitFor;
18-
let assertLog;
1918

2019
describe('ReactTestRendererAsync', () => {
2120
beforeEach(() => {
@@ -28,7 +27,6 @@ describe('ReactTestRendererAsync', () => {
2827
const InternalTestUtils = require('internal-test-utils');
2928
waitForAll = InternalTestUtils.waitForAll;
3029
waitFor = InternalTestUtils.waitFor;
31-
assertLog = InternalTestUtils.assertLog;
3230
});
3331

3432
it('flushAll flushes all work', async () => {
@@ -155,132 +153,4 @@ describe('ReactTestRendererAsync', () => {
155153
// Only the higher priority properties have been committed
156154
expect(renderer.toJSON()).toEqual(['A:2', 'B:2']);
157155
});
158-
159-
describe('Jest matchers', () => {
160-
it('toFlushAndYieldThrough', () => {
161-
const Yield = ({id}) => {
162-
Scheduler.unstable_yieldValue(id);
163-
return id;
164-
};
165-
166-
ReactTestRenderer.create(
167-
<div>
168-
<Yield id="foo" />
169-
<Yield id="bar" />
170-
<Yield id="baz" />
171-
</div>,
172-
{
173-
unstable_isConcurrent: true,
174-
},
175-
);
176-
177-
expect(() =>
178-
expect(Scheduler).toFlushAndYieldThrough(['foo', 'baz']),
179-
).toThrow('// deep equality');
180-
});
181-
182-
it('toFlushAndYield', () => {
183-
const Yield = ({id}) => {
184-
Scheduler.unstable_yieldValue(id);
185-
return id;
186-
};
187-
188-
const renderer = ReactTestRenderer.create(
189-
<div>
190-
<Yield id="foo" />
191-
<Yield id="bar" />
192-
<Yield id="baz" />
193-
</div>,
194-
{
195-
unstable_isConcurrent: true,
196-
},
197-
);
198-
199-
expect(() => expect(Scheduler).toFlushWithoutYielding()).toThrowError(
200-
'// deep equality',
201-
);
202-
203-
renderer.update(
204-
<div>
205-
<Yield id="foo" />
206-
<Yield id="bar" />
207-
<Yield id="baz" />
208-
</div>,
209-
);
210-
211-
expect(() => expect(Scheduler).toFlushAndYield(['foo', 'baz'])).toThrow(
212-
'// deep equality',
213-
);
214-
});
215-
216-
it('toFlushAndThrow', () => {
217-
const Yield = ({id}) => {
218-
Scheduler.unstable_yieldValue(id);
219-
return id;
220-
};
221-
222-
function BadRender() {
223-
throw new Error('Oh no!');
224-
}
225-
226-
function App() {
227-
return (
228-
<div>
229-
<Yield id="A" />
230-
<Yield id="B" />
231-
<BadRender />
232-
<Yield id="C" />
233-
<Yield id="D" />
234-
</div>
235-
);
236-
}
237-
238-
const renderer = ReactTestRenderer.create(<App />, {
239-
unstable_isConcurrent: true,
240-
});
241-
242-
expect(Scheduler).toFlushAndThrow('Oh no!');
243-
assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
244-
245-
renderer.update(<App />);
246-
247-
expect(Scheduler).toFlushAndThrow('Oh no!');
248-
assertLog(['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']);
249-
250-
renderer.update(<App />);
251-
expect(Scheduler).toFlushAndThrow('Oh no!');
252-
});
253-
});
254-
255-
it('toHaveYielded', () => {
256-
const Yield = ({id}) => {
257-
Scheduler.unstable_yieldValue(id);
258-
return id;
259-
};
260-
261-
function App() {
262-
return (
263-
<div>
264-
<Yield id="A" />
265-
<Yield id="B" />
266-
<Yield id="C" />
267-
</div>
268-
);
269-
}
270-
271-
ReactTestRenderer.create(<App />);
272-
expect(() => expect(Scheduler).toHaveYielded(['A', 'B'])).toThrow(
273-
'// deep equality',
274-
);
275-
});
276-
277-
it('flush methods throw if log is not empty', () => {
278-
ReactTestRenderer.create(<div />, {
279-
unstable_isConcurrent: true,
280-
});
281-
Scheduler.unstable_yieldValue('Something');
282-
expect(() => expect(Scheduler).toFlushWithoutYielding()).toThrow(
283-
'Log of yielded values is not empty.',
284-
);
285-
});
286156
});

scripts/jest/matchers/reactTestMatchers.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
22

33
const JestReact = require('jest-react');
4-
const SchedulerMatchers = require('./schedulerTestMatchers');
4+
5+
// TODO: Move to ReactInternalTestUtils
56

67
function captureAssertion(fn) {
78
// Trick to use a Jest matcher inside another Jest matcher. `fn` contains an
@@ -22,10 +23,11 @@ function captureAssertion(fn) {
2223
function assertYieldsWereCleared(Scheduler) {
2324
const actualYields = Scheduler.unstable_clearYields();
2425
if (actualYields.length !== 0) {
25-
throw new Error(
26-
'Log of yielded values is not empty. ' +
27-
'Call expect(Scheduler).toHaveYielded(...) first.'
26+
const error = Error(
27+
'The event log is not empty. Call assertLog(...) first.'
2828
);
29+
Error.captureStackTrace(error, assertYieldsWereCleared);
30+
throw error;
2931
}
3032
}
3133

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

4345
module.exports = {
44-
...SchedulerMatchers,
4546
toMatchRenderedOutput,
4647
};

0 commit comments

Comments
 (0)