@@ -15,7 +15,7 @@ var React;
15
15
var ReactNoop ;
16
16
var ReactFeatureFlags ;
17
17
18
- describe ( 'ReactConcurrency ' , ( ) => {
18
+ describe ( 'ReactIncrementalTriangle ' , ( ) => {
19
19
beforeEach ( ( ) => {
20
20
jest . resetModules ( ) ;
21
21
React = require ( 'react' ) ;
@@ -33,7 +33,6 @@ describe('ReactConcurrency', () => {
33
33
let triangles = [ ] ;
34
34
let leafTriangles = [ ] ;
35
35
let yieldAfterEachRender = false ;
36
- let lastRenderedTriangle = null ;
37
36
class Triangle extends React . Component {
38
37
constructor ( props ) {
39
38
super ( ) ;
@@ -64,9 +63,8 @@ describe('ReactConcurrency', () => {
64
63
) ;
65
64
}
66
65
render ( ) {
67
- lastRenderedTriangle = this ;
68
66
if ( yieldAfterEachRender ) {
69
- ReactNoop . yieldBeforeNextUnitOfWork ( ) ;
67
+ ReactNoop . yield ( this ) ;
70
68
}
71
69
const { counter, depth} = this . props ;
72
70
if ( depth === 0 ) {
@@ -83,7 +81,7 @@ describe('ReactConcurrency', () => {
83
81
}
84
82
}
85
83
86
- let app ;
84
+ let appInstance ;
87
85
class App extends React . Component {
88
86
state = { counter : 0 } ;
89
87
interrupt ( ) {
@@ -98,16 +96,32 @@ describe('ReactConcurrency', () => {
98
96
return currentCounter ;
99
97
}
100
98
render ( ) {
101
- app = this ;
99
+ appInstance = this ;
102
100
return < Triangle counter = { this . state . counter } depth = { 3 } /> ;
103
101
}
104
102
}
105
103
106
104
const depth = 3 ;
107
- ReactNoop . render ( < App depth = { depth } /> ) ;
108
- ReactNoop . flush ( ) ;
109
- // Check initial mount
110
- treeIsConsistent ( 0 ) ;
105
+
106
+ function reset ( nextStep = 0 ) {
107
+ triangles = [ ] ;
108
+ leafTriangles = [ ] ;
109
+
110
+ const previousYieldAfterEachRender = yieldAfterEachRender ;
111
+ yieldAfterEachRender = false ;
112
+ ReactNoop . render ( [ ] ) ;
113
+ ReactNoop . flush ( ) ;
114
+ ReactNoop . render ( < App depth = { depth } /> ) ;
115
+ ReactNoop . flush ( ) ;
116
+ // Check initial mount
117
+ treeIsConsistent ( nextStep ) ;
118
+ yieldAfterEachRender = previousYieldAfterEachRender ;
119
+ return appInstance ;
120
+ }
121
+
122
+ reset ( ) ;
123
+ const totalChildren = leafTriangles . length ;
124
+ const totalTriangles = triangles . length ;
111
125
112
126
function treeIsConsistent ( counter , activeTriangles = new Set ( ) ) {
113
127
let activeIndices = [ ] ;
@@ -136,15 +150,9 @@ describe('ReactConcurrency', () => {
136
150
}
137
151
}
138
152
139
- function renderNextTriangle ( ) {
140
- yieldAfterEachRender = true ;
141
- lastRenderedTriangle = null ;
142
- ReactNoop . flush ( ) ;
143
- yieldAfterEachRender = false ;
144
- return lastRenderedTriangle ;
145
- }
146
-
147
153
function step ( nextCounter , ...configs ) {
154
+ const app = reset ( ) ;
155
+
148
156
const currentCounter = app . setCounter ( nextCounter ) ;
149
157
150
158
const onKeyframes = new Map ( ) ;
@@ -156,25 +164,28 @@ describe('ReactConcurrency', () => {
156
164
throw new Error ( 'targetIndex should be the index of a leaf triangle' ) ;
157
165
}
158
166
159
- const onTriangles = onKeyframes . get ( targetIndex ) || new Set ( ) ;
160
- onTriangles . add ( targetTriangle ) ;
161
- onKeyframes . set ( onKeyframe , onTriangles ) ;
167
+ if ( onKeyframe >= 0 && onKeyframe < totalTriangles ) {
168
+ const onTriangles = onKeyframes . get ( targetIndex ) || new Set ( ) ;
169
+ onTriangles . add ( targetTriangle ) ;
170
+ onKeyframes . set ( onKeyframe , onTriangles ) ;
171
+ }
162
172
163
- const offTriangles = offKeyframes . get ( targetIndex ) || new Set ( ) ;
164
- offTriangles . add ( targetTriangle ) ;
165
- offKeyframes . set ( offKeyframe , offTriangles ) ;
173
+ if ( offKeyframe >= 0 && offKeyframe < totalTriangles ) {
174
+ const offTriangles = offKeyframes . get ( targetIndex ) || new Set ( ) ;
175
+ offTriangles . add ( targetTriangle ) ;
176
+ offKeyframes . set ( offKeyframe , offTriangles ) ;
177
+ }
166
178
}
167
179
168
180
const activeTriangles = new Set ( ) ;
169
181
182
+ yieldAfterEachRender = true ;
170
183
let i = 0 ;
171
- let renderedTriangle = renderNextTriangle ( ) ;
172
- while ( renderedTriangle !== null ) {
184
+ for ( var renderedTriangle of ReactNoop . flushAndYield ( ) ) {
173
185
if ( i ++ > 999 ) {
174
186
throw new Error ( 'Infinite loop' ) ;
175
187
}
176
188
treeIsConsistent ( currentCounter , activeTriangles ) ;
177
-
178
189
var onTriangles = onKeyframes . get ( renderedTriangle . index ) ;
179
190
if ( onTriangles ) {
180
191
onTriangles . forEach ( targetTriangle => {
@@ -184,6 +195,7 @@ describe('ReactConcurrency', () => {
184
195
activeTriangles . add ( targetTriangle ) ;
185
196
onTriangles . delete ( targetTriangle ) ;
186
197
} ) ;
198
+ onKeyframes . delete ( renderedTriangle . index ) ;
187
199
}
188
200
189
201
var offTriangles = offKeyframes . get ( renderedTriangle . index ) ;
@@ -195,19 +207,24 @@ describe('ReactConcurrency', () => {
195
207
activeTriangles . delete ( targetTriangle ) ;
196
208
offTriangles . delete ( targetTriangle ) ;
197
209
} ) ;
210
+ offKeyframes . delete ( renderedTriangle . index ) ;
198
211
}
199
212
200
213
app . interrupt ( ) ;
201
214
ReactNoop . flushAnimationPri ( ) ;
202
215
treeIsConsistent ( currentCounter , activeTriangles ) ;
216
+ }
217
+ yieldAfterEachRender = false ;
203
218
204
- renderedTriangle = renderNextTriangle ( ) ;
219
+ if ( onKeyframes . size !== 0 || offKeyframes . size !== 0 ) {
220
+ onKeyframes . forEach ( k => console . log ( k . size ) ) ;
221
+ throw new Error ( 'Some keyframes were not fired.' ) ;
205
222
}
206
223
207
224
treeIsConsistent ( nextCounter , activeTriangles ) ;
208
225
}
209
226
210
- return { step} ;
227
+ return { step, totalChildren , totalTriangles } ;
211
228
}
212
229
213
230
it ( 'works' , ( ) => {
@@ -224,12 +241,55 @@ describe('ReactConcurrency', () => {
224
241
// hi-pri update to "deactivate" that same node.
225
242
step ( 2 , [ 5 , 5 , 10 ] ) ;
226
243
step ( 3 , [ 22 , 20 , 22 ] ) ;
227
- step ( 4 , [ 13 , 10 , 30 ] ) ;
228
- step ( 5 , [ 7 , 35 , 38 ] ) ;
229
- step ( 6 , [ 17 , 8 , 14 ] ) ;
230
244
231
245
// Simulate multiple hover effects in the same step.
232
- step ( 7 , [ 3 , 4 , 21 ] , [ 17 , 8 , 14 ] , [ 19 , 7 , 12 ] ) ;
233
- step ( 8 , [ 3 , 4 , 39 ] , [ 17 , 8 , 14 ] , [ 19 , 7 , 12 ] ) ;
246
+ step ( 4 , [ 3 , 4 , 21 ] , [ 17 , 8 , 14 ] , [ 19 , 7 , 12 ] ) ;
247
+ step ( 5 , [ 3 , 4 , 39 ] , [ 17 , 8 , 14 ] , [ 19 , 7 , 12 ] ) ;
248
+
249
+ // The following tests are test cases that at one point or another failed.
250
+ // Don't touch them (unless there's a good reason).
251
+ step ( 6 , [ 23 , 11 , 6 ] ) ;
252
+ step ( 7 , [ 7 , 4 , 3 ] , [ 4 , 14 , 6 ] ) ;
253
+ } ) ;
254
+
255
+ it ( 'fuzz tester' , ( ) => {
256
+ // This test is not deterministic because the inputs are randomized. It runs
257
+ // a limited number of tests on every run. If it fails, it will output the
258
+ // inputs that led to the failure. Add the failing case to the test above
259
+ // to prevent future regressions.
260
+ const limit = 100 ;
261
+
262
+ const { step, totalChildren, totalTriangles} = TriangleTester ( ) ;
263
+
264
+ function randomInteger ( min , max ) {
265
+ min = Math . ceil ( min ) ;
266
+ max = Math . floor ( max ) ;
267
+ return Math . floor ( Math . random ( ) * ( max - min ) ) + min ;
268
+ }
269
+
270
+ function randomConfig ( ) {
271
+ const targetIndex = randomInteger ( 0 , totalChildren ) ;
272
+ const onKeyframe = randomInteger ( 0 , totalTriangles ) ;
273
+ // Allow for end keyframe to happen after entire tree has flushed
274
+ const offKeyframe = randomInteger ( 0 , totalTriangles * 2 ) ;
275
+ return [ targetIndex , onKeyframe , offKeyframe ] ;
276
+ }
277
+
278
+ for ( let nextStep = 1 ; nextStep <= limit ; nextStep ++ ) {
279
+ const configs = [ randomConfig ( ) , randomConfig ( ) , randomConfig ( ) ] ;
280
+ try {
281
+ step ( nextStep , ...configs ) ;
282
+ } catch ( error ) {
283
+ console . error ( error ) ;
284
+ const configStr = configs
285
+ . map ( config => `[${ config . join ( ', ' ) } ]` )
286
+ . join ( ', ' ) ;
287
+ throw new Error (
288
+ `Triangle fuzz tester failure. Add this the ReactIncrementalTriangle-test suite and fix it:
289
+ step(n, ${ configStr } )
290
+ ` ,
291
+ ) ;
292
+ }
293
+ }
234
294
} ) ;
235
295
} ) ;
0 commit comments