[Web Animations] Expose Web Animations CSS integration as an experimental feature
[WebKit-https.git] / LayoutTests / imported / mozilla / css-transitions / test_animation-currenttime.html
1 <!doctype html><!-- webkit-test-runner [ enableWebAnimationsCSSIntegration=true ] -->
2 <html>
3   <head>
4     <meta charset=utf-8>
5     <title>Tests for the effect of setting a CSS transition's
6            Animation.currentTime</title>
7     <style>
8
9 .animated-div {
10   margin-left: 100px;
11   transition: margin-left 1000s linear 1000s;
12 }
13
14     </style>
15     <script src="../../../resources/testharness.js"></script>
16     <script src="../../../resources/testharnessreport.js"></script>
17     <script src="../resources/testcommon.js"></script>
18   </head>
19   <body>
20     <div id="log"></div>
21     <script type="text/javascript">
22
23 'use strict';
24
25 // TODO: Once the computedTiming property is implemented, add checks to the
26 // checker helpers to ensure that computedTiming's properties are updated as
27 // expected.
28 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
29
30
31 const ANIM_DELAY_MS = 1000000; // 1000s
32 const ANIM_DUR_MS = 1000000; // 1000s
33
34 /**
35  * These helpers get the value that the currentTime needs to be set to, to put
36  * an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
37  * the middle of various phases or points through the active duration.
38  */
39 function currentTimeForBeforePhase() {
40   return ANIM_DELAY_MS / 2;
41 }
42 function currentTimeForActivePhase() {
43   return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
44 }
45 function currentTimeForAfterPhase() {
46   return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
47 }
48 function currentTimeForStartOfActiveInterval() {
49   return ANIM_DELAY_MS;
50 }
51 function currentTimeForFiftyPercentThroughActiveInterval() {
52   return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
53 }
54 function currentTimeForEndOfActiveInterval() {
55   return ANIM_DELAY_MS + ANIM_DUR_MS;
56 }
57
58
59 // Expected computed 'margin-left' values at points during the active interval:
60 // When we assert_between_inclusive using these values we could in theory cause
61 // intermittent failure due to very long delays between paints, but since the
62 // active duration is 1000s long, a delay would need to be around 100s to cause
63 // that. If that's happening then there are likely other issues that should be
64 // fixed, so a failure to make us look into that seems like a good thing.
65 const INITIAL_POSITION = 100;
66 const TEN_PCT_POSITION = 110;
67 const FIFTY_PCT_POSITION = 150;
68 const END_POSITION = 200;
69
70
71 // The terms used for the naming of the following helper functions refer to
72 // terms used in the Web Animations specification for specific phases of an
73 // animation. The terms can be found here:
74 //
75 //   http://drafts.csswg.org/web-animations/#animation-effect-phases-and-states
76
77 // Called when currentTime is set to zero (the beginning of the start delay).
78 function checkStateOnSettingCurrentTimeToZero(animation)
79 {
80   // We don't test animation.currentTime since our caller just set it.
81
82   assert_equals(animation.playState, 'running',
83     'Animation.playState should be "running" at the start of ' +
84     'the start delay');
85
86   assert_equals(animation.effect.target.style.animationPlayState, 'running',
87     'Animation.effect.target.style.animationPlayState should be ' +
88     '"running" at the start of the start delay');
89
90   var div = animation.effect.target;
91   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
92   assert_equals(marginLeft, UNANIMATED_POSITION,
93                 'the computed value of margin-left should be unaffected ' +
94                 'at the beginning of the start delay');
95 }
96
97 // Called when the ready Promise's callbacks should happen
98 function checkStateOnReadyPromiseResolved(animation)
99 {
100   // the 0.0001 here is for rounding error
101   assert_less_than_equal(animation.currentTime,
102     animation.timeline.currentTime - animation.startTime + 0.0001,
103     'Animation.currentTime should be less than the local time ' +
104     'equivalent of the timeline\'s currentTime on the first paint tick ' +
105     'after animation creation');
106
107   assert_equals(animation.playState, 'running',
108     'Animation.playState should be "running" on the first paint ' +
109     'tick after animation creation');
110
111   var div = animation.effect.target;
112   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
113   assert_equals(marginLeft, INITIAL_POSITION,
114                 'the computed value of margin-left should be unaffected ' +
115                 'by an animation with a delay on ready Promise resolve');
116 }
117
118 // Called when currentTime is set to the time the active interval starts.
119 function checkStateAtActiveIntervalStartTime(animation)
120 {
121   // We don't test animation.currentTime since our caller just set it.
122
123   assert_equals(animation.playState, 'running',
124     'Animation.playState should be "running" at the start of ' +
125     'the active interval');
126
127   var div = animation.effect.target;
128   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
129   assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
130     'the computed value of margin-left should be close to the value at the ' +
131     'beginning of the animation');
132 }
133
134 function checkStateAtFiftyPctOfActiveInterval(animation)
135 {
136   // We don't test animation.currentTime since our caller just set it.
137
138   var div = animation.effect.target;
139   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
140   assert_equals(marginLeft, FIFTY_PCT_POSITION,
141     'the computed value of margin-left should be half way through the ' +
142     'animation at the midpoint of the active interval');
143 }
144
145 // Called when currentTime is set to the time the active interval ends.
146 function checkStateAtActiveIntervalEndTime(animation)
147 {
148   // We don't test animation.currentTime since our caller just set it.
149
150   assert_equals(animation.playState, 'finished',
151     'Animation.playState should be "finished" at the end of ' +
152     'the active interval');
153
154   var div = animation.effect.target;
155   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
156   assert_equals(marginLeft, END_POSITION,
157     'the computed value of margin-left should be the final transitioned-to ' +
158     'value at the end of the active duration');
159 }
160
161 test(function(t)
162 {
163   var div = addDiv(t, {'class': 'animated-div'});
164   flushComputedStyle(div);
165   div.style.marginLeft = '200px'; // initiate transition
166
167   var animation = div.getAnimations()[0];
168   assert_equals(animation.currentTime, 0, 'currentTime should be zero');
169 }, 'currentTime of a newly created transition is zero');
170
171
172 test(function(t)
173 {
174   var div = addDiv(t, {'class': 'animated-div'});
175   flushComputedStyle(div);
176   div.style.marginLeft = '200px'; // initiate transition
177
178   var animation = div.getAnimations()[0];
179
180   // So that animation is running instead of paused when we set currentTime:
181   animation.startTime = animation.timeline.currentTime;
182
183   animation.currentTime = 10;
184   assert_equals(animation.currentTime, 10,
185     'Check setting of currentTime actually works');
186 }, 'Sanity test to check round-tripping assigning to new animation\'s ' +
187    'currentTime');
188
189
190 async_test(function(t) {
191   var div = addDiv(t, {'class': 'animated-div'});
192   var eventWatcher = new EventWatcher(t, div, 'transitionend');
193
194   flushComputedStyle(div);
195   div.style.marginLeft = '200px'; // initiate transition
196
197   var animation = div.getAnimations()[0];
198
199   animation.ready.then(t.step_func(function() {
200     checkStateOnReadyPromiseResolved(animation);
201
202     animation.currentTime = currentTimeForStartOfActiveInterval();
203     checkStateAtActiveIntervalStartTime(animation);
204
205     animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
206     checkStateAtFiftyPctOfActiveInterval(animation);
207
208     animation.currentTime = currentTimeForEndOfActiveInterval();
209     return eventWatcher.wait_for('transitionend');
210   })).then(t.step_func(function() {
211     checkStateAtActiveIntervalEndTime(animation);
212   })).catch(t.step_func(function(reason) {
213     assert_unreached(reason);
214   })).then(function() {
215     t.done();
216   });
217 }, 'Skipping forward through transition');
218
219
220 test(function(t) {
221   var div = addDiv(t, {'class': 'animated-div'});
222   var eventWatcher = new EventWatcher(t, div, 'transitionend');
223
224   flushComputedStyle(div);
225   div.style.marginLeft = '200px'; // initiate transition
226
227   var animation = div.getAnimations()[0];
228
229   // Unlike in the case of CSS animations, we cannot skip to the end and skip
230   // backwards since when we reach the end the transition effect is removed and
231   // changes to the Animation object no longer affect the element. For
232   // this reason we only skip forwards as far as the 50% through point.
233
234   animation.ready.then(t.step_func(function() {
235     animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
236     checkStateAtFiftyPctOfActiveInterval(animation);
237
238     animation.currentTime = currentTimeForStartOfActiveInterval();
239
240     // Despite going backwards from being in the active interval to being
241     // before it, we now expect a 'transitionend' event because the transition
242     // should go from being active to inactive.
243     //
244     // Calling checkStateAtActiveIntervalStartTime will check computed style,
245     // causing computed style to be updated and the 'transitionend' event to
246     // be dispatched synchronously. We need to call wait_for first
247     // otherwise eventWatcher will assert that the event was unexpected.
248     eventWatcher.wait_for('transitionend').then(function() {
249       t.done();
250     });
251     checkStateAtActiveIntervalStartTime(animation);
252   }));
253 }, 'Skipping backwards through transition');
254
255
256 async_test(function(t) {
257   var div = addDiv(t, {'class': 'animated-div'});
258   flushComputedStyle(div);
259   div.style.marginLeft = '200px'; // initiate transition
260
261   var animation = div.getAnimations()[0];
262
263   animation.ready.then(t.step_func(function() {
264     var exception;
265     try {
266       animation.currentTime = null;
267     } catch (e) {
268       exception = e;
269     }
270     assert_equals(exception.name, 'TypeError',
271       'Expect TypeError exception on trying to set ' +
272       'Animation.currentTime to null');
273   })).catch(t.step_func(function(reason) {
274     assert_unreached(reason);
275   })).then(function() {
276     t.done();
277   });
278 }, 'Setting currentTime to null');
279
280
281 async_test(function(t) {
282   var div = addDiv(t, {'class': 'animated-div'});
283   flushComputedStyle(div);
284   div.style.marginLeft = '200px'; // initiate transition
285
286   var animation = div.getAnimations()[0];
287   var pauseTime;
288
289   animation.ready.then(t.step_func(function() {
290     assert_not_equals(animation.currentTime, null,
291       'Animation.currentTime not null on ready Promise resolve');
292     animation.pause();
293     return animation.ready;
294   })).then(t.step_func(function() {
295     pauseTime = animation.currentTime;
296     return waitForFrame();
297   })).then(t.step_func(function() {
298     assert_equals(animation.currentTime, pauseTime,
299       'Animation.currentTime is unchanged after pausing');
300   })).catch(t.step_func(function(reason) {
301     assert_unreached(reason);
302   })).then(function() {
303     t.done();
304   });
305 }, 'Animation.currentTime after pausing');
306
307     </script>
308   </body>
309 </html>