Skip to content

Commit 14e68e3

Browse files
committed
Add unit tests for expiration and coalescing
1 parent 8972ae8 commit 14e68e3

File tree

2 files changed

+140
-2
lines changed

2 files changed

+140
-2
lines changed

src/renderers/noop/ReactNoopEntry.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ function removeChild(
8686
parentInstance.children.splice(index, 1);
8787
}
8888

89+
let elapsedTimeInMs = 0;
90+
8991
var NoopRenderer = ReactFiberReconciler({
9092
getRootHostContext() {
9193
if (failInBeginPhase) {
@@ -206,8 +208,7 @@ var NoopRenderer = ReactFiberReconciler({
206208
resetAfterCommit(): void {},
207209

208210
now(): number {
209-
// TODO: Add an API to advance time.
210-
return 0;
211+
return elapsedTimeInMs;
211212
},
212213
});
213214

@@ -344,6 +345,14 @@ var ReactNoop = {
344345
expect(actual).toEqual(expected);
345346
},
346347

348+
expire(ms: number): void {
349+
elapsedTimeInMs += ms;
350+
},
351+
352+
flushExpired(): Array<mixed> {
353+
return ReactNoop.flushUnitsOfWork(0);
354+
},
355+
347356
yield(value: mixed) {
348357
if (yieldedValues === null) {
349358
yieldedValues = [value];
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Copyright 2013-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @emails react-core
10+
*/
11+
12+
'use strict';
13+
14+
var React;
15+
var ReactNoop;
16+
17+
describe('ReactExpiration', () => {
18+
beforeEach(() => {
19+
jest.resetModules();
20+
React = require('react');
21+
ReactNoop = require('react-noop-renderer');
22+
});
23+
24+
function span(prop) {
25+
return {type: 'span', children: [], prop};
26+
}
27+
28+
it('increases priority of updates as time progresses', () => {
29+
ReactNoop.render(<span prop="done" />);
30+
31+
expect(ReactNoop.getChildren()).toEqual([]);
32+
33+
// Nothing has expired yet because time hasn't advanced.
34+
ReactNoop.flushExpired();
35+
expect(ReactNoop.getChildren()).toEqual([]);
36+
37+
// Advance by 300ms, not enough to expire the low pri update.
38+
ReactNoop.expire(300);
39+
ReactNoop.flushExpired();
40+
expect(ReactNoop.getChildren()).toEqual([]);
41+
42+
// Advance by another second. Now the update should expire and flush.
43+
ReactNoop.expire(1000);
44+
ReactNoop.flushExpired();
45+
expect(ReactNoop.getChildren()).toEqual([span('done')]);
46+
});
47+
48+
it('coalesces updates to the same component', () => {
49+
const foos = [];
50+
class Foo extends React.Component {
51+
constructor() {
52+
super();
53+
this.state = {step: 0};
54+
foos.push(this);
55+
}
56+
render() {
57+
return <span prop={this.state.step} />;
58+
}
59+
}
60+
61+
ReactNoop.render([<Foo key="A" />, <Foo key="B" />]);
62+
ReactNoop.flush();
63+
const [a, b] = foos;
64+
65+
a.setState({step: 1});
66+
67+
// Advance time by 500ms.
68+
ReactNoop.expire(500);
69+
70+
// Update A again. This update should coalesce with the previous update.
71+
a.setState({step: 2});
72+
// Update B. This is the first update, so it has nothing to coalesce with.
73+
b.setState({step: 1});
74+
75+
// Advance time. This should be enough to flush both updates to A, but not
76+
// the update to B. If only the first update to A flushes, but not the
77+
// second, then it wasn't coalesced properly.
78+
ReactNoop.expire(500);
79+
ReactNoop.flushExpired();
80+
expect(ReactNoop.getChildren()).toEqual([span(2), span(0)]);
81+
82+
// Now expire the update to B.
83+
ReactNoop.expire(500);
84+
ReactNoop.flushExpired();
85+
expect(ReactNoop.getChildren()).toEqual([span(2), span(1)]);
86+
});
87+
88+
it('stops coalescing after a certain threshold', () => {
89+
let instance;
90+
class Foo extends React.Component {
91+
state = {step: 0};
92+
render() {
93+
instance = this;
94+
return <span prop={this.state.step} />;
95+
}
96+
}
97+
98+
ReactNoop.render(<Foo />);
99+
ReactNoop.flush();
100+
101+
instance.setState({step: 1});
102+
103+
// Advance time by 500 ms.
104+
ReactNoop.expire(500);
105+
106+
// Update again. This update should coalesce with the previous update.
107+
instance.setState({step: 2});
108+
109+
// Advance time by 480ms. Not enough to expire the updates.
110+
ReactNoop.expire(480);
111+
ReactNoop.flushExpired();
112+
expect(ReactNoop.getChildren()).toEqual([span(0)]);
113+
114+
// Update again. This update should NOT be coalesced, because the
115+
// previous updates have almost expired.
116+
instance.setState({step: 3});
117+
118+
// Advance time. This should expire the first two updates,
119+
// but not the third.
120+
ReactNoop.expire(500);
121+
ReactNoop.flushExpired();
122+
expect(ReactNoop.getChildren()).toEqual([span(2)]);
123+
124+
// Now expire the remaining update.
125+
ReactNoop.expire(1000);
126+
ReactNoop.flushExpired();
127+
expect(ReactNoop.getChildren()).toEqual([span(3)]);
128+
});
129+
});

0 commit comments

Comments
 (0)