fae6249ea262e7e007eaff5bebfe6a0b75f66752
[WebKit-https.git] / LayoutTests / imported / mozilla / css-transitions / test_animation-starttime.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.startTime</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 startTime needs to be set to, to put an
36  * animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
37  * middle of various phases or points through the active duration.
38  */
39 function startTimeForBeforePhase(timeline) {
40   return timeline.currentTime - ANIM_DELAY_MS / 2;
41 }
42 function startTimeForActivePhase(timeline) {
43   return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
44 }
45 function startTimeForAfterPhase(timeline) {
46   return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
47 }
48 function startTimeForStartOfActiveInterval(timeline) {
49   return timeline.currentTime - ANIM_DELAY_MS;
50 }
51 function startTimeForFiftyPercentThroughActiveInterval(timeline) {
52   return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
53 }
54 function startTimeForEndOfActiveInterval(timeline) {
55   return timeline.currentTime - 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 // The terms used for the naming of the following helper functions refer to
71 // terms used in the Web Animations specification for specific phases of an
72 // animation. The terms can be found here:
73 //
74 //   https://drafts.csswg.org/web-animations/#animation-effect-phases-and-states
75 //
76 // Note the distinction between the "animation start time" which occurs before
77 // the start delay and the start of the active interval which occurs after it.
78
79 // Called when the ready Promise's callbacks should happen
80 function checkStateOnReadyPromiseResolved(animation)
81 {
82   assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
83     'Animation.startTime should be less than the timeline\'s ' +
84     'currentTime on the first paint tick after animation creation');
85
86   assert_equals(animation.playState, 'running',
87     'Animation.playState should be "running" on the first paint ' +
88     'tick after animation creation');
89
90   var div = animation.effect.target;
91   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
92   assert_equals(marginLeft, INITIAL_POSITION,
93                 'the computed value of margin-left should be unaffected ' +
94                 'by an animation with a delay on ready Promise resolve');
95 }
96
97 // Called when startTime is set to the time the active interval starts.
98 function checkStateAtActiveIntervalStartTime(animation)
99 {
100   // We don't test animation.startTime since our caller just set it.
101
102   assert_equals(animation.playState, 'running',
103     'Animation.playState should be "running" at the start of ' +
104     'the active interval');
105
106   var div = animation.effect.target;
107   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
108   assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
109     'the computed value of margin-left should be close to the value at the ' +
110     'beginning of the animation');
111 }
112
113 function checkStateAtFiftyPctOfActiveInterval(animation)
114 {
115   // We don't test animation.startTime since our caller just set it.
116
117   var div = animation.effect.target;
118   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
119   assert_equals(marginLeft, FIFTY_PCT_POSITION,
120     'the computed value of margin-left should be half way through the ' +
121     'animation at the midpoint of the active interval');
122 }
123
124 // Called when startTime is set to the time the active interval ends.
125 function checkStateAtActiveIntervalEndTime(animation)
126 {
127   // We don't test animation.startTime since our caller just set it.
128
129   assert_equals(animation.playState, 'finished',
130     'Animation.playState should be "finished" at the end of ' +
131     'the active interval');
132
133   var div = animation.effect.target;
134   var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
135   assert_equals(marginLeft, END_POSITION,
136     'the computed value of margin-left should be the final transitioned-to ' +
137     'value at the end of the active duration');
138 }
139
140 test(function(t)
141 {
142   var div = addDiv(t, {'class': 'animated-div'});
143   flushComputedStyle(div);
144   div.style.marginLeft = '200px'; // initiate transition
145
146   var animation = div.getAnimations()[0];
147   assert_equals(animation.startTime, null, 'startTime is unresolved');
148 }, 'startTime of a newly created transition is unresolved');
149
150
151 test(function(t)
152 {
153   var div = addDiv(t, {'class': 'animated-div'});
154   flushComputedStyle(div);
155   div.style.marginLeft = '200px'; // initiate transition
156
157   var animation = div.getAnimations()[0];
158   var currentTime = animation.timeline.currentTime;
159   animation.startTime = currentTime;
160   assert_times_equal(animation.startTime, currentTime,
161     'Check setting of startTime actually works');
162 }, 'Sanity test to check round-tripping assigning to new animation\'s ' +
163    'startTime');
164
165
166 async_test(function(t) {
167   var div = addDiv(t, {'class': 'animated-div'});
168   var eventWatcher = new EventWatcher(t, div, 'transitionend');
169
170   flushComputedStyle(div);
171   div.style.marginLeft = '200px'; // initiate transition
172
173   var animation = div.getAnimations()[0];
174
175   animation.ready.then(t.step_func(function() {
176     checkStateOnReadyPromiseResolved(animation);
177
178     animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
179     checkStateAtActiveIntervalStartTime(animation);
180
181     animation.startTime =
182       startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
183     checkStateAtFiftyPctOfActiveInterval(animation);
184
185     animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
186     return eventWatcher.wait_for('transitionend');
187   })).then(t.step_func(function() {
188     checkStateAtActiveIntervalEndTime(animation);
189   })).catch(t.step_func(function(reason) {
190     assert_unreached(reason);
191   })).then(function() {
192     t.done();
193   });
194 }, 'Skipping forward through animation');
195
196
197 test(function(t) {
198   var div = addDiv(t, {'class': 'animated-div'});
199   var eventWatcher = new EventWatcher(t, div, 'transitionend');
200
201   flushComputedStyle(div);
202   div.style.marginLeft = '200px'; // initiate transition
203
204   var animation = div.getAnimations()[0];
205
206   // Unlike in the case of CSS animations, we cannot skip to the end and skip
207   // backwards since when we reach the end the transition effect is removed and
208   // changes to the Animation object no longer affect the element. For
209   // this reason we only skip forwards as far as the 90% through point.
210
211   animation.startTime =
212     startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
213   checkStateAtFiftyPctOfActiveInterval(animation);
214
215   animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
216
217   // Despite going backwards from being in the active interval to being before
218   // it, we now expect an 'animationend' event because the animation should go
219   // from being active to inactive.
220   //
221   // Calling checkStateAtActiveIntervalStartTime will check computed style,
222   // causing computed style to be updated and the 'transitionend' event to
223   // be dispatched synchronously. We need to call waitForEvent first
224   // otherwise eventWatcher will assert that the event was unexpected.
225   eventWatcher.wait_for('transitionend').then(function() {
226     t.done();
227   });
228   checkStateAtActiveIntervalStartTime(animation);
229 }, 'Skipping backwards through transition');
230
231
232 async_test(function(t) {
233   var div = addDiv(t, {'class': 'animated-div'});
234
235   flushComputedStyle(div);
236   div.style.marginLeft = '200px'; // initiate transition
237
238   var animation = div.getAnimations()[0];
239
240   var storedCurrentTime;
241
242   animation.ready.then(t.step_func(function() {
243     storedCurrentTime = animation.currentTime;
244     animation.startTime = null;
245     return animation.ready;
246   })).catch(t.step_func(function(reason) {
247     assert_unreached(reason);
248   })).then(t.step_func(function() {
249     assert_equals(animation.currentTime, storedCurrentTime,
250       'Test that hold time is correct');
251     t.done();
252   }));
253 }, 'Setting startTime to null');
254
255
256 async_test(function(t) {
257   var div = addDiv(t, {'class': 'animated-div'});
258
259   flushComputedStyle(div);
260   div.style.marginLeft = '200px'; // initiate transition
261
262   var animation = div.getAnimations()[0];
263
264   animation.ready.then(t.step_func(function() {
265     var savedStartTime = animation.startTime;
266
267     assert_not_equals(animation.startTime, null,
268       'Animation.startTime not null on ready Promise resolve');
269
270     animation.pause();
271     return animation.ready;
272   })).then(t.step_func(function() {
273     assert_equals(animation.startTime, null,
274       'Animation.startTime is null after paused');
275     assert_equals(animation.playState, 'paused',
276       'Animation.playState is "paused" after pause() call');
277   })).catch(t.step_func(function(reason) {
278     assert_unreached(reason);
279   })).then(function() {
280     t.done();
281   });
282 }, 'Animation.startTime after paused');
283
284     </script>
285   </body>
286 </html>