diff options
Diffstat (limited to 'testing/web-platform/tests/web-animations/timing-model/animations/reversing-an-animation.html')
-rw-r--r-- | testing/web-platform/tests/web-animations/timing-model/animations/reversing-an-animation.html | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/reversing-an-animation.html b/testing/web-platform/tests/web-animations/timing-model/animations/reversing-an-animation.html new file mode 100644 index 0000000000..8d869d72aa --- /dev/null +++ b/testing/web-platform/tests/web-animations/timing-model/animations/reversing-an-animation.html @@ -0,0 +1,266 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Reversing an animation</title> +<link rel="help" + href="https://drafts.csswg.org/web-animations/#reversing-an-animation-section"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../testcommon.js"></script> +<body> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + + await animation.ready; + // Wait a frame because if currentTime is still 0 when we call + // reverse(), it will throw (per spec). + await waitForAnimationFrames(1); + + assert_greater_than_equal(animation.currentTime, 0, + 'currentTime expected to be greater than 0, one frame after starting'); + animation.currentTime = 50 * MS_PER_SEC; + const previousPlaybackRate = animation.playbackRate; + animation.reverse(); + assert_equals(animation.playbackRate, previousPlaybackRate, + 'Playback rate should not have changed'); + await animation.ready; + + assert_equals(animation.playbackRate, -previousPlaybackRate, + 'Playback rate should be inverted'); +}, 'Reversing an animation inverts the playback rate'); + +promise_test(async t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + animation.currentTime = 50 * MS_PER_SEC; + animation.pause(); + + await animation.ready; + + animation.reverse(); + await animation.ready; + + assert_equals(animation.playState, 'running', + 'Animation.playState should be "running" after reverse()'); +}, 'Reversing an animation plays a pausing animation'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + animation.currentTime = 50 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 50 * MS_PER_SEC, + 'The current time should not change it is in the middle of ' + + 'the animation duration'); +}, 'Reversing an animation maintains the same current time'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 200 * MS_PER_SEC, + delay: -100 * MS_PER_SEC }); + assert_true(animation.pending, + 'The animation is pending before we call reverse'); + + animation.reverse(); + + assert_true(animation.pending, + 'The animation is still pending after calling reverse'); +}, 'Reversing an animation does not cause it to leave the pending state'); + +promise_test(async t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 200 * MS_PER_SEC, + delay: -100 * MS_PER_SEC }); + let readyResolved = false; + animation.ready.then(() => { readyResolved = true; }); + + animation.reverse(); + + await Promise.resolve(); + assert_false(readyResolved, + 'ready promise should not have been resolved yet'); +}, 'Reversing an animation does not cause it to resolve the ready promise'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + animation.currentTime = 200 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 100 * MS_PER_SEC, + 'reverse() should start playing from the animation effect end ' + + 'if the playbackRate > 0 and the currentTime > effect end'); +}, 'Reversing an animation when playbackRate > 0 and currentTime > ' + + 'effect end should make it play from the end'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + + animation.currentTime = -200 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 100 * MS_PER_SEC, + 'reverse() should start playing from the animation effect end ' + + 'if the playbackRate > 0 and the currentTime < 0'); +}, 'Reversing an animation when playbackRate > 0 and currentTime < 0 ' + + 'should make it play from the end'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + animation.playbackRate = -1; + animation.currentTime = -200 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 0, + 'reverse() should start playing from the start of animation time ' + + 'if the playbackRate < 0 and the currentTime < 0'); +}, 'Reversing an animation when playbackRate < 0 and currentTime < 0 ' + + 'should make it play from the start'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + animation.playbackRate = -1; + animation.currentTime = 200 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 0, + 'reverse() should start playing from the start of animation time ' + + 'if the playbackRate < 0 and the currentTime > effect end'); +}, 'Reversing an animation when playbackRate < 0 and currentTime > effect ' + + 'end should make it play from the start'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + animation.currentTime = -200 * MS_PER_SEC; + + assert_throws_dom('InvalidStateError', + () => { animation.reverse(); }, + 'reverse() should throw InvalidStateError ' + + 'if the playbackRate > 0 and the currentTime < 0 ' + + 'and the target effect is positive infinity'); +}, 'Reversing an animation when playbackRate > 0 and currentTime < 0 ' + + 'and the target effect end is positive infinity should throw an exception'); + +promise_test(async t => { + const animation = createDiv(t).animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + animation.currentTime = -200 * MS_PER_SEC; + + try { animation.reverse(); } catch(e) { } + + assert_equals(animation.playbackRate, 1, 'playbackRate is unchanged'); + + await animation.ready; + assert_equals(animation.playbackRate, 1, 'playbackRate remains unchanged'); +}, 'When reversing throws an exception, the playback rate remains unchanged'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + animation.currentTime = -200 * MS_PER_SEC; + animation.playbackRate = 0; + + try { + animation.reverse(); + } catch (e) { + assert_unreached(`Unexpected exception when calling reverse(): ${e}`); + } +}, 'Reversing animation when playbackRate = 0 and currentTime < 0 ' + + 'and the target effect end is positive infinity should NOT throw an ' + + 'exception'); + +test(t => { + const div = createDiv(t); + const animation = div.animate({}, { duration: 100 * MS_PER_SEC, + iterations: Infinity }); + animation.playbackRate = -1; + animation.currentTime = -200 * MS_PER_SEC; + animation.reverse(); + + assert_equals(animation.currentTime, 0, + 'reverse() should start playing from the start of animation time ' + + 'if the playbackRate < 0 and the currentTime < 0 ' + + 'and the target effect is positive infinity'); +}, 'Reversing an animation when playbackRate < 0 and currentTime < 0 ' + + 'and the target effect end is positive infinity should make it play ' + + 'from the start'); + +promise_test(async t => { + const div = createDiv(t); + const animation = div.animate({}, 100 * MS_PER_SEC); + animation.playbackRate = 0; + animation.currentTime = 50 * MS_PER_SEC; + animation.reverse(); + + await animation.ready; + assert_equals(animation.playbackRate, 0, + 'reverse() should preserve playbackRate if the playbackRate == 0'); + assert_equals(animation.currentTime, 50 * MS_PER_SEC, + 'reverse() should not affect the currentTime if the playbackRate == 0'); +}, 'Reversing when when playbackRate == 0 should preserve the current ' + + 'time and playback rate'); + +test(t => { + const div = createDiv(t); + const animation = + new Animation(new KeyframeEffect(div, null, 100 * MS_PER_SEC)); + assert_equals(animation.currentTime, null); + + animation.reverse(); + + assert_equals(animation.currentTime, 100 * MS_PER_SEC, + 'animation.currentTime should be at its effect end'); +}, 'Reversing an idle animation from starts playing the animation'); + +test(t => { + const div = createDiv(t); + const animation = + new Animation(new KeyframeEffect(div, null, 100 * MS_PER_SEC), null); + + assert_throws_dom('InvalidStateError', () => { animation.reverse(); }); +}, 'Reversing an animation without an active timeline throws an ' + + 'InvalidStateError'); + +promise_test(async t => { + const animation = createDiv(t).animate(null, 100 * MS_PER_SEC); + await animation.ready; + + animation.updatePlaybackRate(2); + animation.reverse(); + + await animation.ready; + assert_equals(animation.playbackRate, -2); +}, 'Reversing should use the negative pending playback rate'); + +promise_test(async t => { + const animation = createDiv(t).animate(null, { + duration: 100 * MS_PER_SEC, + iterations: Infinity, + }); + animation.currentTime = -200 * MS_PER_SEC; + await animation.ready; + + animation.updatePlaybackRate(2); + assert_throws_dom('InvalidStateError', () => { animation.reverse(); }); + assert_equals(animation.playbackRate, 1); + + await animation.ready; + assert_equals(animation.playbackRate, 2); +}, 'When reversing fails, it should restore any previous pending playback' + + ' rate'); + +</script> +</body> |