Skip to content

Commit 2803474

Browse files
committed
Add tests from #8716
1 parent 0a989ef commit 2803474

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

scripts/fiber/tests-passing.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,8 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
18601860
* can defer side-effects and resume them later on
18611861
* can defer side-effects and reuse them later - complex
18621862
* deprioritizes setStates that happens within a deprioritized tree
1863+
* does not drop priority from a progressed subtree
1864+
* does not complete already completed work
18631865
* calls callback after update is flushed
18641866
* calls setState callback even if component bails out
18651867
* calls componentWillUnmount after a deletion, even if nested

src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,172 @@ describe('ReactIncrementalSideEffects', () => {
795795
// moves to "current" without flushing due to having lower priority. Does this
796796
// even happen? Maybe a child doesn't get processed because it is lower prio?
797797

798+
it('does not drop priority from a progressed subtree', () => {
799+
let ops = [];
800+
let lowPri;
801+
let highPri;
802+
803+
function LowPriDidComplete() {
804+
ops.push('LowPriDidComplete');
805+
// Because this is terminal, beginning work on LowPriDidComplete implies
806+
// that LowPri will be completed before the scheduler yields.
807+
return null;
808+
}
809+
810+
class LowPri extends React.Component {
811+
state = {step: 0};
812+
render() {
813+
ops.push('LowPri');
814+
lowPri = this;
815+
return [
816+
<span key="1" prop={this.state.step} />,
817+
<LowPriDidComplete key="2" />,
818+
];
819+
}
820+
}
821+
822+
function LowPriSibling() {
823+
ops.push('LowPriSibling');
824+
return null;
825+
}
826+
827+
class HighPri extends React.Component {
828+
state = {step: 0};
829+
render() {
830+
ops.push('HighPri');
831+
highPri = this;
832+
return <span prop={this.state.step} />;
833+
}
834+
}
835+
836+
function App() {
837+
ops.push('App');
838+
return [
839+
<div key="1">
840+
<LowPri />
841+
<LowPriSibling />
842+
</div>,
843+
<div key="2"><HighPri /></div>,
844+
];
845+
}
846+
847+
ReactNoop.render(<App />);
848+
ReactNoop.flush();
849+
expect(ReactNoop.getChildren()).toEqual([div(span(0)), div(span(0))]);
850+
ops = [];
851+
852+
lowPri.setState({step: 1});
853+
// Do just enough work to begin LowPri
854+
ReactNoop.flushDeferredPri(30);
855+
expect(ops).toEqual(['LowPri']);
856+
// Now we'll do one more tick of work to complete LowPri. Because LowPri
857+
// has a sibling, the parent div of LowPri has not yet completed.
858+
ReactNoop.flushDeferredPri(10);
859+
expect(ops).toEqual(['LowPri', 'LowPriDidComplete']);
860+
expect(ReactNoop.getChildren()).toEqual([
861+
div(span(0)), // Complete, but not yet updated
862+
div(span(0)),
863+
]);
864+
ops = [];
865+
866+
// Interrupt with high pri update
867+
ReactNoop.performAnimationWork(() => highPri.setState({step: 1}));
868+
ReactNoop.flushAnimationPri();
869+
expect(ops).toEqual(['HighPri']);
870+
expect(ReactNoop.getChildren()).toEqual([
871+
div(span(0)), // Completed, but not yet updated
872+
div(span(1)),
873+
]);
874+
ops = [];
875+
876+
ReactNoop.flush();
877+
expect(ReactNoop.getChildren()).toEqual([div(span(1)), div(span(1))]);
878+
});
879+
880+
it('does not complete already completed work', () => {
881+
let ops = [];
882+
let lowPri;
883+
let highPri;
884+
885+
function LowPriDidComplete() {
886+
ops.push('LowPriDidComplete');
887+
// Because this is terminal, beginning work on LowPriDidComplete implies
888+
// that LowPri will be completed before the scheduler yields.
889+
return null;
890+
}
891+
892+
class LowPri extends React.Component {
893+
state = {step: 0};
894+
render() {
895+
ops.push('LowPri');
896+
lowPri = this;
897+
return [
898+
<span key="1" prop={this.state.step} />,
899+
<LowPriDidComplete key="2" />,
900+
];
901+
}
902+
}
903+
904+
function LowPriSibling() {
905+
ops.push('LowPriSibling');
906+
return null;
907+
}
908+
909+
class HighPri extends React.Component {
910+
state = {step: 0};
911+
render() {
912+
ops.push('HighPri');
913+
highPri = this;
914+
return <span prop={this.state.step} />;
915+
}
916+
}
917+
918+
function App() {
919+
ops.push('App');
920+
return [
921+
<div key="1">
922+
<LowPri />
923+
<LowPriSibling />
924+
</div>,
925+
<div key="2"><HighPri /></div>,
926+
];
927+
}
928+
929+
ReactNoop.render(<App />);
930+
ReactNoop.flush();
931+
expect(ReactNoop.getChildren()).toEqual([div(span(0)), div(span(0))]);
932+
ops = [];
933+
934+
lowPri.setState({step: 1});
935+
// Do just enough work to begin LowPri
936+
ReactNoop.flushDeferredPri(30);
937+
expect(ops).toEqual(['LowPri']);
938+
// Now we'll do one more tick of work to complete LowPri. Because LowPri
939+
// has a sibling, the parent div of LowPri has not yet completed.
940+
ReactNoop.flushDeferredPri(10);
941+
expect(ops).toEqual(['LowPri', 'LowPriDidComplete']);
942+
expect(ReactNoop.getChildren()).toEqual([
943+
div(span(0)), // Complete, but not yet updated
944+
div(span(0)),
945+
]);
946+
ops = [];
947+
948+
// Interrupt with high pri update
949+
ReactNoop.performAnimationWork(() => highPri.setState({step: 1}));
950+
ReactNoop.flushAnimationPri();
951+
expect(ops).toEqual(['HighPri']);
952+
expect(ReactNoop.getChildren()).toEqual([
953+
div(span(0)), // Completed, but not yet updated
954+
div(span(1)),
955+
]);
956+
ops = [];
957+
958+
// If this is not enough to commit the rest of the work, that means we're
959+
// not bailing out on the already-completed LowPri tree.
960+
ReactNoop.flushDeferredPri(45);
961+
expect(ReactNoop.getChildren()).toEqual([div(span(1)), div(span(1))]);
962+
});
963+
798964
it('calls callback after update is flushed', () => {
799965
let instance;
800966
class Foo extends React.Component {

0 commit comments

Comments
 (0)