@@ -9,49 +9,55 @@ var delay = require('../assets/delay');
9
9
10
10
var mock = require ( '@mocks/animation' ) ;
11
11
12
- function runTests ( duration ) {
13
- describe ( 'Test animate API (frame duration = ' + duration + ')' , function ( ) {
14
- 'use strict' ;
12
+ describe ( 'Test animate API' , function ( ) {
13
+ 'use strict' ;
15
14
16
- var gd , mockCopy ;
17
- var transOpts = { frameduration : duration } ;
15
+ var gd , mockCopy ;
18
16
19
- function verifyQueueEmpty ( gd ) {
20
- expect ( gd . _transitionData . _frameQueue . length ) . toEqual ( 0 ) ;
21
- }
17
+ function verifyQueueEmpty ( gd ) {
18
+ expect ( gd . _transitionData . _frameQueue . length ) . toEqual ( 0 ) ;
19
+ }
22
20
23
- function verifyFrameTransitionOrder ( gd , expectedFrames ) {
24
- var calls = PlotlyInternal . transition . calls ;
21
+ function verifyFrameTransitionOrder ( gd , expectedFrames ) {
22
+ var calls = PlotlyInternal . transition . calls ;
25
23
26
- expect ( calls . count ( ) ) . toEqual ( expectedFrames . length ) ;
24
+ expect ( calls . count ( ) ) . toEqual ( expectedFrames . length ) ;
27
25
28
- for ( var i = 0 ; i < calls . count ( ) ; i ++ ) {
29
- expect ( calls . argsFor ( i ) [ 1 ] ) . toEqual (
30
- gd . _transitionData . _frameHash [ expectedFrames [ i ] ] . data
31
- ) ;
32
- }
26
+ for ( var i = 0 ; i < calls . count ( ) ; i ++ ) {
27
+ expect ( calls . argsFor ( i ) [ 1 ] ) . toEqual (
28
+ gd . _transitionData . _frameHash [ expectedFrames [ i ] ] . data
29
+ ) ;
33
30
}
31
+ }
34
32
35
- beforeEach ( function ( done ) {
36
- gd = createGraphDiv ( ) ;
33
+ beforeEach ( function ( done ) {
34
+ gd = createGraphDiv ( ) ;
37
35
38
- mockCopy = Lib . extendDeep ( { } , mock ) ;
39
-
40
- spyOn ( PlotlyInternal , 'transition' ) . and . callFake ( function ( ) {
41
- // Transition's fake behaviro is to resolve after a short period of time:
42
- return Promise . resolve ( ) . then ( delay ( duration ) ) ;
43
- } ) ;
36
+ mockCopy = Lib . extendDeep ( { } , mock ) ;
44
37
45
- Plotly . plot ( gd , mockCopy . data , mockCopy . layout ) . then ( function ( ) {
46
- Plotly . addFrames ( gd , mockCopy . frames ) ;
47
- } ) . then ( done ) ;
38
+ spyOn ( PlotlyInternal , 'transition' ) . and . callFake ( function ( ) {
39
+ // Transition's fake behaviro is to resolve after a short period of time:
40
+ return Promise . resolve ( ) . then ( delay ( 5 ) ) ;
48
41
} ) ;
49
42
50
- afterEach ( function ( ) {
51
- // *must* purge between tests otherwise dangling async events might not get cleaned up properly:
52
- Plotly . purge ( gd ) ;
53
- destroyGraphDiv ( ) ;
54
- } ) ;
43
+ Plotly . plot ( gd , mockCopy . data , mockCopy . layout ) . then ( function ( ) {
44
+ Plotly . addFrames ( gd , mockCopy . frames ) ;
45
+ } ) . then ( done ) ;
46
+ } ) ;
47
+
48
+ afterEach ( function ( ) {
49
+ // *must* purge between tests otherwise dangling async events might not get cleaned up properly:
50
+ Plotly . purge ( gd ) ;
51
+ destroyGraphDiv ( ) ;
52
+ } ) ;
53
+
54
+ for ( var i = 0 ; i < 2 ; i ++ ) {
55
+ // Run tests for 0ms and 30ms duration:
56
+ runTests ( 10 + 30 * i ) ;
57
+ }
58
+
59
+ function runTests ( duration ) {
60
+ var transOpts = { frameduration : duration } ;
55
61
56
62
it ( 'animates to a frame' , function ( done ) {
57
63
Plotly . animate ( gd , [ 'frame0' ] , { duration : 1.2345 } ) . then ( function ( ) {
@@ -83,54 +89,51 @@ function runTests(duration) {
83
89
Plotly . animate ( gd , [ 'foobar' ] , transOpts ) . then ( fail ) . then ( done , done ) ;
84
90
} ) ;
85
91
86
- it ( 'animates to a single frame ' , function ( done ) {
87
- Plotly . animate ( gd , [ 'frame0' ] , transOpts ) . then ( function ( ) {
88
- expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 1 ) ;
92
+ it ( 'animates all frames if list is null ' , function ( done ) {
93
+ Plotly . animate ( gd , null , transOpts ) . then ( function ( ) {
94
+ verifyFrameTransitionOrder ( gd , [ 'base' , 'frame0' , 'frame1' , 'frame2' , 'frame3' ] ) ;
89
95
verifyQueueEmpty ( gd ) ;
90
96
} ) . catch ( fail ) . then ( done ) ;
91
97
} ) ;
92
98
93
- it ( 'animates to a list of frames ' , function ( done ) {
94
- Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , transOpts ) . then ( function ( ) {
95
- expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 2 ) ;
99
+ it ( 'animates all frames if list is undefined ' , function ( done ) {
100
+ Plotly . animate ( gd , undefined , transOpts ) . then ( function ( ) {
101
+ verifyFrameTransitionOrder ( gd , [ 'base' , 'frame0' , 'frame1' , 'frame2' , 'frame3' ] ) ;
96
102
verifyQueueEmpty ( gd ) ;
97
103
} ) . catch ( fail ) . then ( done ) ;
98
104
} ) ;
99
105
100
- it ( 'animates frames by group ' , function ( done ) {
101
- Plotly . animate ( gd , 'even-frames' , transOpts ) . then ( function ( ) {
102
- expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 2 ) ;
106
+ it ( 'animates to a single frame ' , function ( done ) {
107
+ Plotly . animate ( gd , [ 'frame0' ] , transOpts ) . then ( function ( ) {
108
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 1 ) ;
103
109
verifyQueueEmpty ( gd ) ;
104
110
} ) . catch ( fail ) . then ( done ) ;
105
111
} ) ;
106
112
107
- it ( 'animates groups in the correct order' , function ( done ) {
108
- Plotly . animate ( gd , 'even-frames' , transOpts ) ;
109
- Plotly . animate ( gd , 'odd-frames' , transOpts ) . then ( function ( ) {
110
- verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
113
+ it ( 'animates to an empty list' , function ( done ) {
114
+ Plotly . animate ( gd , [ ] , transOpts ) . then ( function ( ) {
115
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 0 ) ;
111
116
verifyQueueEmpty ( gd ) ;
112
117
} ) . catch ( fail ) . then ( done ) ;
113
118
} ) ;
114
119
115
- it ( 'drops queued frames when immediate = true' , function ( done ) {
116
- Plotly . animate ( gd , 'even-frames' , transOpts ) ;
117
- Plotly . animate ( gd , 'odd-frames' , transOpts , { immediate : true } ) . then ( function ( ) {
118
- verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame1' , 'frame3' ] ) ;
120
+ it ( 'animates to a list of frames' , function ( done ) {
121
+ Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , transOpts ) . then ( function ( ) {
122
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 2 ) ;
119
123
verifyQueueEmpty ( gd ) ;
120
124
} ) . catch ( fail ) . then ( done ) ;
121
125
} ) ;
122
126
123
- it ( 'animates frames in the correct order ' , function ( done ) {
124
- Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , transOpts ) . then ( function ( ) {
125
- verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
127
+ it ( 'animates frames by group ' , function ( done ) {
128
+ Plotly . animate ( gd , 'even-frames' , transOpts ) . then ( function ( ) {
129
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 2 ) ;
126
130
verifyQueueEmpty ( gd ) ;
127
131
} ) . catch ( fail ) . then ( done ) ;
128
132
} ) ;
129
133
130
- it ( 'animates frames and groups in sequence' , function ( done ) {
131
- Plotly . animate ( gd , 'even-frames' , transOpts ) ;
134
+ it ( 'animates frames in the correct order' , function ( done ) {
132
135
Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , transOpts ) . then ( function ( ) {
133
- verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame0' , 'frame2' , ' frame1', 'frame3' ] ) ;
136
+ verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
134
137
verifyQueueEmpty ( gd ) ;
135
138
} ) . catch ( fail ) . then ( done ) ;
136
139
} ) ;
@@ -204,18 +207,23 @@ function runTests(duration) {
204
207
} ) . catch ( fail ) . then ( done ) ;
205
208
} ) ;
206
209
207
- it ( 'rejects when an animation is interrupted' , function ( done ) {
208
- var interrupted = false ;
209
- Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , transOpts ) . then ( fail , function ( ) {
210
- interrupted = true ;
211
- } ) ;
212
-
213
- Plotly . animate ( gd , [ 'frame2' ] , transOpts , { immediate : true } ) . then ( function ( ) {
214
- expect ( interrupted ) . toBe ( true ) ;
215
- verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' ] ) ;
210
+ it ( 'resolves at the end of each animation sequence' , function ( done ) {
211
+ Plotly . animate ( gd , 'even-frames' , transOpts ) . then ( function ( ) {
212
+ return Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , transOpts ) ;
213
+ } ) . then ( function ( ) {
214
+ verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
216
215
verifyQueueEmpty ( gd ) ;
217
216
} ) . catch ( fail ) . then ( done ) ;
218
217
} ) ;
218
+ }
219
+
220
+ // The tests above use promises to ensure ordering, but the tests below this call Plotly.animate
221
+ // without chaining promises which would result in race conditions. This is not invalid behavior,
222
+ // but it doesn't ensure proper ordering and completion, so these must be performed with finite
223
+ // duration. Stricly speaking, these tests *do* involve race conditions, but the finite duration
224
+ // prevents that from causing problems.
225
+ describe ( 'Calling Plotly.animate synchronously in series' , function ( ) {
226
+ var transOpts = { frameduration : 30 } ;
219
227
220
228
it ( 'emits plotly_animationinterrupted when an animation is interrupted' , function ( done ) {
221
229
var interrupted = false ;
@@ -231,6 +239,7 @@ function runTests(duration) {
231
239
} ) . catch ( fail ) . then ( done ) ;
232
240
} ) ;
233
241
242
+
234
243
it ( 'queues successive animations' , function ( done ) {
235
244
var starts = 0 ;
236
245
var ends = 0 ;
@@ -243,27 +252,75 @@ function runTests(duration) {
243
252
expect ( starts ) . toEqual ( 1 ) ;
244
253
} ) ;
245
254
246
- Plotly . animate ( gd , 'even-frames' , transOpts ) ;
247
- Plotly . animate ( gd , 'odd-frames' , transOpts ) . then ( delay ( 10 ) ) . then ( function ( ) {
255
+ Plotly . animate ( gd , 'even-frames' , { duration : 16 } ) ;
256
+ Plotly . animate ( gd , 'odd-frames' , { duration : 16 } ) . then ( delay ( 10 ) ) . then ( function ( ) {
248
257
expect ( ends ) . toEqual ( 1 ) ;
249
258
verifyQueueEmpty ( gd ) ;
250
259
} ) . catch ( fail ) . then ( done ) ;
251
260
} ) ;
252
261
253
- it ( 'resolves at the end of each animation sequence' , function ( done ) {
254
- Plotly . animate ( gd , 'even-frames' , transOpts ) . then ( function ( ) {
255
- return Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , transOpts ) ;
256
- } ) . then ( function ( ) {
262
+ it ( 'an empty list with immediate dumps previous frames' , function ( done ) {
263
+ Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , { frameduration : 50 } ) ;
264
+ Plotly . animate ( gd , [ ] , null , { immediate : true } ) . then ( function ( ) {
265
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 1 ) ;
266
+ verifyQueueEmpty ( gd ) ;
267
+ } ) . catch ( fail ) . then ( done ) ;
268
+ } ) ;
269
+
270
+ it ( 'animates groups in the correct order' , function ( done ) {
271
+ Plotly . animate ( gd , 'even-frames' , transOpts ) ;
272
+ Plotly . animate ( gd , 'odd-frames' , transOpts ) . then ( function ( ) {
273
+ verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
274
+ verifyQueueEmpty ( gd ) ;
275
+ } ) . catch ( fail ) . then ( done ) ;
276
+ } ) ;
277
+
278
+ it ( 'drops queued frames when immediate = true' , function ( done ) {
279
+ Plotly . animate ( gd , 'even-frames' , transOpts ) ;
280
+ Plotly . animate ( gd , 'odd-frames' , transOpts , { immediate : true } ) . then ( function ( ) {
281
+ verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame1' , 'frame3' ] ) ;
282
+ verifyQueueEmpty ( gd ) ;
283
+ } ) . catch ( fail ) . then ( done ) ;
284
+ } ) ;
285
+
286
+ it ( 'animates frames and groups in sequence' , function ( done ) {
287
+ Plotly . animate ( gd , 'even-frames' , transOpts ) ;
288
+ Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , transOpts ) . then ( function ( ) {
257
289
verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' , 'frame0' , 'frame2' , 'frame1' , 'frame3' ] ) ;
258
290
verifyQueueEmpty ( gd ) ;
259
291
} ) . catch ( fail ) . then ( done ) ;
260
292
} ) ;
293
+
294
+ it ( 'rejects when an animation is interrupted' , function ( done ) {
295
+ var interrupted = false ;
296
+ Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , transOpts ) . then ( fail , function ( ) {
297
+ interrupted = true ;
298
+ } ) ;
299
+
300
+ Plotly . animate ( gd , [ 'frame2' ] , transOpts , { immediate : true } ) . then ( function ( ) {
301
+ expect ( interrupted ) . toBe ( true ) ;
302
+ verifyFrameTransitionOrder ( gd , [ 'frame0' , 'frame2' ] ) ;
303
+ verifyQueueEmpty ( gd ) ;
304
+ } ) . catch ( fail ) . then ( done ) ;
305
+ } ) ;
261
306
} ) ;
262
- }
263
307
264
- for ( var i = 0 ; i < 2 ; i ++ ) {
265
- // Set a duration:
266
- var d = 30 * i ;
308
+ it ( 'animates reasonably even when transition duration >> frame duration' , function ( done ) {
309
+ var starts = 0 ;
310
+ var ends = 0 ;
311
+
312
+ gd . on ( 'plotly_animating' , function ( ) {
313
+ starts ++ ;
314
+ } ) . on ( 'plotly_animated' , function ( ) {
315
+ ends ++ ;
316
+ } ) ;
317
+
318
+ Plotly . animate ( gd , [ 'frame0' , 'frame1' ] , { duration : 200 , frameduration : 20 } ) . then ( function ( ) {
319
+ expect ( starts ) . toEqual ( 1 ) ;
320
+ expect ( ends ) . toEqual ( 1 ) ;
321
+ expect ( PlotlyInternal . transition . calls . count ( ) ) . toEqual ( 2 ) ;
322
+ verifyQueueEmpty ( gd ) ;
323
+ } ) . catch ( fail ) . then ( done ) ;
324
+ } ) ;
267
325
268
- runTests ( d ) ;
269
- }
326
+ } ) ;
0 commit comments