diff options
Diffstat (limited to 'testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html')
-rw-r--r-- | testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html b/testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html new file mode 100644 index 0000000000..fd8719299d --- /dev/null +++ b/testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html @@ -0,0 +1,355 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Animatable.getAnimations</title> +<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animatable-getanimations"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../testcommon.js"></script> +<body> +<script> +'use strict'; + +test(t => { + const div = createDiv(t); + assert_array_equals(div.getAnimations(), []); +}, 'Returns an empty array for an element with no animations'); + +test(t => { + const div = createDiv(t); + const animationA = div.animate(null, 100 * MS_PER_SEC); + const animationB = div.animate(null, 100 * MS_PER_SEC); + assert_array_equals(div.getAnimations(), [animationA, animationB]); +}, 'Returns both animations for an element with two animations'); + +test(t => { + const divA = createDiv(t); + const divB = createDiv(t); + const animationA = divA.animate(null, 100 * MS_PER_SEC); + const animationB = divB.animate(null, 100 * MS_PER_SEC); + assert_array_equals(divA.getAnimations(), [animationA], 'divA'); + assert_array_equals(divB.getAnimations(), [animationB], 'divB'); +}, 'Returns only the animations specific to each sibling element'); + +test(t => { + const divParent = createDiv(t); + const divChild = createDiv(t); + divParent.appendChild(divChild); + const animationParent = divParent.animate(null, 100 * MS_PER_SEC); + const animationChild = divChild.animate(null, 100 * MS_PER_SEC); + assert_array_equals(divParent.getAnimations(), [animationParent], + 'divParent'); + assert_array_equals(divChild.getAnimations(), [animationChild], 'divChild'); +}, 'Returns only the animations specific to each parent/child element'); + +test(t => { + const divParent = createDiv(t); + const divChild = createDiv(t); + divParent.appendChild(divChild); + const divGrandChildA = createDiv(t); + const divGrandChildB = createDiv(t); + divChild.appendChild(divGrandChildA); + divChild.appendChild(divGrandChildB); + + // Trigger the animations in a somewhat random order + const animGrandChildB = divGrandChildB.animate(null, 100 * MS_PER_SEC); + const animChild = divChild.animate(null, 100 * MS_PER_SEC); + const animGrandChildA = divGrandChildA.animate(null, 100 * MS_PER_SEC); + + assert_array_equals( + divParent.getAnimations({ subtree: true }), + [animGrandChildB, animChild, animGrandChildA], + 'Returns expected animations from parent' + ); + assert_array_equals( + divChild.getAnimations({ subtree: true }), + [animGrandChildB, animChild, animGrandChildA], + 'Returns expected animations from child' + ); + assert_array_equals( + divGrandChildA.getAnimations({ subtree: true }), + [animGrandChildA], + 'Returns expected animations from grandchild A' + ); +}, 'Returns animations on descendants when subtree: true is specified'); + +test(t => { + createStyle(t, { + '@keyframes anim': '', + [`.pseudo::before`]: 'animation: anim 100s; ' + "content: '';", + }); + const div = createDiv(t); + div.classList.add('pseudo'); + + assert_equals( + div.getAnimations().length, + 0, + 'Returns no animations when subtree is false' + ); + assert_equals( + div.getAnimations({ subtree: true }).length, + 1, + 'Returns one animation when subtree is true' + ); +}, 'Returns animations on pseudo-elements when subtree: true is specified'); + +test(t => { + const host = createDiv(t); + const shadow = host.attachShadow({ mode: 'open' }); + + const elem = createDiv(t); + shadow.appendChild(elem); + + const elemChild = createDiv(t); + elem.appendChild(elemChild); + + elemChild.animate(null, 100 * MS_PER_SEC); + + assert_equals( + host.getAnimations({ subtree: true }).length, + 0, + 'Returns no animations with subtree:true when called on the host' + ); + assert_equals( + elem.getAnimations({ subtree: true }).length, + 1, + 'Returns one animation when called on a parent in the shadow tree' + ); +}, 'Does NOT cross shadow-tree boundaries when subtree: true is specified'); + +test(t => { + const foreignElement + = document.createElementNS('http://example.org/test', 'test'); + document.body.appendChild(foreignElement); + t.add_cleanup(() => { + foreignElement.remove(); + }); + + const animation = foreignElement.animate(null, 100 * MS_PER_SEC); + assert_array_equals(foreignElement.getAnimations(), [animation]); +}, 'Returns animations for a foreign element'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + animation.finish(); + assert_array_equals(div.getAnimations(), []); +}, 'Does not return finished animations that do not fill forwards'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, { + duration: 100 * MS_PER_SEC, + fill: 'forwards', + }); + animation.finish(); + assert_array_equals(div.getAnimations(), [animation]); +}, 'Returns finished animations that fill forwards'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, { + duration: 100 * MS_PER_SEC, + delay: 100 * MS_PER_SEC, + }); + assert_array_equals(div.getAnimations(), [animation]); +}, 'Returns animations yet to reach their active phase'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + animation.playbackRate = -1; + assert_array_equals(div.getAnimations(), []); +}, 'Does not return reversed finished animations that do not fill backwards'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, { + duration: 100 * MS_PER_SEC, + fill: 'backwards', + }); + animation.playbackRate = -1; + assert_array_equals(div.getAnimations(), [animation]); +}, 'Returns reversed finished animations that fill backwards'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + animation.playbackRate = -1; + animation.currentTime = 200 * MS_PER_SEC; + assert_array_equals(div.getAnimations(), [animation]); +}, 'Returns reversed animations yet to reach their active phase'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, { + duration: 100 * MS_PER_SEC, + delay: 100 * MS_PER_SEC, + }); + animation.playbackRate = 0; + assert_array_equals(div.getAnimations(), []); +}, 'Does not return animations with zero playback rate in before phase'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + animation.finish(); + animation.playbackRate = 0; + animation.currentTime = 200 * MS_PER_SEC; + assert_array_equals(div.getAnimations(), []); +}, 'Does not return animations with zero playback rate in after phase'); + +test(t => { + const div = createDiv(t); + const effect = new KeyframeEffect(div, {}, 225); + const animation = new Animation(effect, new DocumentTimeline()); + animation.reverse(); + animation.pause(); + animation.playbackRate = -1;; + animation.updatePlaybackRate(1); + assert_array_equals(div.getAnimations(), []); +}, 'Does not return an animation that has recently been made not current by setting the playback rate'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + + animation.finish(); + assert_array_equals(div.getAnimations(), [], + 'Animation should not be returned when it is finished'); + + animation.effect.updateTiming({ + duration: animation.effect.getTiming().duration + 100 * MS_PER_SEC, + }); + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned after extending the' + + ' duration'); + + animation.effect.updateTiming({ duration: 0 }); + assert_array_equals(div.getAnimations(), [], + 'Animation should not be returned after setting the' + + ' duration to zero'); +}, 'Returns animations based on dynamic changes to individual' + + ' animations\' duration'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + + animation.effect.updateTiming({ endDelay: -200 * MS_PER_SEC }); + assert_array_equals(div.getAnimations(), [], + 'Animation should not be returned after setting a' + + ' negative end delay such that the end time is less' + + ' than the current time'); + + animation.effect.updateTiming({ endDelay: 100 * MS_PER_SEC }); + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned after setting a positive' + + ' end delay such that the end time is more than the' + + ' current time'); +}, 'Returns animations based on dynamic changes to individual' + + ' animations\' end delay'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, 100 * MS_PER_SEC); + + animation.finish(); + assert_array_equals(div.getAnimations(), [], + 'Animation should not be returned when it is finished'); + + animation.effect.updateTiming({ iterations: 10 }); + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned after inreasing the' + + ' number of iterations'); + + animation.effect.updateTiming({ iterations: 0 }); + assert_array_equals(div.getAnimations(), [], + 'Animations should not be returned after setting the' + + ' iteration count to zero'); + + animation.effect.updateTiming({ iterations: Infinity }); + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned after inreasing the' + + ' number of iterations to infinity'); +}, 'Returns animations based on dynamic changes to individual' + + ' animations\' iteration count'); + +test(t => { + const div = createDiv(t); + const animation = div.animate(null, + { duration: 100 * MS_PER_SEC, + delay: 50 * MS_PER_SEC, + endDelay: -50 * MS_PER_SEC }); + + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned at during delay phase'); + + animation.currentTime = 50 * MS_PER_SEC; + assert_array_equals(div.getAnimations(), [animation], + 'Animation should be returned after seeking to the start' + + ' of the active interval'); + + animation.currentTime = 100 * MS_PER_SEC; + assert_array_equals(div.getAnimations(), [], + 'Animation should not be returned after seeking to the' + + ' clipped end of the active interval'); +}, 'Returns animations based on dynamic changes to individual' + + ' animations\' current time'); + +promise_test(async t => { + const div = createDiv(t); + + const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); + const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); + await animA.finished; + // It is not guaranteed that the mircrotask PerformCheckpoint() happens before + // the animation finish promised got resolved, because the microtask + // checkpoint could also be triggered from other source such as the event_loop + // Thus we wait for one animation frame to make sure the finished animation is + // properly removed. + await waitForNextFrame(1); + assert_array_equals(div.getAnimations(), [animB]); +}, 'Does not return an animation that has been removed'); + +promise_test(async t => { + const div = createDiv(t); + + const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); + const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); + await animA.finished; + + animA.persist(); + + assert_array_equals(div.getAnimations(), [animA, animB]); +}, 'Returns an animation that has been persisted'); + +promise_test(async t => { + const div = createDiv(t); + const watcher = EventWatcher(t, div, 'transitionrun'); + + // Create a covering animation to prevent transitions from firing after + // calling getAnimations(). + const coveringAnimation = new Animation( + new KeyframeEffect(div, { opacity: [0, 1] }, 100 * MS_PER_SEC) + ); + + // Setup transition start point. + div.style.transition = 'opacity 100s'; + getComputedStyle(div).opacity; + + // Update specified style but don't flush style. + div.style.opacity = '0.5'; + + // Fetch animations + div.getAnimations(); + + // Play the covering animation to ensure that only the call to + // getAnimations() has a chance to trigger transitions. + coveringAnimation.play(); + + // If getAnimations() flushed style, we should get a transitionrun event. + await watcher.wait_for('transitionrun'); +}, 'Triggers a style change event'); + +</script> +</body> |