summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/interfaces/Animatable/getAnimations.html
diff options
context:
space:
mode:
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.html355
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>