Skip to content

Commit

Permalink
Add unit tests for expiration and coalescing
Browse files Browse the repository at this point in the history
  • Loading branch information
acdlite committed Aug 11, 2017
1 parent 8972ae8 commit 67388a1
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/renderers/noop/ReactNoopEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ function removeChild(
parentInstance.children.splice(index, 1);
}

let elapsedTimeInMs = 0;

var NoopRenderer = ReactFiberReconciler({
getRootHostContext() {
if (failInBeginPhase) {
Expand Down Expand Up @@ -206,8 +208,7 @@ var NoopRenderer = ReactFiberReconciler({
resetAfterCommit(): void {},

now(): number {
// TODO: Add an API to advance time.
return 0;
return elapsedTimeInMs;
},
});

Expand Down Expand Up @@ -344,6 +345,14 @@ var ReactNoop = {
expect(actual).toEqual(expected);
},

expire(ms: number): void {
elapsedTimeInMs += ms;
},

flushExpired(): Array<mixed> {
return ReactNoop.flushUnitsOfWork(0);
},

yield(value: mixed) {
if (yieldedValues === null) {
yieldedValues = [value];
Expand Down
129 changes: 129 additions & 0 deletions src/renderers/shared/fiber/__tests__/ReactExpiration-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';

var React;
var ReactNoop;

describe('ReactExpiration', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('react-noop-renderer');
});

function span(prop) {
return {type: 'span', children: [], prop};
}

it('increases priority of updates as time progresses', () => {
ReactNoop.render(<span prop="done" />);

expect(ReactNoop.getChildren()).toEqual([]);

// Nothing has expired yet because time hasn't advanced.
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([]);

// Advance by 300ms, not enough to expire the low pri update.
ReactNoop.expire(300);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([]);

// Advance by another second. Now the update should expire and flush.
ReactNoop.expire(1000);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span('done')]);
});

it('coalesces updates to the same component', () => {
const foos = [];
class Foo extends React.Component {
constructor() {
super();
this.state = {step: 0};
foos.push(this);
}
render() {
return <span prop={this.state.step} />;
}
}

ReactNoop.render([<Foo key="A" />, <Foo key="B" />]);
ReactNoop.flush();
const [a, b] = foos;

a.setState({step: 1});

// Advance time by 500ms.
ReactNoop.expire(500);

// Update A again. This update should coalesce with the previous update.
a.setState({step: 2});
// Update B. This is the first update, so it has nothing to coalesce with.
b.setState({step: 1});

// Advance time. This should be enough to flush both updates to A, but not
// the update to B. If only the first update to A flushes, but not the
// second, then it wasn't coalesced properly.
ReactNoop.expire(500);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span(2), span(0)]);

// Now expire the update to B.
ReactNoop.expire(500);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span(2), span(1)]);
});

it('stops coalescing after a certain threshold', () => {
let instance;
class Foo extends React.Component {
state = {step: 0};
render() {
instance = this;
return <span prop={this.state.step} />;
}
}

ReactNoop.render(<Foo />);
ReactNoop.flush();

instance.setState({step: 1});

// Advance time by 500 ms.
ReactNoop.expire(500);

// Update again. This update should coalesce with the previous update.
instance.setState({step: 2});

// Advance time by 480ms. Not enough to expire the updates.
ReactNoop.expire(480);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span(0)]);

// Update again. This update should NOT be coalesced, because the
// previous updates have almost expired.
instance.setState({step: 3});

// Advance time by 20ms. This should expire the first two updates,
// but not the third.
ReactNoop.expire(480);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span(2)]);

// Now expire the remaining update.
ReactNoop.expire(1000);
ReactNoop.flushExpired();
expect(ReactNoop.getChildren()).toEqual([span(3)]);
});
});

0 comments on commit 67388a1

Please sign in to comment.