883f74dd66f3b8a180561109638d53fe856d9f03
[WebKit-https.git] / LayoutTests / imported / mozilla / css-animations / test_animation-starttime.html
1 <!doctype html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
2 <html>
3   <head>
4     <meta charset=utf-8>
5     <title>Tests for the effect of setting a CSS animation's
6            Animation.startTime</title>
7     <style>
8
9 .animated-div {
10   margin-left: 10px;
11   /* Make it easier to calculate expected values: */
12   animation-timing-function: linear ! important;
13 }
14
15 @keyframes anim {
16   from { margin-left: 100px; }
17   to { margin-left: 200px; }
18 }
19
20     </style>
21     <script src="../../../resources/testharness.js"></script>
22     <script src="../../../resources/testharnessreport.js"></script>
23     <script src="../resources/testcommon.js"></script>
24   </head>
25   <body>
26     <div id="log"></div>
27     <script type="text/javascript">
28
29 'use strict';
30
31 // TODO: We should separate this test(Testing for CSS Animation events /
32 // Testing for start time of Web Animation).
33 // e.g:
34 //  CSS Animation events test:
35 //    - check the firing an event after setting an Animation.startTime
36 //  The start time of Web Animation test:
37 //    - check an start time value on several situation(init / processing..)
38 //    - Based on W3C Spec, check the behavior of setting current time.
39
40 // TODO: Once the computedTiming property is implemented, add checks to the
41 // checker helpers to ensure that computedTiming's properties are updated as
42 // expected.
43 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
44
45 const CSS_ANIM_EVENTS =
46   ['animationstart', 'animationiteration', 'animationend'];
47
48 test(function(t)
49 {
50   var div = addDiv(t, { 'style': 'animation: anim 100s' });
51   var animation = div.getAnimations()[0];
52
53   assert_equals(animation.startTime, null, 'startTime is unresolved');
54 }, 'startTime of a newly created (play-pending) animation is unresolved');
55
56 test(function(t)
57 {
58   var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
59   var animation = div.getAnimations()[0];
60   assert_equals(animation.startTime, null, 'startTime is unresolved');
61 }, 'startTime of a newly created (pause-pending) animation is unresolved');
62
63 promise_test(function(t)
64 {
65   var div = addDiv(t, { 'style': 'animation: anim 100s' });
66   var animation = div.getAnimations()[0];
67
68   return animation.ready.then(function() {
69     assert_true(animation.startTime > 0,
70                 'startTime is resolved when running');
71   });
72 }, 'startTime is resolved when running');
73
74 promise_test(function(t)
75 {
76   var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
77   var animation = div.getAnimations()[0];
78
79   return animation.ready.then(function() {
80     assert_equals(animation.startTime, null,
81                   'startTime is unresolved when paused');
82   });
83 }, 'startTime is unresolved when paused');
84
85 promise_test(function(t)
86 {
87   var div = addDiv(t, { 'style': 'animation: anim 100s' });
88   var animation = div.getAnimations()[0];
89
90   return animation.ready.then(function() {
91     div.style.animationPlayState = 'paused';
92     getComputedStyle(div).animationPlayState;
93
94     assert_not_equals(animation.startTime, null,
95                       'startTime is resolved when pause-pending');
96
97     div.style.animationPlayState = 'running';
98     getComputedStyle(div).animationPlayState;
99
100     assert_not_equals(animation.startTime, null,
101                       'startTime is preserved when a pause is aborted');
102   });
103 }, 'startTime while pause-pending and play-pending');
104
105 promise_test(function(t) {
106   var div = addDiv(t, { 'style': 'animation: anim 100s' });
107   var animation = div.getAnimations()[0];
108   // Seek to end to put us in the finished state
109   animation.currentTime = 100 * MS_PER_SEC;
110
111   return animation.ready.then(function() {
112     // Call play() which puts us back in the running state
113     animation.play();
114
115     assert_equals(animation.startTime, null, 'startTime is unresolved');
116   });
117 }, 'startTime while play-pending from finished state');
118
119 test(function(t) {
120   var div = addDiv(t, { 'style': 'animation: anim 100s' });
121   var animation = div.getAnimations()[0];
122   animation.finish();
123   // Call play() which puts us back in the running state
124   animation.play();
125
126   assert_equals(animation.startTime, null, 'startTime is unresolved');
127 }, 'startTime while play-pending from finished state using finish()');
128
129 promise_test(function(t) {
130   var div = addDiv(t, { style: 'animation: anim 100s' });
131   var animation = div.getAnimations()[0];
132
133   assert_equals(animation.startTime, null, 'The initial startTime is null');
134   var initialTimelineTime = document.timeline.currentTime;
135
136   return animation.ready.then(function() {
137     assert_true(animation.startTime > initialTimelineTime,
138                 'After the animation has started, startTime is greater than ' +
139                 'the time when it was started');
140     var startTimeBeforePausing = animation.startTime;
141
142     div.style.animationPlayState = 'paused';
143     // Flush styles just in case querying animation.startTime doesn't flush
144     // styles (which would be a bug in of itself and could mask a further bug
145     // by causing startTime to appear to not change).
146     getComputedStyle(div).animationPlayState;
147
148     assert_equals(animation.startTime, startTimeBeforePausing,
149                   'The startTime does not change when pausing-pending');
150     return animation.ready;
151   }).then(function() {
152     assert_equals(animation.startTime, null,
153                   'After actually pausing, the startTime of an animation ' +
154                   'is null');
155   });
156 }, 'Pausing should make the startTime become null');
157
158 test(function(t)
159 {
160   var div = addDiv(t, {'class': 'animated-div'});
161   div.style.animation = 'anim 100s 100s';
162   var animation = div.getAnimations()[0];
163   var currentTime = animation.timeline.currentTime;
164   animation.startTime = currentTime;
165
166   assert_times_equal(animation.startTime, currentTime,
167     'Check setting of startTime actually works');
168 }, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
169    'startTime');
170
171 promise_test(function(t) {
172   var div = addDiv(t, {'class': 'animated-div'});
173   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
174   div.style.animation = 'anim 100s 100s';
175   var animation = div.getAnimations()[0];
176
177   return animation.ready.then(function() {
178     assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
179       'Animation.startTime should be less than the timeline\'s ' +
180       'currentTime on the first paint tick after animation creation');
181
182     animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
183     return eventWatcher.wait_for('animationstart');
184   }).then(function() {
185     animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
186     return eventWatcher.wait_for('animationend');
187   });
188 }, 'Skipping forward through animation');
189
190 promise_test(function(t) {
191   var div = addDiv(t, {'class': 'animated-div'});
192   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
193   div.style.animation = 'anim 100s 100s';
194   var animation = div.getAnimations()[0];
195   animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
196   var previousTimelineTime = animation.timeline.currentTime;
197
198   return eventWatcher.wait_for(['animationstart',
199                                 'animationend']).then(function() {
200     assert_true(document.timeline.currentTime - previousTimelineTime <
201                   100 * MS_PER_SEC,
202                 'Sanity check that seeking worked rather than the events ' +
203                 'firing after normal playback through the very long ' +
204                 'animation duration');
205
206     animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
207
208     // Despite going backwards from after the end of the animation (to being
209     // in the active interval), we now expect an 'animationstart' event
210     // because the animation should go from being inactive to active.
211     return eventWatcher.wait_for('animationstart');
212   }).then(function() {
213     animation.startTime = animation.timeline.currentTime;
214
215     // Despite going backwards from just after the active interval starts to
216     // the animation start time, we now expect an animationend event
217     // because we went from inside to outside the active interval.
218     return eventWatcher.wait_for('animationend');
219   }).then(function() {
220     assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
221       'Animation.startTime should be less than the timeline\'s ' +
222       'currentTime on the first paint tick after animation creation');
223   });
224 }, 'Skipping backwards through animation');
225
226 // Next we have multiple tests to check that redundant startTime changes do NOT
227 // dispatch events. It's impossible to distinguish between events not being
228 // dispatched and events just taking an incredibly long time to dispatch
229 // without waiting an infinitely long time. Obviously we don't want to do that
230 // (block this test from finishing forever), so instead we just listen for
231 // events until two animation frames (i.e. requestAnimationFrame callbacks)
232 // have happened, then assume that no events will ever be dispatched for the
233 // redundant changes if no events were detected in that time.
234
235 promise_test(function(t) {
236   var div = addDiv(t, {'class': 'animated-div'});
237   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
238   div.style.animation = "anim 100s 100s";
239   var animation = div.getAnimations()[0];
240
241   animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
242   animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
243
244   return waitForAnimationFrames(2);
245 }, 'Redundant change, before -> active, then back');
246
247 promise_test(function(t) {
248   var div = addDiv(t, {'class': 'animated-div'});
249   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
250   div.style.animation = "anim 100s 100s";
251   var animation = div.getAnimations()[0];
252
253   animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
254   animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
255
256   return waitForAnimationFrames(2);
257 }, 'Redundant change, before -> after, then back');
258
259 promise_test(function(t) {
260   var div = addDiv(t, {'class': 'animated-div'});
261   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
262   div.style.animation = "anim 100s 100s";
263   var animation = div.getAnimations()[0];
264
265   var retPromise =  eventWatcher.wait_for('animationstart').then(function() {
266     animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
267     animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
268
269     return waitForAnimationFrames(2);
270   });
271   // get us into the initial state:
272   animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
273
274   return retPromise;
275 }, 'Redundant change, active -> before, then back');
276
277 promise_test(function(t) {
278   var div = addDiv(t, {'class': 'animated-div'});
279   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
280   div.style.animation = "anim 100s 100s";
281   var animation = div.getAnimations()[0];
282
283   var retPromise = eventWatcher.wait_for('animationstart').then(function() {
284     animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
285     animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
286
287     return waitForAnimationFrames(2);
288   });
289   // get us into the initial state:
290   animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
291
292   return retPromise;
293 }, 'Redundant change, active -> after, then back');
294
295 promise_test(function(t) {
296   var div = addDiv(t, {'class': 'animated-div'});
297   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
298   div.style.animation = "anim 100s 100s";
299   var animation = div.getAnimations()[0];
300
301   var retPromise = eventWatcher.wait_for(['animationstart',
302                                           'animationend']).then(function() {
303     animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
304     animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
305
306     return waitForAnimationFrames(2);
307   });
308   // get us into the initial state:
309   animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
310
311   return retPromise;
312 }, 'Redundant change, after -> before, then back');
313
314 promise_test(function(t) {
315   var div = addDiv(t, {'class': 'animated-div'});
316   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
317   div.style.animation = "anim 100s 100s";
318   var animation = div.getAnimations()[0];
319
320   var retPromise = eventWatcher.wait_for(['animationstart',
321                                           'animationend']).then(function() {
322     animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
323     animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
324
325     return waitForAnimationFrames(2);
326
327   });
328   // get us into the initial state:
329   animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
330
331   return retPromise;
332 }, 'Redundant change, after -> active, then back');
333
334 promise_test(function(t) {
335   var div = addDiv(t, {'class': 'animated-div'});
336   div.style.animation = 'anim 100s 100s';
337   var animation = div.getAnimations()[0];
338   var storedCurrentTime;
339
340   return animation.ready.then(function() {
341     storedCurrentTime = animation.currentTime;
342     animation.startTime = null;
343     return animation.ready;
344   }).then(function() {
345     assert_equals(animation.currentTime, storedCurrentTime,
346       'Test that hold time is correct');
347   });
348 }, 'Setting startTime to null');
349
350 promise_test(function(t) {
351   var div = addDiv(t, {'class': 'animated-div'});
352   div.style.animation = 'anim 100s';
353   var animation = div.getAnimations()[0];
354
355   return animation.ready.then(function() {
356     var savedStartTime = animation.startTime;
357
358     assert_not_equals(animation.startTime, null,
359       'Animation.startTime not null on ready Promise resolve');
360
361     animation.pause();
362     return animation.ready;
363   }).then(function() {
364     assert_equals(animation.startTime, null,
365       'Animation.startTime is null after paused');
366     assert_equals(animation.playState, 'paused',
367       'Animation.playState is "paused" after pause() call');
368   });
369 }, 'Animation.startTime after pausing');
370
371 promise_test(function(t) {
372   var div = addDiv(t, {'class': 'animated-div'});
373   div.style.animation = 'anim 100s';
374   var animation = div.getAnimations()[0];
375
376   return animation.ready.then(function() {
377     animation.cancel();
378     assert_equals(animation.startTime, null,
379                   'The startTime of a cancelled animation should be null');
380   });
381 }, 'Animation.startTime after cancelling');
382
383     </script>
384   </body>
385 </html>