diff options
Diffstat (limited to 'dom/animation/test/css-animations/file_element-get-animations.html')
-rw-r--r-- | dom/animation/test/css-animations/file_element-get-animations.html | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/dom/animation/test/css-animations/file_element-get-animations.html b/dom/animation/test/css-animations/file_element-get-animations.html new file mode 100644 index 000000000..68386c98b --- /dev/null +++ b/dom/animation/test/css-animations/file_element-get-animations.html @@ -0,0 +1,445 @@ +<!doctype html> +<meta charset=utf-8> +<script src="../testcommon.js"></script> +<style> +@keyframes anim1 { + to { left: 100px } +} +@keyframes anim2 { + to { top: 100px } +} +@keyframes multiPropAnim { + to { background: green, opacity: 0.5, left: 100px, top: 100px } +} +@keyframes empty { } +</style> +<body> +<script> +'use strict'; + +test(function(t) { + var div = addDiv(t); + assert_equals(div.getAnimations().length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with no animations'); +}, 'getAnimations for non-animated content'); + +promise_test(function(t) { + var div = addDiv(t); + + // FIXME: This test does too many things. It should be split up. + + // Add an animation + div.style.animation = 'anim1 100s'; + var animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns an Animation running CSS Animations'); + return animations[0].ready.then(function() { + var startTime = animations[0].startTime; + assert_true(startTime > 0 && startTime <= document.timeline.currentTime, + 'CSS animation has a sensible start time'); + + // Wait a moment then add a second animation. + // + // We wait for the next frame so that we can test that the start times of + // the animations differ. + return waitForFrame(); + }).then(function() { + div.style.animation = 'anim1 100s, anim2 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns one Animation for each value of' + + ' animation-name'); + // Wait until both Animations are ready + // (We don't make any assumptions about the order of the Animations since + // that is the purpose of the following test.) + return waitForAllAnimations(animations); + }).then(function() { + assert_true(animations[0].startTime < animations[1].startTime, + 'Additional Animations for CSS animations start after the original' + + ' animation and appear later in the list'); + }); +}, 'getAnimations for CSS Animations'); + +test(function(t) { + var div = addDiv(t, { style: 'animation: anim1 100s' }); + assert_class_string(div.getAnimations()[0], 'CSSAnimation', + 'Interface of returned animation is CSSAnimation'); +}, 'getAnimations returns CSSAnimation objects for CSS Animations'); + +test(function(t) { + var div = addDiv(t); + + // Add an animation that targets multiple properties + div.style.animation = 'multiPropAnim 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns only one Animation for a CSS Animation' + + ' that targets multiple properties'); +}, 'getAnimations for multi-property animations'); + +promise_test(function(t) { + var div = addDiv(t); + + // Add an animation + div.style.backgroundColor = 'red'; + div.style.animation = 'anim1 100s'; + window.getComputedStyle(div).backgroundColor; + + // Wait until a frame after the animation starts, then add a transition + var animations = div.getAnimations(); + return animations[0].ready.then(waitForFrame).then(function() { + div.style.transition = 'all 100s'; + div.style.backgroundColor = 'green'; + + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns Animations for both animations and' + + ' transitions that run simultaneously'); + assert_class_string(animations[0], 'CSSTransition', + 'First-returned animation is the CSS Transition'); + assert_class_string(animations[1], 'CSSAnimation', + 'Second-returned animation is the CSS Animation'); + }); +}, 'getAnimations for both CSS Animations and CSS Transitions at once'); + +async_test(function(t) { + var div = addDiv(t); + + // Set up event listener + div.addEventListener('animationend', t.step_func(function() { + assert_equals(div.getAnimations().length, 0, + 'getAnimations does not return Animations for finished ' + + ' (and non-forwards-filling) CSS Animations'); + t.done(); + })); + + // Add a very short animation + div.style.animation = 'anim1 0.01s'; +}, 'getAnimations for CSS Animations that have finished'); + +async_test(function(t) { + var div = addDiv(t); + + // Set up event listener + div.addEventListener('animationend', t.step_func(function() { + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns Animations for CSS Animations that have' + + ' finished but are filling forwards'); + t.done(); + })); + + // Add a very short animation + div.style.animation = 'anim1 0.01s forwards'; +}, 'getAnimations for CSS Animations that have finished but are' + + ' forwards filling'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'none 100s'; + + var animations = div.getAnimations(); + assert_equals(animations.length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with animation-name: none'); + + div.style.animation = 'none 100s, anim1 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns Animations only for those CSS Animations whose' + + ' animation-name is not none'); +}, 'getAnimations for CSS Animations with animation-name: none'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'missing 100s'; + var animations = div.getAnimations(); + assert_equals(animations.length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with animation-name: missing'); + + div.style.animation = 'anim1 100s, missing 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns Animations only for those CSS Animations whose' + + ' animation-name is found'); +}, 'getAnimations for CSS Animations with animation-name: missing'); + +promise_test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 100s, notyet 100s'; + var animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations initally only returns Animations for CSS Animations whose' + + ' animation-name is found'); + + return animations[0].ready.then(waitForFrame).then(function() { + var keyframes = '@keyframes notyet { to { left: 100px; } }'; + document.styleSheets[0].insertRule(keyframes, 0); + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations includes Animation when @keyframes rule is added' + + ' later'); + return waitForAllAnimations(animations); + }).then(function() { + assert_true(animations[0].startTime < animations[1].startTime, + 'Newly added animation has a later start time'); + document.styleSheets[0].deleteRule(0); + }); +}, 'getAnimations for CSS Animations where the @keyframes rule is added' + + ' later'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 100s, anim1 100s'; + assert_equals(div.getAnimations().length, 2, + 'getAnimations returns one Animation for each CSS animation-name' + + ' even if the names are duplicated'); +}, 'getAnimations for CSS Animations with duplicated animation-name'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'empty 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns Animations for CSS animations with an' + + ' empty keyframes rule'); +}, 'getAnimations for CSS Animations with empty keyframes rule'); + +promise_test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 100s 100s'; + var animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns animations for CSS animations whose' + + ' delay makes them start later'); + return animations[0].ready.then(waitForFrame).then(function() { + assert_true(animations[0].startTime <= document.timeline.currentTime, + 'For CSS Animations in delay phase, the start time of the Animation is' + + ' not in the future'); + }); +}, 'getAnimations for CSS animations in delay phase'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 0s 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns animations for CSS animations whose' + + ' duration is zero'); + div.remove(); +}, 'getAnimations for zero-duration CSS Animations'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 100s'; + var originalAnimation = div.getAnimations()[0]; + + // Update pause state (an Animation change) + div.style.animationPlayState = 'paused'; + var pendingAnimation = div.getAnimations()[0]; + assert_equals(pendingAnimation.playState, 'pending', + 'animation\'s play state is updated'); + assert_equals(originalAnimation, pendingAnimation, + 'getAnimations returns the same objects even when their' + + ' play state changes'); + + // Update duration (an Animation change) + div.style.animationDuration = '200s'; + var extendedAnimation = div.getAnimations()[0]; + // FIXME: Check extendedAnimation.effect.timing.duration has changed once the + // API is available + assert_equals(originalAnimation, extendedAnimation, + 'getAnimations returns the same objects even when their' + + ' duration changes'); +}, 'getAnimations returns objects with the same identity'); + +test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim1 100s'; + + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns an animation before cancelling'); + + var animation = div.getAnimations()[0]; + + animation.cancel(); + assert_equals(div.getAnimations().length, 0, + 'getAnimations does not return cancelled animations'); + + animation.play(); + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns cancelled animations that have been re-started'); + +}, 'getAnimations for CSS Animations that are cancelled'); + +promise_test(function(t) { + var div = addDiv(t); + div.style.animation = 'anim2 100s'; + + return div.getAnimations()[0].ready.then(function() { + // Prepend to the list and test that even though anim1 was triggered + // *after* anim2, it should come first because it appears first + // in the animation-name property. + div.style.animation = 'anim1 100s, anim2 100s'; + var anims = div.getAnimations(); + assert_equals(anims[0].animationName, 'anim1', + 'animation order after prepending to list'); + assert_equals(anims[1].animationName, 'anim2', + 'animation order after prepending to list'); + + // Normally calling cancel and play would this push anim1 to the top of + // the stack but it shouldn't for CSS animations that map an the + // animation-name property. + var anim1 = anims[0]; + anim1.cancel(); + anim1.play(); + anims = div.getAnimations(); + assert_equals(anims[0].animationName, 'anim1', + 'animation order after cancelling and restarting'); + assert_equals(anims[1].animationName, 'anim2', + 'animation order after cancelling and restarting'); + }); +}, 'getAnimations for CSS Animations follows animation-name order'); + +test(function(t) { + addStyle(t, { '#target::after': 'animation: anim1 10s;', + '#target::before': 'animation: anim1 10s;' }); + var target = addDiv(t, { 'id': 'target' }); + target.style.animation = 'anim1 100s'; + + var animations = target.getAnimations({ subtree: false }); + assert_equals(animations.length, 1, + 'Should find only the element'); + assert_equals(animations[0].effect.target, target, + 'Effect target should be the element'); +}, 'Test AnimationFilter{ subtree: false } with single element'); + +test(function(t) { + addStyle(t, { '#target::after': 'animation: anim1 10s;', + '#target::before': 'animation: anim1 10s;' }); + var target = addDiv(t, { 'id': 'target' }); + target.style.animation = 'anim1 100s'; + + var animations = target.getAnimations({ subtree: true }); + assert_equals(animations.length, 3, + 'getAnimations({ subtree: true }) ' + + 'should return animations on pseudo-elements'); + assert_equals(animations[0].effect.target, target, + 'The animation targeting the parent element ' + + 'should be returned first'); + assert_equals(animations[1].effect.target.type, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned second'); + assert_equals(animations[2].effect.target.type, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned last'); +}, 'Test AnimationFilter{ subtree: true } with single element'); + +test(function(t) { + addStyle(t, { '#parent::after': 'animation: anim1 10s;', + '#parent::before': 'animation: anim1 10s;', + '#child::after': 'animation: anim1 10s;', + '#child::before': 'animation: anim1 10s;' }); + var parent = addDiv(t, { 'id': 'parent' }); + parent.style.animation = 'anim1 100s'; + var child = addDiv(t, { 'id': 'child' }); + child.style.animation = 'anim1 100s'; + parent.appendChild(child); + + var animations = parent.getAnimations({ subtree: false }); + assert_equals(animations.length, 1, + 'Should find only the element even if it has a child'); + assert_equals(animations[0].effect.target, parent, + 'Effect target shuld be the element'); +}, 'Test AnimationFilter{ subtree: false } with element that has a child'); + +test(function(t) { + addStyle(t, { '#parent::after': 'animation: anim1 10s;', + '#parent::before': 'animation: anim1 10s;', + '#child::after': 'animation: anim1 10s;', + '#child::before': 'animation: anim1 10s;' }); + var parent = addDiv(t, { 'id': 'parent' }); + var child = addDiv(t, { 'id': 'child' }); + parent.style.animation = 'anim1 100s'; + child.style.animation = 'anim1 100s'; + parent.appendChild(child); + + var animations = parent.getAnimations({ subtree: true }); + assert_equals(animations.length, 6, + 'Should find all elements, pesudo-elements that parent has'); + + assert_equals(animations[0].effect.target, parent, + 'The animation targeting the parent element ' + + 'should be returned first'); + assert_equals(animations[1].effect.target.type, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned second'); + assert_equals(animations[1].effect.target.parentElement, parent, + 'This ::before element should be child of parent element'); + assert_equals(animations[2].effect.target.type, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned third'); + assert_equals(animations[2].effect.target.parentElement, parent, + 'This ::after element should be child of parent element'); + + assert_equals(animations[3].effect.target, child, + 'The animation targeting the child element ' + + 'should be returned fourth'); + assert_equals(animations[4].effect.target.type, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned fifth'); + assert_equals(animations[4].effect.target.parentElement, child, + 'This ::before element should be child of child element'); + assert_equals(animations[5].effect.target.type, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned last'); + assert_equals(animations[5].effect.target.parentElement, child, + 'This ::after element should be child of child element'); +}, 'Test AnimationFilter{ subtree: true } with element that has a child'); + +test(function(t) { + var parent = addDiv(t, { 'id': 'parent' }); + var child1 = addDiv(t, { 'id': 'child1' }); + var grandchild1 = addDiv(t, { 'id': 'grandchild1' }); + var grandchild2 = addDiv(t, { 'id': 'grandchild2' }); + var child2 = addDiv(t, { 'id': 'child2' }); + + parent.style.animation = 'anim1 100s'; + child1.style.animation = 'anim1 100s'; + grandchild1.style.animation = 'anim1 100s'; + grandchild2.style.animation = 'anim1 100s'; + child2.style.animation = 'anim1 100s'; + + parent.appendChild(child1); + child1.appendChild(grandchild1); + child1.appendChild(grandchild2); + parent.appendChild(child2); + + var animations = parent.getAnimations({ subtree: true }); + assert_equals( + parent.getAnimations({ subtree: true }).length, 5, + 'Should find all descendants of the element'); + + assert_equals(animations[0].effect.target, parent, + 'The animation targeting the parent element ' + + 'should be returned first'); + + assert_equals(animations[1].effect.target, child1, + 'The animation targeting the child1 element ' + + 'should be returned second'); + + assert_equals(animations[2].effect.target, grandchild1, + 'The animation targeting the grandchild1 element ' + + 'should be returned third'); + + assert_equals(animations[3].effect.target, grandchild2, + 'The animation targeting the grandchild2 element ' + + 'should be returned fourth'); + + assert_equals(animations[4].effect.target, child2, + 'The animation targeting the child2 element ' + + 'should be returned last'); + +}, 'Test AnimationFilter{ subtree: true } with element that has many descendant'); + +done(); +</script> +</body> |