Test renderer flushAll method verifies an array of expected yields (#13174)
Brian Vaughn committed
Jul 9, 2018 at 09:05 UTC
1f32d3c6dcc51f6a540997ab884a300b3f2ac5d4
3 files changed
+89
-55
packages/react-test-renderer/src/ReactTestRendererScheduling.js
+39
-36
@@ -12,7 +12,7 @@ import type {Deadline} from 'react-reconciler/src/ReactFiberScheduler';
12
// Current virtual time
13
export let nowImplementation = () => 0;
14
export let scheduledCallback: ((deadline: Deadline) => mixed) | null = null;
15
-export let yieldedValues: Array<mixed> | null = null;
15
+export let yieldedValues: Array<mixed> = [];
16
17
export function scheduleDeferredCallback(
18
callback: (deadline: Deadline) => mixed,
@@ -31,8 +31,39 @@ export function setNowImplementation(implementation: () => number): void {
31
nowImplementation = implementation;
32
}
33
34
-export function flushAll(): Array<mixed> {
35
- yieldedValues = null;
34
+function verifyExpectedValues(expectedValues: Array<mixed>): void {
35
+ for (let i = 0; i < expectedValues.length; i++) {
36
+ const expectedValue = `"${(expectedValues[i]: any)}"`;
37
+ const yieldedValue =
38
+ i < yieldedValues.length ? `"${(yieldedValues[i]: any)}"` : 'nothing';
39
+ if (yieldedValue !== expectedValue) {
40
+ const error = new Error(
41
+ `Flush expected to yield ${(expectedValue: any)}, but ${(yieldedValue: any)} was yielded`,
42
+ );
43
+ // Attach expected and yielded arrays,
44
+ // So the caller could pretty print the diff (if desired).
45
+ (error: any).expectedValues = expectedValues;
46
+ (error: any).actualValues = yieldedValues;
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ if (expectedValues.length !== yieldedValues.length) {
52
+ const error = new Error(
53
+ `Flush expected to yield ${expectedValues.length} values, but yielded ${
54
+ yieldedValues.length
55
+ }`,
56
+ );
57
+ // Attach expected and yielded arrays,
58
+ // So the caller could pretty print the diff (if desired).
59
+ (error: any).expectedValues = expectedValues;
60
+ (error: any).actualValues = yieldedValues;
61
+ throw error;
62
+ }
63
+}
64
+
65
+export function flushAll(expectedValues: Array<mixed>): Array<mixed> {
66
+ yieldedValues = [];
67
while (scheduledCallback !== null) {
68
const cb = scheduledCallback;
69
scheduledCallback = null;
@@ -47,25 +78,19 @@ export function flushAll(): Array<mixed> {
78
didTimeout: false,
79
});
80
}
50
- if (yieldedValues === null) {
51
- // Always return an array.
52
- return [];
53
- }
81
+ verifyExpectedValues(expectedValues);
82
return yieldedValues;
83
}
84
85
export function flushThrough(expectedValues: Array<mixed>): Array<mixed> {
86
let didStop = false;
59
- yieldedValues = null;
87
+ yieldedValues = [];
88
while (scheduledCallback !== null && !didStop) {
89
const cb = scheduledCallback;
90
scheduledCallback = null;
91
cb({
92
timeRemaining() {
65
- if (
66
- yieldedValues !== null &&
67
- yieldedValues.length >= expectedValues.length
68
- ) {
93
+ if (yieldedValues.length >= expectedValues.length) {
94
// We at least as many values as expected. Stop rendering.
95
didStop = true;
96
return 0;
@@ -79,34 +104,12 @@ export function flushThrough(expectedValues: Array<mixed>): Array<mixed> {
104
didTimeout: false,
105
});
106
}
82
- if (yieldedValues === null) {
83
- // Always return an array.
84
- yieldedValues = [];
85
- }
86
- for (let i = 0; i < expectedValues.length; i++) {
87
- const expectedValue = `"${(expectedValues[i]: any)}"`;
88
- const yieldedValue =
89
- i < yieldedValues.length ? `"${(yieldedValues[i]: any)}"` : 'nothing';
90
- if (yieldedValue !== expectedValue) {
91
- const error = new Error(
92
- `flushThrough expected to yield ${(expectedValue: any)}, but ${(yieldedValue: any)} was yielded`,
93
- );
94
- // Attach expected and yielded arrays,
95
- // So the caller could pretty print the diff (if desired).
96
- (error: any).expectedValues = expectedValues;
97
- (error: any).actualValues = yieldedValues;
98
- throw error;
99
- }
100
- }
107
+ verifyExpectedValues(expectedValues);
108
return yieldedValues;
109
}
110
111
export function yieldValue(value: mixed): void {
105
- if (yieldedValues === null) {
106
- yieldedValues = [value];
107
- } else {
108
- yieldedValues.push(value);
109
- }
112
+ yieldedValues.push(value);
113
}
114
115
export function withCleanYields(fn: Function) {
packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
+42
-8
@@ -32,7 +32,7 @@ describe('ReactTestRendererAsync', () => {
32
expect(renderer.toJSON()).toEqual(null);
33
34
// Flush initial mount.
35
- renderer.unstable_flushAll();
35
+ renderer.unstable_flushAll([]);
36
expect(renderer.toJSON()).toEqual('Hi');
37
38
// Update
@@ -40,7 +40,7 @@ describe('ReactTestRendererAsync', () => {
40
// Not yet updated.
41
expect(renderer.toJSON()).toEqual('Hi');
42
// Flush update.
43
- renderer.unstable_flushAll();
43
+ renderer.unstable_flushAll([]);
44
expect(renderer.toJSON()).toEqual('Bye');
45
});
46
@@ -62,11 +62,11 @@ describe('ReactTestRendererAsync', () => {
62
unstable_isAsync: true,
63
});
64
65
- expect(renderer.unstable_flushAll()).toEqual(['A:1', 'B:1', 'C:1']);
65
+ renderer.unstable_flushAll(['A:1', 'B:1', 'C:1']);
66
expect(renderer.toJSON()).toEqual(['A:1', 'B:1', 'C:1']);
67
68
renderer.update(<Parent step={2} />);
69
- expect(renderer.unstable_flushAll()).toEqual(['A:2', 'B:2', 'C:2']);
69
+ renderer.unstable_flushAll(['A:2', 'B:2', 'C:2']);
70
expect(renderer.toJSON()).toEqual(['A:2', 'B:2', 'C:2']);
71
});
72
@@ -97,7 +97,7 @@ describe('ReactTestRendererAsync', () => {
97
expect(renderer.toJSON()).toEqual(null);
98
99
// Flush the remaining work
100
- expect(renderer.unstable_flushAll()).toEqual(['C:1']);
100
+ renderer.unstable_flushAll(['C:1']);
101
expect(renderer.toJSON()).toEqual(['A:1', 'B:1', 'C:1']);
102
});
103
@@ -159,7 +159,41 @@ describe('ReactTestRendererAsync', () => {
159
);
160
161
expect(() => renderer.unstable_flushThrough(['foo', 'baz'])).toThrow(
162
- 'flushThrough expected to yield "baz", but "bar" was yielded',
162
+ 'Flush expected to yield "baz", but "bar" was yielded',
163
+ );
164
+ });
165
+
166
+ it('should error if flushAll params dont match yielded values', () => {
167
+ const Yield = ({id}) => {
168
+ renderer.unstable_yield(id);
169
+ return id;
170
+ };
171
+
172
+ const renderer = ReactTestRenderer.create(
173
+ <div>
174
+ <Yield id="foo" />
175
+ <Yield id="bar" />
176
+ <Yield id="baz" />
177
+ </div>,
178
+ {
179
+ unstable_isAsync: true,
180
+ },
181
+ );
182
+
183
+ expect(() => renderer.unstable_flushAll([])).toThrow(
184
+ 'Flush expected to yield 0 values, but yielded 3',
185
+ );
186
+
187
+ renderer.update(
188
+ <div>
189
+ <Yield id="foo" />
190
+ <Yield id="bar" />
191
+ <Yield id="baz" />
192
+ </div>,
193
+ );
194
+
195
+ expect(() => renderer.unstable_flushAll(['foo', 'baz'])).toThrow(
196
+ 'Flush expected to yield "baz", but "bar" was yielded',
197
);
198
});
199
@@ -179,7 +213,7 @@ describe('ReactTestRendererAsync', () => {
213
);
214
215
expect(() => renderer.unstable_flushThrough(['foo', 'bar'])).toThrow(
182
- 'flushThrough expected to yield "bar", but nothing was yielded',
216
+ 'Flush expected to yield "bar", but nothing was yielded',
217
);
218
});
219
@@ -189,7 +223,7 @@ describe('ReactTestRendererAsync', () => {
223
});
224
225
expect(() => renderer.unstable_flushThrough(['foo'])).toThrow(
192
- 'flushThrough expected to yield "foo", but nothing was yielded',
226
+ 'Flush expected to yield "foo", but nothing was yielded',
227
);
228
});
229
});
packages/react/src/__tests__/ReactProfiler-test.internal.js
+8
-11
@@ -178,7 +178,7 @@ describe('Profiler', () => {
178
// Times are logged until a render is committed.
179
renderer.unstable_flushThrough(['first']);
180
expect(callback).toHaveBeenCalledTimes(0);
181
- expect(renderer.unstable_flushAll()).toEqual(['last']);
181
+ renderer.unstable_flushAll(['last']);
182
expect(callback).toHaveBeenCalledTimes(1);
183
});
184
@@ -532,7 +532,7 @@ describe('Profiler', () => {
532
expect(callback).toHaveBeenCalledTimes(0);
533
534
// Resume render for remaining children.
535
- expect(renderer.unstable_flushAll()).toEqual(['Yield:3']);
535
+ renderer.unstable_flushAll(['Yield:3']);
536
537
// Verify that logged times include both durations above.
538
expect(callback).toHaveBeenCalledTimes(1);
@@ -576,7 +576,7 @@ describe('Profiler', () => {
576
577
// Flush the remaninig work,
578
// Which should take an additional 10ms of simulated time.
579
- expect(renderer.unstable_flushAll()).toEqual(['Yield:10', 'Yield:17']);
579
+ renderer.unstable_flushAll(['Yield:10', 'Yield:17']);
580
expect(callback).toHaveBeenCalledTimes(2);
581
582
const [innerCall, outerCall] = callback.mock.calls;
@@ -647,7 +647,7 @@ describe('Profiler', () => {
647
callback.mockReset();
648
649
// Verify no more unexpected callbacks from low priority work
650
- expect(renderer.unstable_flushAll()).toEqual([]);
650
+ renderer.unstable_flushAll([]);
651
expect(callback).toHaveBeenCalledTimes(0);
652
});
653
@@ -672,7 +672,7 @@ describe('Profiler', () => {
672
673
// Render everything initially.
674
// This should take 21 seconds of actual and base time.
675
- expect(renderer.unstable_flushAll()).toEqual(['Yield:6', 'Yield:15']);
675
+ renderer.unstable_flushAll(['Yield:6', 'Yield:15']);
676
expect(callback).toHaveBeenCalledTimes(1);
677
let call = callback.mock.calls[0];
678
expect(call[2]).toBe(21); // actual time
@@ -733,7 +733,7 @@ describe('Profiler', () => {
733
expect(call[5]).toBe(275); // commit time
734
735
// Verify no more unexpected callbacks from low priority work
736
- expect(renderer.unstable_flushAll()).toEqual([]);
736
+ renderer.unstable_flushAll([]);
737
expect(callback).toHaveBeenCalledTimes(1);
738
});
739
@@ -780,7 +780,7 @@ describe('Profiler', () => {
780
// Render everything initially.
781
// This simulates a total of 14ms of actual render time.
782
// The base render time is also 14ms for the initial render.
783
- expect(renderer.unstable_flushAll()).toEqual([
783
+ renderer.unstable_flushAll([
784
'FirstComponent:1',
785
'Yield:4',
786
'SecondComponent:2',
@@ -836,10 +836,7 @@ describe('Profiler', () => {
836
// The tree contains 42ms of base render time at this point,
837
// Reflecting the most recent (longer) render durations.
838
// TODO: This actual time should decrease by 10ms once the scheduler supports resuming.
839
- expect(renderer.unstable_flushAll()).toEqual([
840
- 'FirstComponent:10',
841
- 'Yield:4',
842
- ]);
839
+ renderer.unstable_flushAll(['FirstComponent:10', 'Yield:4']);
840
expect(callback).toHaveBeenCalledTimes(1);
841
call = callback.mock.calls[0];
842
expect(call[2]).toBe(14); // actual time