Unreviewed, rolling out r251536.
[WebKit-https.git] / LayoutTests / imported / mozilla / css-animations / test_element-get-animations.html
1 <!doctype html>
2 <meta charset=utf-8>
3 <script src="../../../resources/testharness.js"></script>
4 <script src="../../../resources/testharnessreport.js"></script>
5 <script src="../resources/testcommon.js"></script>
6 <style>
7 @keyframes anim1 {
8   to { left: 100px }
9 }
10 @keyframes anim2 {
11   to { top: 100px }
12 }
13 @keyframes multiPropAnim {
14   to { background: green, opacity: 0.5, left: 100px, top: 100px }
15 }
16 ::before {
17   content: ''
18 }
19 ::after {
20   content: ''
21 }
22 @keyframes empty { }
23 </style>
24 <body>
25 <div id="log"></div>
26 <script>
27 'use strict';
28
29 test(function(t) {
30   var div = addDiv(t);
31   assert_equals(div.getAnimations().length, 0,
32     'getAnimations returns an empty sequence for an element'
33     + ' with no animations');
34 }, 'getAnimations for non-animated content');
35
36 promise_test(function(t) {
37   var div = addDiv(t);
38
39   // FIXME: This test does too many things. It should be split up.
40
41   // Add an animation
42   div.style.animation = 'anim1 100s';
43   var animations = div.getAnimations();
44   assert_equals(animations.length, 1,
45     'getAnimations returns an Animation running CSS Animations');
46   return animations[0].ready.then(function() {
47     var startTime = animations[0].startTime;
48     assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
49       'CSS animation has a sensible start time');
50
51     // Wait a moment then add a second animation.
52     //
53     // We wait for the next frame so that we can test that the start times of
54     // the animations differ.
55     return waitForFrame();
56   }).then(function() {
57     div.style.animation = 'anim1 100s, anim2 100s';
58     animations = div.getAnimations();
59     assert_equals(animations.length, 2,
60       'getAnimations returns one Animation for each value of'
61       + ' animation-name');
62     // Wait until both Animations are ready
63     // (We don't make any assumptions about the order of the Animations since
64     //  that is the purpose of the following test.)
65     return waitForAllAnimations(animations);
66   }).then(function() {
67     assert_true(animations[0].startTime < animations[1].startTime,
68       'Additional Animations for CSS animations start after the original'
69       + ' animation and appear later in the list');
70   });
71 }, 'getAnimations for CSS Animations');
72
73 test(function(t) {
74   var div = addDiv(t, { style: 'animation: anim1 100s' });
75   assert_class_string(div.getAnimations()[0], 'CSSAnimation',
76                       'Interface of returned animation is CSSAnimation');
77 }, 'getAnimations returns CSSAnimation objects for CSS Animations');
78
79 test(function(t) {
80   var div = addDiv(t);
81
82   // Add an animation that targets multiple properties
83   div.style.animation = 'multiPropAnim 100s';
84   assert_equals(div.getAnimations().length, 1,
85     'getAnimations returns only one Animation for a CSS Animation'
86     + ' that targets multiple properties');
87 }, 'getAnimations for multi-property animations');
88
89 promise_test(function(t) {
90   var div = addDiv(t);
91
92   // Add an animation
93   div.style.backgroundColor = 'red';
94   div.style.animation = 'anim1 100s';
95   getComputedStyle(div).backgroundColor;
96
97   // Wait until a frame after the animation starts, then add a transition
98   var animations = div.getAnimations();
99   return animations[0].ready.then(waitForFrame).then(function() {
100     div.style.transition = 'all 100s';
101     div.style.backgroundColor = 'green';
102
103     animations = div.getAnimations();
104     assert_equals(animations.length, 2,
105       'getAnimations returns Animations for both animations and'
106       + ' transitions that run simultaneously');
107     assert_class_string(animations[0], 'CSSTransition',
108                         'First-returned animation is the CSS Transition');
109     assert_class_string(animations[1], 'CSSAnimation',
110                         'Second-returned animation is the CSS Animation');
111   });
112 }, 'getAnimations for both CSS Animations and CSS Transitions at once');
113
114 async_test(function(t) {
115   var div = addDiv(t);
116
117   // Set up event listener
118   div.addEventListener('animationend', t.step_func(function() {
119     assert_equals(div.getAnimations().length, 0,
120       'getAnimations does not return Animations for finished '
121       + ' (and non-forwards-filling) CSS Animations');
122     t.done();
123   }));
124
125   // Add a very short animation
126   div.style.animation = 'anim1 0.01s';
127 }, 'getAnimations for CSS Animations that have finished');
128
129 async_test(function(t) {
130   var div = addDiv(t);
131
132   // Set up event listener
133   div.addEventListener('animationend', t.step_func(function() {
134     assert_equals(div.getAnimations().length, 1,
135       'getAnimations returns Animations for CSS Animations that have'
136       + ' finished but are filling forwards');
137     t.done();
138   }));
139
140   // Add a very short animation
141   div.style.animation = 'anim1 0.01s forwards';
142 }, 'getAnimations for CSS Animations that have finished but are'
143    + ' forwards filling');
144
145 test(function(t) {
146   var div = addDiv(t);
147   div.style.animation = 'none 100s';
148
149   var animations = div.getAnimations();
150   assert_equals(animations.length, 0,
151     'getAnimations returns an empty sequence for an element'
152     + ' with animation-name: none');
153
154   div.style.animation = 'none 100s, anim1 100s';
155   animations = div.getAnimations();
156   assert_equals(animations.length, 1,
157     'getAnimations returns Animations only for those CSS Animations whose'
158     + ' animation-name is not none');
159 }, 'getAnimations for CSS Animations with animation-name: none');
160
161 test(function(t) {
162   var div = addDiv(t);
163   div.style.animation = 'missing 100s';
164   var animations = div.getAnimations();
165   assert_equals(animations.length, 0,
166     'getAnimations returns an empty sequence for an element'
167     + ' with animation-name: missing');
168
169   div.style.animation = 'anim1 100s, missing 100s';
170   animations = div.getAnimations();
171   assert_equals(animations.length, 1,
172     'getAnimations returns Animations only for those CSS Animations whose'
173     + ' animation-name is found');
174 }, 'getAnimations for CSS Animations with animation-name: missing');
175
176 promise_test(function(t) {
177   var div = addDiv(t);
178   div.style.animation = 'anim1 100s, notyet 100s';
179   var animations = div.getAnimations();
180   assert_equals(animations.length, 1,
181     'getAnimations initally only returns Animations for CSS Animations whose'
182     + ' animation-name is found');
183
184   return animations[0].ready.then(waitForFrame).then(function() {
185     var keyframes = '@keyframes notyet { to { left: 100px; } }';
186     document.styleSheets[0].insertRule(keyframes, 0);
187     animations = div.getAnimations();
188     assert_equals(animations.length, 2,
189       'getAnimations includes Animation when @keyframes rule is added'
190       + ' later');
191     return waitForAllAnimations(animations);
192   }).then(function() {
193     assert_true(animations[0].startTime < animations[1].startTime,
194       'Newly added animation has a later start time');
195     document.styleSheets[0].deleteRule(0);
196   });
197 }, 'getAnimations for CSS Animations where the @keyframes rule is added'
198    + ' later');
199
200 test(function(t) {
201   var div = addDiv(t);
202   div.style.animation = 'anim1 100s, anim1 100s';
203   assert_equals(div.getAnimations().length, 2,
204     'getAnimations returns one Animation for each CSS animation-name'
205     + ' even if the names are duplicated');
206 }, 'getAnimations for CSS Animations with duplicated animation-name');
207
208 test(function(t) {
209   var div = addDiv(t);
210   div.style.animation = 'empty 100s';
211   assert_equals(div.getAnimations().length, 1,
212     'getAnimations returns Animations for CSS animations with an'
213     + ' empty keyframes rule');
214 }, 'getAnimations for CSS Animations with empty keyframes rule');
215
216 promise_test(function(t) {
217   var div = addDiv(t);
218   div.style.animation = 'anim1 100s 100s';
219   var animations = div.getAnimations();
220   assert_equals(animations.length, 1,
221     'getAnimations returns animations for CSS animations whose'
222     + ' delay makes them start later');
223   return animations[0].ready.then(waitForFrame).then(function() {
224     assert_true(animations[0].startTime <= document.timeline.currentTime,
225       'For CSS Animations in delay phase, the start time of the Animation is'
226       + ' not in the future');
227   });
228 }, 'getAnimations for CSS animations in delay phase');
229
230 test(function(t) {
231   var div = addDiv(t);
232   div.style.animation = 'anim1 0s 100s';
233   assert_equals(div.getAnimations().length, 1,
234     'getAnimations returns animations for CSS animations whose'
235     + ' duration is zero');
236   div.remove();
237 }, 'getAnimations for zero-duration CSS Animations');
238
239 test(function(t) {
240   var div = addDiv(t);
241   div.style.animation = 'anim1 100s';
242   var originalAnimation = div.getAnimations()[0];
243
244   // Update pause state (an Animation change)
245   div.style.animationPlayState = 'paused';
246   var pendingAnimation = div.getAnimations()[0];
247   assert_equals(pendingAnimation.playState, 'paused',
248                 'animation\'s play state is updated');
249   assert_equals(originalAnimation, pendingAnimation,
250                 'getAnimations returns the same objects even when their'
251                 + ' play state changes');
252
253   // Update duration (an Animation change)
254   div.style.animationDuration = '200s';
255   var extendedAnimation = div.getAnimations()[0];
256   // FIXME: Check extendedAnimation.effect.timing.duration has changed once the
257   // API is available
258   assert_equals(originalAnimation, extendedAnimation,
259                 'getAnimations returns the same objects even when their'
260                 + ' duration changes');
261 }, 'getAnimations returns objects with the same identity');
262
263 test(function(t) {
264   var div = addDiv(t);
265   div.style.animation = 'anim1 100s';
266
267   assert_equals(div.getAnimations().length, 1,
268     'getAnimations returns an animation before cancelling');
269
270   var animation = div.getAnimations()[0];
271
272   animation.cancel();
273   assert_equals(div.getAnimations().length, 0,
274     'getAnimations does not return cancelled animations');
275
276   animation.play();
277   assert_equals(div.getAnimations().length, 1,
278     'getAnimations returns cancelled animations that have been re-started');
279
280 }, 'getAnimations for CSS Animations that are cancelled');
281
282 promise_test(function(t) {
283   var div = addDiv(t);
284   div.style.animation = 'anim2 100s';
285
286   return div.getAnimations()[0].ready.then(function() {
287     // Prepend to the list and test that even though anim1 was triggered
288     // *after* anim2, it should come first because it appears first
289     // in the animation-name property.
290     div.style.animation = 'anim1 100s, anim2 100s';
291     var anims = div.getAnimations();
292     assert_equals(anims[0].animationName, 'anim1',
293                   'animation order after prepending to list');
294     assert_equals(anims[1].animationName, 'anim2',
295                   'animation order after prepending to list');
296
297     // Normally calling cancel and play would this push anim1 to the top of
298     // the stack but it shouldn't for CSS animations that map an the
299     // animation-name property.
300     var anim1 = anims[0];
301     anim1.cancel();
302     anim1.play();
303     anims = div.getAnimations();
304     assert_equals(anims[0].animationName, 'anim1',
305                   'animation order after cancelling and restarting');
306     assert_equals(anims[1].animationName, 'anim2',
307                   'animation order after cancelling and restarting');
308   });
309 }, 'getAnimations for CSS Animations follows animation-name order');
310
311 test(function(t) {
312   addStyle(t, { '#target::after': 'animation: anim1 10s;',
313                 '#target::before': 'animation: anim1 10s;' });
314   var target = addDiv(t, { 'id': 'target' });
315   target.style.animation = 'anim1 100s';
316
317   var animations = target.getAnimations({ subtree: false });
318   assert_equals(animations.length, 1,
319                 'Should find only the element');
320   assert_equals(animations[0].effect.target, target,
321                 'Effect target should be the element');
322 }, 'Test AnimationFilter{ subtree: false } with single element');
323
324 test(function(t) {
325   addStyle(t, { '#target::after': 'animation: anim1 10s;',
326                 '#target::before': 'animation: anim1 10s;' });
327   var target = addDiv(t, { 'id': 'target' });
328   target.style.animation = 'anim1 100s';
329
330   var animations = target.getAnimations({ subtree: true });
331   assert_equals(animations.length, 3,
332                 'getAnimations({ subtree: true }) ' +
333                 'should return animations on pseudo-elements');
334   assert_equals(animations[0].effect.target, target,
335                 'The animation targeting the parent element ' +
336                 'should be returned first');
337   assert_equals(animations[1].effect.target.type, '::before',
338                 'The animation targeting the ::before pseudo-element ' +
339                 'should be returned second');
340   assert_equals(animations[2].effect.target.type, '::after',
341                 'The animation targeting the ::after pesudo-element ' +
342                 'should be returned last');
343 }, 'Test AnimationFilter{ subtree: true } with single element');
344
345 test(function(t) {
346   addStyle(t, { '#parent::after': 'animation: anim1 10s;',
347                 '#parent::before': 'animation: anim1 10s;',
348                 '#child::after': 'animation: anim1 10s;',
349                 '#child::before': 'animation: anim1 10s;' });
350   var parent = addDiv(t, { 'id': 'parent' });
351   parent.style.animation = 'anim1 100s';
352   var child = addDiv(t, { 'id': 'child' });
353   child.style.animation = 'anim1 100s';
354   parent.appendChild(child);
355
356   var animations = parent.getAnimations({ subtree: false });
357   assert_equals(animations.length, 1,
358                 'Should find only the element even if it has a child');
359   assert_equals(animations[0].effect.target, parent,
360                 'Effect target shuld be the element');
361 }, 'Test AnimationFilter{ subtree: false } with element that has a child');
362
363 test(function(t) {
364   addStyle(t, { '#parent::after': 'animation: anim1 10s;',
365                 '#parent::before': 'animation: anim1 10s;',
366                 '#child::after': 'animation: anim1 10s;',
367                 '#child::before': 'animation: anim1 10s;' });
368   var parent = addDiv(t, { 'id': 'parent' });
369   var child = addDiv(t, { 'id': 'child' });
370   parent.style.animation = 'anim1 100s';
371   child.style.animation = 'anim1 100s';
372   parent.appendChild(child);
373
374   var animations = parent.getAnimations({ subtree: true });
375   assert_equals(animations.length, 6,
376                 'Should find all elements, pesudo-elements that parent has');
377
378   assert_equals(animations[0].effect.target, parent,
379                 'The animation targeting the parent element ' +
380                 'should be returned first');
381   assert_equals(animations[1].effect.target.type, '::before',
382                 'The animation targeting the ::before pseudo-element ' +
383                 'should be returned second');
384   assert_equals(animations[1].effect.target.parentElement, parent,
385                 'This ::before element should be child of parent element');
386   assert_equals(animations[2].effect.target.type, '::after',
387                 'The animation targeting the ::after pesudo-element ' +
388                 'should be returned third');
389   assert_equals(animations[2].effect.target.parentElement, parent,
390                 'This ::after element should be child of parent element');
391
392   assert_equals(animations[3].effect.target, child,
393                 'The animation targeting the child element ' +
394                 'should be returned fourth');
395   assert_equals(animations[4].effect.target.type, '::before',
396                 'The animation targeting the ::before pseudo-element ' +
397                 'should be returned fifth');
398   assert_equals(animations[4].effect.target.parentElement, child,
399                 'This ::before element should be child of child element');
400   assert_equals(animations[5].effect.target.type, '::after',
401                 'The animation targeting the ::after pesudo-element ' +
402                 'should be returned last');
403   assert_equals(animations[5].effect.target.parentElement, child,
404                 'This ::after element should be child of child element');
405 }, 'Test AnimationFilter{ subtree: true } with element that has a child');
406
407 test(function(t) {
408   var parent = addDiv(t, { 'id': 'parent' });
409   var child1 = addDiv(t, { 'id': 'child1' });
410   var grandchild1 = addDiv(t, { 'id': 'grandchild1' });
411   var grandchild2 = addDiv(t, { 'id': 'grandchild2' });
412   var child2 = addDiv(t, { 'id': 'child2' });
413
414   parent.style.animation = 'anim1 100s';
415   child1.style.animation = 'anim1 100s';
416   grandchild1.style.animation = 'anim1 100s';
417   grandchild2.style.animation = 'anim1 100s';
418   child2.style.animation = 'anim1 100s';
419
420   parent.appendChild(child1);
421   child1.appendChild(grandchild1);
422   child1.appendChild(grandchild2);
423   parent.appendChild(child2);
424
425   var animations = parent.getAnimations({ subtree: true });
426   assert_equals(
427     parent.getAnimations({ subtree: true }).length, 5,
428                          'Should find all descendants of the element');
429
430   assert_equals(animations[0].effect.target, parent,
431                 'The animation targeting the parent element ' +
432                 'should be returned first');
433
434   assert_equals(animations[1].effect.target, child1,
435                 'The animation targeting the child1 element ' +
436                 'should be returned second');
437
438   assert_equals(animations[2].effect.target, grandchild1,
439                 'The animation targeting the grandchild1 element ' +
440                 'should be returned third');
441
442   assert_equals(animations[3].effect.target, grandchild2,
443                 'The animation targeting the grandchild2 element ' +
444                 'should be returned fourth');
445
446   assert_equals(animations[4].effect.target, child2,
447                 'The animation targeting the child2 element ' +
448                 'should be returned last');
449
450 }, 'Test AnimationFilter{ subtree: true } with element that has many descendant');
451
452 </script>
453 </body>