summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/interfaces/Animation/finished.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/web-animations/interfaces/Animation/finished.html')
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/finished.html416
1 files changed, 416 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/finished.html b/testing/web-platform/tests/web-animations/interfaces/Animation/finished.html
new file mode 100644
index 0000000000..bee4fd8fb7
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/finished.html
@@ -0,0 +1,416 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.finished</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-finished">
+<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(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ return animation.ready.then(() => {
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is the same object when playing starts');
+ animation.pause();
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise does not change when pausing');
+ animation.play();
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise does not change when play() unpauses');
+
+ animation.currentTime = 100 * MS_PER_SEC;
+
+ return animation.finished;
+ }).then(() => {
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is the same object when playing completes');
+ });
+}, 'Test pausing then playing does not change the finished promise');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ let previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(() => {
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is the same object when playing completes');
+ animation.play();
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise changes when replaying animation');
+
+ previousFinishedPromise = animation.finished;
+ animation.play();
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is the same after redundant play() call');
+
+ });
+}, 'Test restarting a finished animation');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ let previousFinishedPromise;
+ animation.finish();
+ return animation.finished.then(() => {
+ previousFinishedPromise = animation.finished;
+ animation.playbackRate = -1;
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise should be replaced when reversing a ' +
+ 'finished promise');
+ animation.currentTime = 0;
+ return animation.finished;
+ }).then(() => {
+ previousFinishedPromise = animation.finished;
+ animation.play();
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is replaced after play() call on ' +
+ 'finished, reversed animation');
+ });
+}, 'Test restarting a reversed finished animation');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(() => {
+ animation.currentTime = 100 * MS_PER_SEC + 1000;
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise is unchanged jumping past end of ' +
+ 'finished animation');
+ });
+}, 'Test redundant finishing of animation');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ // Setup callback to run if finished promise is resolved
+ let finishPromiseResolved = false;
+ animation.finished.then(() => {
+ finishPromiseResolved = true;
+ });
+ return animation.ready.then(() => {
+ // Jump to mid-way in interval and pause
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ animation.pause();
+ return animation.ready;
+ }).then(() => {
+ // Jump to the end
+ // (But don't use finish() since that should unpause as well)
+ animation.currentTime = 100 * MS_PER_SEC;
+ return waitForAnimationFrames(2);
+ }).then(() => {
+ assert_false(finishPromiseResolved,
+ 'Finished promise should not resolve when paused');
+ });
+}, 'Finished promise does not resolve when paused');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ // Setup callback to run if finished promise is resolved
+ let finishPromiseResolved = false;
+ animation.finished.then(() => {
+ finishPromiseResolved = true;
+ });
+ return animation.ready.then(() => {
+ // Jump to mid-way in interval and pause
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ animation.pause();
+ // Jump to the end
+ animation.currentTime = 100 * MS_PER_SEC;
+ return waitForAnimationFrames(2);
+ }).then(() => {
+ assert_false(finishPromiseResolved,
+ 'Finished promise should not resolve when pause-pending');
+ });
+}, 'Finished promise does not resolve when pause-pending');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.finish();
+ return animation.finished.then(resolvedAnimation => {
+ assert_equals(resolvedAnimation, animation,
+ 'Object identity of animation passed to Promise callback'
+ + ' matches the animation object owning the Promise');
+ });
+}, 'The finished promise is fulfilled with its Animation');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+
+ // Set up listeners on finished promise
+ const retPromise = animation.finished.then(() => {
+ assert_unreached('finished promise was fulfilled');
+ }).catch(err => {
+ assert_equals(err.name, 'AbortError',
+ 'finished promise is rejected with AbortError');
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise should change after the original is ' +
+ 'rejected');
+ });
+
+ animation.cancel();
+
+ return retPromise;
+}, 'finished promise is rejected when an animation is canceled by calling ' +
+ 'cancel()');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(() => {
+ animation.cancel();
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'A new finished promise should be created when'
+ + ' canceling a finished animation');
+ });
+}, 'canceling an already-finished animation replaces the finished promise');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const HALF_DUR = 100 * MS_PER_SEC / 2;
+ const QUARTER_DUR = 100 * MS_PER_SEC / 4;
+ let gotNextFrame = false;
+ let currentTimeBeforeShortening;
+ animation.currentTime = HALF_DUR;
+ return animation.ready.then(() => {
+ currentTimeBeforeShortening = animation.currentTime;
+ animation.effect.updateTiming({ duration: QUARTER_DUR });
+ // Below we use gotNextFrame to check that shortening of the animation
+ // duration causes the finished promise to resolve, rather than it just
+ // getting resolved on the next animation frame. This relies on the fact
+ // that the promises are resolved as a micro-task before the next frame
+ // happens.
+ waitForAnimationFrames(1).then(() => {
+ gotNextFrame = true;
+ });
+
+ return animation.finished;
+ }).then(() => {
+ assert_false(gotNextFrame, 'shortening of the animation duration should ' +
+ 'resolve the finished promise');
+ assert_equals(animation.currentTime, currentTimeBeforeShortening,
+ 'currentTime should be unchanged when duration shortened');
+ const previousFinishedPromise = animation.finished;
+ animation.effect.updateTiming({ duration: 100 * MS_PER_SEC });
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise should change after lengthening the ' +
+ 'duration causes the animation to become active');
+ });
+}, 'Test finished promise changes for animation duration changes');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const retPromise = animation.ready.then(() => {
+ animation.playbackRate = 0;
+ animation.currentTime = 100 * MS_PER_SEC + 1000;
+ return waitForAnimationFrames(2);
+ });
+
+ animation.finished.then(t.step_func(() => {
+ assert_unreached('finished promise should not resolve when playbackRate ' +
+ 'is zero');
+ }));
+
+ return retPromise;
+}, 'Test finished promise changes when playbackRate == 0');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ return animation.ready.then(() => {
+ animation.playbackRate = -1;
+ return animation.finished;
+ });
+}, 'Test finished promise resolves when reaching to the natural boundary.');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(() => {
+ animation.currentTime = 0;
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'Finished promise should change once a prior ' +
+ 'finished promise resolved and the animation ' +
+ 'falls out finished state');
+ });
+}, 'Test finished promise changes when a prior finished promise resolved ' +
+ 'and the animation falls out finished state');
+
+test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ assert_equals(animation.finished, previousFinishedPromise,
+ 'No new finished promise generated when finished state ' +
+ 'is checked asynchronously');
+}, 'Test no new finished promise generated when finished state ' +
+ 'is checked asynchronously');
+
+test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ const previousFinishedPromise = animation.finished;
+ animation.finish();
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'New finished promise generated when finished state ' +
+ 'is checked synchronously');
+}, 'Test new finished promise generated when finished state ' +
+ 'is checked synchronously');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ let resolvedFinished = false;
+ animation.finished.then(() => {
+ resolvedFinished = true;
+ });
+ return animation.ready.then(() => {
+ animation.finish();
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ }).then(() => {
+ assert_true(resolvedFinished,
+ 'Animation.finished should be resolved even if ' +
+ 'the finished state is changed soon');
+ });
+
+}, 'Test synchronous finished promise resolved even if finished state ' +
+ 'is changed soon');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ let resolvedFinished = false;
+ animation.finished.then(() => {
+ resolvedFinished = true;
+ });
+
+ return animation.ready.then(() => {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.finish();
+ }).then(() => {
+ assert_true(resolvedFinished,
+ 'Animation.finished should be resolved soon after finish() is ' +
+ 'called even if there are other asynchronous promises just before it');
+ });
+}, 'Test synchronous finished promise resolved even if asynchronous ' +
+ 'finished promise happens just before synchronous promise');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.finished.then(t.step_func(() => {
+ assert_unreached('Animation.finished should not be resolved');
+ }));
+
+ return animation.ready.then(() => {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ });
+}, 'Test finished promise is not resolved when the animation ' +
+ 'falls out finished state immediately');
+
+promise_test(t => {
+ const div = createDiv(t);
+ const animation = div.animate({}, 100 * MS_PER_SEC);
+ return animation.ready.then(() => {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.finished.then(t.step_func(() => {
+ assert_unreached('Animation.finished should not be resolved');
+ }));
+ animation.currentTime = 0;
+ });
+
+}, 'Test finished promise is not resolved once the animation ' +
+ 'falls out finished state even though the current finished ' +
+ 'promise is generated soon after animation state became finished');
+
+promise_test(t => {
+ const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ let ready = false;
+ animation.ready.then(
+ t.step_func(() => {
+ ready = true;
+ }),
+ t.unreached_func('Ready promise must not be rejected')
+ );
+
+ const testSuccess = animation.finished.then(
+ t.step_func(() => {
+ assert_true(ready, 'Ready promise has resolved');
+ }),
+ t.unreached_func('Finished promise must not be rejected')
+ );
+
+ const timeout = waitForAnimationFrames(3).then(() => {
+ return Promise.reject('Finished promise did not arrive in time');
+ });
+
+ animation.finish();
+ return Promise.race([timeout, testSuccess]);
+}, 'Finished promise should be resolved after the ready promise is resolved');
+
+promise_test(t => {
+ const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ let caught = false;
+ animation.ready.then(
+ t.unreached_func('Ready promise must not be resolved'),
+ t.step_func(() => {
+ caught = true;
+ })
+ );
+
+ const testSuccess = animation.finished.then(
+ t.unreached_func('Finished promise must not be resolved'),
+ t.step_func(() => {
+ assert_true(caught, 'Ready promise has been rejected');
+ })
+ );
+
+ const timeout = waitForAnimationFrames(3).then(() => {
+ return Promise.reject('Finished promise was not rejected in time');
+ });
+
+ animation.cancel();
+ return Promise.race([timeout, testSuccess]);
+}, 'Finished promise should be rejected after the ready promise is rejected');
+
+promise_test(async t => {
+ const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+
+ // Ensure the finished promise is created
+ const finished = animation.finished;
+
+ window.addEventListener(
+ 'unhandledrejection',
+ t.unreached_func('Should not get an unhandled rejection')
+ );
+
+ animation.cancel();
+
+ // Wait a moment to allow a chance for the event to be dispatched.
+ await waitForAnimationFrames(2);
+}, 'Finished promise does not report an unhandledrejection when rejected');
+
+</script>
+</body>