summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/interfaces/Animation
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/web-animations/interfaces/Animation
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/web-animations/interfaces/Animation')
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/cancel.html60
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/constructor.html115
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/effect.html23
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/finish.html246
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/finished.html370
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/id.html28
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/oncancel.html33
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/onfinish.html121
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/pause.html98
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/play.html34
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/playState.html53
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/playbackRate.html86
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/ready.html96
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/reverse.html158
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/startTime.html55
15 files changed, 1576 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/cancel.html b/testing/web-platform/tests/web-animations/interfaces/Animation/cancel.html
new file mode 100644
index 000000000..f8f174abd
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/cancel.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.cancel()</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-cancel">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({transform: ['translate(100px)', 'translate(100px)']},
+ 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ assert_not_equals(getComputedStyle(div).transform, 'none',
+ 'transform style is animated before cancelling');
+ animation.cancel();
+ assert_equals(getComputedStyle(div).transform, 'none',
+ 'transform style is no longer animated after cancelling');
+ });
+}, 'Animated style is cleared after calling Animation.cancel()');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({marginLeft: ['100px', '200px']},
+ 100 * MS_PER_SEC);
+ animation.effect.timing.easing = 'linear';
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, '0px',
+ 'margin-left style is not animated after cancelling');
+
+ animation.currentTime = 50 * MS_PER_SEC;
+ assert_equals(getComputedStyle(div).marginLeft, '150px',
+ 'margin-left style is updated when cancelled animation is'
+ + ' seeked');
+}, 'After cancelling an animation, it can still be seeked');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({marginLeft:['100px', '200px']},
+ 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, '0px',
+ 'margin-left style is not animated after cancelling');
+ animation.play();
+ assert_equals(getComputedStyle(div).marginLeft, '100px',
+ 'margin-left style is animated after re-starting animation');
+ return animation.ready;
+ }).then(function() {
+ assert_equals(animation.playState, 'running',
+ 'Animation succeeds in running after being re-started');
+ });
+}, 'After cancelling an animation, it can still be re-used');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/constructor.html b/testing/web-platform/tests/web-animations/interfaces/Animation/constructor.html
new file mode 100644
index 000000000..4f76194b6
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/constructor.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation constructor tests</title>
+<link rel="help" href="http://w3c.github.io/web-animations/#dom-animation-animation">
+<link rel="author" title="Hiroyuki Ikezoe" href="mailto:hiikezoe@mozilla-japan.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<div id="target"></div>
+<script>
+"use strict";
+
+var gTarget = document.getElementById("target");
+
+function createEffect() {
+ return new KeyframeEffectReadOnly(gTarget, { opacity: [0, 1] });
+}
+
+function createNull() {
+ return null;
+}
+
+var gTestArguments = [
+ {
+ createEffect: createNull,
+ timeline: null,
+ expectedTimeline: null,
+ expectedTimelineDescription: "null",
+ description: "with null effect and null timeline"
+ },
+ {
+ createEffect: createNull,
+ timeline: document.timeline,
+ expectedTimeline: document.timeline,
+ expectedTimelineDescription: "document.timeline",
+ description: "with null effect and non-null timeline"
+ },
+ {
+ createEffect: createNull,
+ expectedTimeline: document.timeline,
+ expectedTimelineDescription: "document.timeline",
+ description: "with null effect and no timeline parameter"
+ },
+ {
+ createEffect: createEffect,
+ timeline: null,
+ expectedTimeline: null,
+ expectedTimelineDescription: "null",
+ description: "with non-null effect and null timeline"
+ },
+ {
+ createEffect: createEffect,
+ timeline: document.timeline,
+ expectedTimeline: document.timeline,
+ expectedTimelineDescription: "document.timeline",
+ description: "with non-null effect and non-null timeline"
+ },
+ {
+ createEffect: createEffect,
+ expectedTimeline: document.timeline,
+ expectedTimelineDescription: "document.timeline",
+ description: "with non-null effect and no timeline parameter"
+ },
+];
+
+gTestArguments.forEach(function(args) {
+ test(function(t) {
+ var effect = args.createEffect();
+ var animation = new Animation(effect, args.timeline);
+
+ assert_not_equals(animation, null,
+ "An animation sohuld be created");
+ assert_equals(animation.effect, effect,
+ "Animation returns the same effect passed to " +
+ "the Constructor");
+ assert_equals(animation.timeline, args.expectedTimeline,
+ "Animation timeline should be " + args.expectedTimelineDescription);
+ assert_equals(animation.playState, "idle",
+ "Animation.playState should be initially 'idle'");
+ }, "Animation can be constructed " + args.description);
+});
+
+test(function(t) {
+ var effect = new KeyframeEffectReadOnly(null,
+ { left: ["10px", "20px"] },
+ { duration: 10000,
+ fill: "forwards" });
+ var anim = new Animation(effect, document.timeline);
+ anim.pause();
+ assert_equals(effect.getComputedTiming().progress, 0.0);
+ anim.currentTime += 5000;
+ assert_equals(effect.getComputedTiming().progress, 0.5);
+ anim.finish();
+ assert_equals(effect.getComputedTiming().progress, 1.0);
+}, "Animation constructed by an effect with null target runs normally");
+
+async_test(function(t) {
+ var iframe = document.createElement('iframe');
+
+ iframe.addEventListener('load', t.step_func(function() {
+ var div = createDiv(t, iframe.contentDocument);
+ var effect = new KeyframeEffectReadOnly(div, null, 10000);
+ var anim = new Animation(effect);
+ assert_equals(anim.timeline, document.timeline);
+ iframe.remove();
+ t.done();
+ }));
+
+ document.body.appendChild(iframe);
+}, "Animation constructed with a keyframe that target element is in iframe");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/effect.html b/testing/web-platform/tests/web-animations/interfaces/Animation/effect.html
new file mode 100644
index 000000000..d881a96a7
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/effect.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.effect tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-effect">
+<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";
+
+test(function(t) {
+ var anim = new Animation();
+ assert_equals(anim.effect, null, "initial effect is null");
+
+ var newEffect = new KeyframeEffectReadOnly(createDiv(t), null);
+ anim.effect = newEffect;
+ assert_equals(anim.effect, newEffect, "new effect is set");
+}, "effect is set correctly.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/finish.html b/testing/web-platform/tests/web-animations/interfaces/Animation/finish.html
new file mode 100644
index 000000000..9b9c4c710
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/finish.html
@@ -0,0 +1,246 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.finish()</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-finish">
+<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';
+
+var gKeyFrames = { 'marginLeft': ['100px', '200px'] };
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.playbackRate = 0;
+
+ assert_throws({name: 'InvalidStateError'}, function() {
+ animation.finish();
+ });
+}, 'Test exceptions when finishing non-running animation');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames,
+ {duration : 100 * MS_PER_SEC,
+ iterations : Infinity});
+
+ assert_throws({name: 'InvalidStateError'}, function() {
+ animation.finish();
+ });
+}, 'Test exceptions when finishing infinite animation');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.finish();
+
+ assert_equals(animation.currentTime, 100 * MS_PER_SEC,
+ 'After finishing, the currentTime should be set to the end ' +
+ 'of the active duration');
+}, 'Test finishing of animation');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ // 1s past effect end
+ animation.currentTime =
+ animation.effect.getComputedTiming().endTime + 1 * MS_PER_SEC;
+ animation.finish();
+
+ assert_equals(animation.currentTime, 100 * MS_PER_SEC,
+ 'After finishing, the currentTime should be set back to the ' +
+ 'end of the active duration');
+}, 'Test finishing of animation with a current time past the effect end');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.currentTime = 100 * MS_PER_SEC;
+ return animation.finished.then(function() {
+ animation.playbackRate = -1;
+ animation.finish();
+
+ assert_equals(animation.currentTime, 0,
+ 'After finishing a reversed animation the currentTime ' +
+ 'should be set to zero');
+ });
+}, 'Test finishing of reversed animation');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.currentTime = 100 * MS_PER_SEC;
+ return animation.finished.then(function() {
+ animation.playbackRate = -1;
+ animation.currentTime = -1000;
+ animation.finish();
+
+ assert_equals(animation.currentTime, 0,
+ 'After finishing a reversed animation the currentTime ' +
+ 'should be set back to zero');
+ });
+}, 'Test finishing of reversed animation with a current time less than zero');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.pause();
+ return animation.ready.then(function() {
+ animation.finish();
+
+ assert_equals(animation.playState, 'finished',
+ 'The play state of a paused animation should become ' +
+ '"finished" after finish() is called');
+ assert_times_equal(animation.startTime,
+ animation.timeline.currentTime - 100 * MS_PER_SEC,
+ 'The start time of a paused animation should be set ' +
+ 'after calling finish()');
+ });
+}, 'Test finish() while paused');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.pause();
+ // Update playbackRate so we can test that the calculated startTime
+ // respects it
+ animation.playbackRate = 2;
+ // While animation is still pause-pending call finish()
+ animation.finish();
+
+ assert_equals(animation.playState, 'finished',
+ 'The play state of a pause-pending animation should become ' +
+ '"finished" after finish() is called');
+ assert_times_equal(animation.startTime,
+ animation.timeline.currentTime - 100 * MS_PER_SEC / 2,
+ 'The start time of a pause-pending animation should ' +
+ 'be set after calling finish()');
+}, 'Test finish() while pause-pending with positive playbackRate');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.pause();
+ animation.playbackRate = -2;
+ animation.finish();
+
+ assert_equals(animation.playState, 'finished',
+ 'The play state of a pause-pending animation should become ' +
+ '"finished" after finish() is called');
+ assert_equals(animation.startTime, animation.timeline.currentTime,
+ 'The start time of a pause-pending animation should be ' +
+ 'set after calling finish()');
+}, 'Test finish() while pause-pending with negative playbackRate');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ animation.playbackRate = 0.5;
+ animation.finish();
+
+ assert_equals(animation.playState, 'finished',
+ 'The play state of a play-pending animation should become ' +
+ '"finished" after finish() is called');
+ assert_times_equal(animation.startTime,
+ animation.timeline.currentTime - 100 * MS_PER_SEC / 0.5,
+ 'The start time of a play-pending animation should ' +
+ 'be set after calling finish()');
+}, 'Test finish() while play-pending');
+
+// FIXME: Add a test for when we are play-pending without an active timeline.
+// - In that case even after calling finish() we should still be pending but
+// the current time should be updated
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.pause();
+ animation.play();
+ // We are now in the unusual situation of being play-pending whilst having
+ // a resolved start time. Check that finish() still triggers a transition
+ // to the finished state immediately.
+ animation.finish();
+
+ assert_equals(animation.playState, 'finished',
+ 'After aborting a pause then calling finish() the play ' +
+ 'state of an animation should become "finished" immediately');
+ });
+}, 'Test finish() during aborted pause');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ div.style.marginLeft = '10px';
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.finish();
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+
+ assert_equals(marginLeft, 10,
+ 'The computed style should be reset when finish() is ' +
+ 'called');
+ });
+}, 'Test resetting of computed style');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(gKeyFrames, 100 * MS_PER_SEC);
+ var resolvedFinished = false;
+ animation.finished.then(function() {
+ resolvedFinished = true;
+ });
+
+ return animation.ready.then(function() {
+ animation.finish();
+ }).then(function() {
+ assert_true(resolvedFinished,
+ 'Animation.finished should be resolved soon after ' +
+ 'Animation.finish()');
+ });
+}, 'Test finish() resolves finished promise synchronously');
+
+promise_test(function(t) {
+ var effect = new KeyframeEffectReadOnly(null, gKeyFrames, 100 * MS_PER_SEC);
+ var animation = new Animation(effect, document.timeline);
+ var resolvedFinished = false;
+ animation.finished.then(function() {
+ resolvedFinished = true;
+ });
+
+ return animation.ready.then(function() {
+ animation.finish();
+ }).then(function() {
+ assert_true(resolvedFinished,
+ 'Animation.finished should be resolved soon after ' +
+ 'Animation.finish()');
+ });
+}, 'Test finish() resolves finished promise synchronously with an animation ' +
+ 'without a target');
+
+promise_test(function(t) {
+ var effect = new KeyframeEffectReadOnly(null, gKeyFrames, 100 * MS_PER_SEC);
+ var animation = new Animation(effect, document.timeline);
+ animation.play();
+
+ var resolvedFinished = false;
+ animation.finished.then(function() {
+ resolvedFinished = true;
+ });
+
+ return animation.ready.then(function() {
+ animation.currentTime = animation.effect.getComputedTiming().endTime - 1;
+ return waitForAnimationFrames(2);
+ }).then(function() {
+ assert_true(resolvedFinished,
+ 'Animation.finished should be resolved soon after ' +
+ 'Animation finishes normally');
+ });
+}, 'Test normally finished animation resolves finished promise synchronously ' +
+ 'with an animation without a target');
+
+</script>
+</body>
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 000000000..d56de1b03
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/finished.html
@@ -0,0 +1,370 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.finished</title>
+<link rel="help" href="https://w3c.github.io/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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+ return animation.ready.then(function() {
+ 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(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise;
+ animation.finish();
+ return animation.finished.then(function() {
+ 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(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ // Setup callback to run if finished promise is resolved
+ var finishPromiseResolved = false;
+ animation.finished.then(function() {
+ finishPromiseResolved = true;
+ });
+ return animation.ready.then(function() {
+ // Jump to mid-way in interval and pause
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ animation.pause();
+ return animation.ready;
+ }).then(function() {
+ // 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(function() {
+ assert_false(finishPromiseResolved,
+ 'Finished promise should not resolve when paused');
+ });
+}, 'Finished promise does not resolve when paused');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ // Setup callback to run if finished promise is resolved
+ var finishPromiseResolved = false;
+ animation.finished.then(function() {
+ finishPromiseResolved = true;
+ });
+ return animation.ready.then(function() {
+ // 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(function() {
+ assert_false(finishPromiseResolved,
+ 'Finished promise should not resolve when pause-pending');
+ });
+}, 'Finished promise does not resolve when pause-pending');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.finish();
+ return animation.finished.then(function(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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+
+ // Set up listeners on finished promise
+ var retPromise = animation.finished.then(function() {
+ assert_unreached('finished promise was fulfilled');
+ }).catch(function(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 cancelled by calling ' +
+ 'cancel()');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(function() {
+ animation.cancel();
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'A new finished promise should be created when'
+ + ' cancelling a finished animation');
+ });
+}, 'cancelling an already-finished animation replaces the finished promise');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.cancel();
+ // The spec says we still create a new finished promise and reject the old
+ // one even if we're already idle. That behavior might change, but for now
+ // test that we do that.
+ var retPromise = animation.finished.catch(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'finished promise is rejected with AbortError');
+ });
+
+ // Redundant call to cancel();
+ var previousFinishedPromise = animation.finished;
+ animation.cancel();
+ assert_not_equals(animation.finished, previousFinishedPromise,
+ 'A redundant call to cancel() should still generate a new'
+ + ' finished promise');
+ return retPromise;
+}, 'cancelling an idle animation still replaces the finished promise');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ const HALF_DUR = 100 * MS_PER_SEC / 2;
+ const QUARTER_DUR = 100 * MS_PER_SEC / 4;
+ var gotNextFrame = false;
+ var currentTimeBeforeShortening;
+ animation.currentTime = HALF_DUR;
+ return animation.ready.then(function() {
+ currentTimeBeforeShortening = animation.currentTime;
+ animation.effect.timing.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(function() {
+ gotNextFrame = true;
+ });
+
+ return animation.finished;
+ }).then(function() {
+ 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');
+ var previousFinishedPromise = animation.finished;
+ animation.effect.timing.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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var retPromise = animation.ready.then(function() {
+ animation.playbackRate = 0;
+ animation.currentTime = 100 * MS_PER_SEC + 1000;
+ return waitForAnimationFrames(2);
+ });
+
+ animation.finished.then(t.step_func(function() {
+ assert_unreached('finished promise should not resolve when playbackRate ' +
+ 'is zero');
+ }));
+
+ return retPromise;
+}, 'Test finished promise changes when playbackRate == 0');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.playbackRate = -1;
+ return animation.finished;
+ });
+}, 'Test finished promise resolves when reaching to the natural boundary.');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var previousFinishedPromise = animation.finished;
+ animation.finish();
+ return animation.finished.then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var resolvedFinished = false;
+ animation.finished.then(function() {
+ resolvedFinished = true;
+ });
+ return animation.ready.then(function() {
+ animation.finish();
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ }).then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var resolvedFinished = false;
+ animation.finished.then(function() {
+ resolvedFinished = true;
+ });
+
+ return animation.ready.then(function() {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.finish();
+ }).then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.finished.then(t.step_func(function() {
+ assert_unreached('Animation.finished should not be resolved');
+ }));
+
+ return animation.ready.then(function() {
+ 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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.finished.then(t.step_func(function() {
+ 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');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/id.html b/testing/web-platform/tests/web-animations/interfaces/Animation/id.html
new file mode 100644
index 000000000..2fadd5623
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/id.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.id</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-id">
+<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";
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ assert_equals(animation.id, '', 'id for Animation is initially empty');
+}, 'Animation.id initial value');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.id = 'anim';
+
+ assert_equals(animation.id, 'anim', 'animation.id reflects the value set');
+}, 'Animation.id setter');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/oncancel.html b/testing/web-platform/tests/web-animations/interfaces/Animation/oncancel.html
new file mode 100644
index 000000000..b8d9ced61
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/oncancel.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.oncancel</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-oncancel">
+<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";
+
+async_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var finishedTimelineTime;
+ animation.finished.then().catch(function() {
+ finishedTimelineTime = animation.timeline.currentTime;
+ });
+
+ animation.oncancel = t.step_func_done(function(event) {
+ assert_equals(event.currentTime, null,
+ 'event.currentTime should be null');
+ assert_equals(event.timelineTime, finishedTimelineTime,
+ 'event.timelineTime should equal to the animation timeline ' +
+ 'when finished promise is rejected');
+ });
+
+ animation.cancel();
+}, 'oncancel event is fired when animation.cancel() is called.');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/onfinish.html b/testing/web-platform/tests/web-animations/interfaces/Animation/onfinish.html
new file mode 100644
index 000000000..50e5bed6b
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/onfinish.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.onfinish</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-onfinish">
+<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";
+
+async_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var finishedTimelineTime;
+ animation.finished.then(function() {
+ finishedTimelineTime = animation.timeline.currentTime;
+ });
+
+ animation.onfinish = t.step_func_done(function(event) {
+ assert_equals(event.currentTime, 0,
+ 'event.currentTime should be zero');
+ assert_equals(event.timelineTime, finishedTimelineTime,
+ 'event.timelineTime should equal to the animation timeline ' +
+ 'when finished promise is resolved');
+ });
+
+ animation.playbackRate = -1;
+}, 'onfinish event is fired when the currentTime < 0 and ' +
+ 'the playbackRate < 0');
+
+async_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ var finishedTimelineTime;
+ animation.finished.then(function() {
+ finishedTimelineTime = animation.timeline.currentTime;
+ });
+
+ animation.onfinish = t.step_func_done(function(event) {
+ assert_equals(event.currentTime, 100 * MS_PER_SEC,
+ 'event.currentTime should be the effect end');
+ assert_equals(event.timelineTime, finishedTimelineTime,
+ 'event.timelineTime should equal to the animation timeline ' +
+ 'when finished promise is resolved');
+ });
+
+ animation.currentTime = 100 * MS_PER_SEC;
+}, 'onfinish event is fired when the currentTime > 0 and ' +
+ 'the playbackRate > 0');
+
+async_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ var finishedTimelineTime;
+ animation.finished.then(function() {
+ finishedTimelineTime = animation.timeline.currentTime;
+ });
+
+ animation.onfinish = t.step_func_done(function(event) {
+ assert_equals(event.currentTime, 100 * MS_PER_SEC,
+ 'event.currentTime should be the effect end');
+ assert_equals(event.timelineTime, finishedTimelineTime,
+ 'event.timelineTime should equal to the animation timeline ' +
+ 'when finished promise is resolved');
+ });
+
+ animation.finish();
+}, 'onfinish event is fired when animation.finish() is called');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ animation.onfinish = function(event) {
+ assert_unreached('onfinish event should not be fired');
+ };
+
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ animation.pause();
+
+ return animation.ready.then(function() {
+ animation.currentTime = 100 * MS_PER_SEC;
+ return waitForAnimationFrames(2);
+ });
+}, 'onfinish event is not fired when paused');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.onfinish = function(event) {
+ assert_unreached('onfinish event should not be fired');
+ };
+
+ return animation.ready.then(function() {
+ animation.playbackRate = 0;
+ animation.currentTime = 100 * MS_PER_SEC;
+ return waitForAnimationFrames(2);
+ });
+}, 'onfinish event is not fired when the playbackRate is zero');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.onfinish = function(event) {
+ assert_unreached('onfinish event should not be fired');
+ };
+
+ return animation.ready.then(function() {
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.currentTime = 100 * MS_PER_SEC / 2;
+ return waitForAnimationFrames(2);
+ });
+}, 'onfinish event is not fired when the animation falls out ' +
+ 'finished state immediately');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/pause.html b/testing/web-platform/tests/web-animations/interfaces/Animation/pause.html
new file mode 100644
index 000000000..bcfaf4a9b
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/pause.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.pause()</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-pause">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 1000 * MS_PER_SEC);
+ var previousCurrentTime = animation.currentTime;
+
+ return animation.ready.then(waitForAnimationFrames(1)).then(function() {
+ assert_true(animation.currentTime >= previousCurrentTime,
+ 'currentTime is initially increasing');
+ animation.pause();
+ return animation.ready;
+ }).then(function() {
+ previousCurrentTime = animation.currentTime;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(animation.currentTime, previousCurrentTime,
+ 'currentTime does not increase after calling pause()');
+ });
+}, 'pause() a running animation');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 1000 * MS_PER_SEC);
+
+ // Go to idle state then pause
+ animation.cancel();
+ animation.pause();
+
+ assert_equals(animation.currentTime, 0, 'currentTime is set to 0');
+ assert_equals(animation.startTime, null, 'startTime is not set');
+ assert_equals(animation.playState, 'pending', 'initially pause-pending');
+
+ // Check it still resolves as expected
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'paused',
+ 'resolves to paused state asynchronously');
+ assert_equals(animation.currentTime, 0,
+ 'keeps the initially set currentTime');
+ });
+}, 'pause() from idle');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 1000 * MS_PER_SEC);
+ animation.cancel();
+ animation.playbackRate = -1;
+ animation.pause();
+
+ assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
+ 'currentTime is set to the effect end');
+
+ return animation.ready.then(function() {
+ assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
+ 'keeps the initially set currentTime');
+ });
+}, 'pause() from idle with a negative playbackRate');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, {duration: 1000 * MS_PER_SEC,
+ iterations: Infinity});
+ animation.cancel();
+ animation.playbackRate = -1;
+
+ assert_throws('InvalidStateError',
+ function () { animation.pause(); },
+ 'Expect InvalidStateError exception on calling pause() ' +
+ 'from idle with a negative playbackRate and ' +
+ 'infinite-duration animation');
+}, 'pause() from idle with a negative playbackRate and endless effect');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 1000 * MS_PER_SEC);
+ return animation.ready
+ .then(function(animation) {
+ animation.finish();
+ animation.pause();
+ return animation.ready;
+ }).then(function(animation) {
+ assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
+ 'currentTime after pausing finished animation');
+ });
+}, 'pause() on a finished animation');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/play.html b/testing/web-platform/tests/web-animations/interfaces/Animation/play.html
new file mode 100644
index 000000000..767d8df17
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/play.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.play()</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-play">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({ transform: ['none', 'translate(10px)']},
+ { duration : 100 * MS_PER_SEC,
+ iterations : Infinity});
+ return animation.ready.then(function() {
+ // Seek to a time outside the active range so that play() will have to
+ // snap back to the start
+ animation.currentTime = -5 * MS_PER_SEC;
+ animation.playbackRate = -1;
+
+ assert_throws('InvalidStateError',
+ function () { animation.play(); },
+ 'Expected InvalidStateError exception on calling play() ' +
+ 'with a negative playbackRate and infinite-duration ' +
+ 'animation');
+ });
+}, 'play() throws when seeking an infinite-duration animation played in ' +
+ 'reverse');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/playState.html b/testing/web-platform/tests/web-animations/interfaces/Animation/playState.html
new file mode 100644
index 000000000..15af526cd
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/playState.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.playState</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-playstate">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ assert_equals(animation.playState, 'pending');
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ });
+}, 'Animation.playState reports \'pending\'->\'running\' when initially ' +
+ 'played');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.pause();
+
+ assert_equals(animation.playState, 'pending');
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'paused');
+ });
+}, 'Animation.playState reports \'pending\'->\'paused\' when pausing');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.cancel();
+ assert_equals(animation.playState, 'idle');
+}, 'Animation.playState is \'idle\' when canceled.');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.cancel();
+ animation.currentTime = 50 * MS_PER_SEC;
+ assert_equals(animation.playState, 'paused',
+ 'After seeking an idle animation, it is effectively paused');
+}, 'Animation.playState is \'paused\' after cancelling an animation, ' +
+ 'seeking it makes it paused');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/playbackRate.html b/testing/web-platform/tests/web-animations/interfaces/Animation/playbackRate.html
new file mode 100644
index 000000000..c923df6b4
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/playbackRate.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.playbackRate</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-playbackrate">
+<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";
+
+function assert_playbackrate(animation,
+ previousAnimationCurrentTime,
+ previousTimelineCurrentTime,
+ description) {
+ var accuracy = 0.001; /* accuracy of DOMHighResTimeStamp */
+ var animationCurrentTimeDifference =
+ animation.currentTime - previousAnimationCurrentTime;
+ var timelineCurrentTimeDifference =
+ animation.timeline.currentTime - previousTimelineCurrentTime;
+
+ assert_approx_equals(animationCurrentTimeDifference,
+ timelineCurrentTimeDifference * animation.playbackRate,
+ accuracy,
+ description);
+}
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(null, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.currentTime = 7 * MS_PER_SEC; // ms
+ animation.playbackRate = 0.5;
+
+ assert_equals(animation.currentTime, 7 * MS_PER_SEC,
+ 'Reducing Animation.playbackRate should not change the currentTime ' +
+ 'of a playing animation');
+ animation.playbackRate = 2;
+ assert_equals(animation.currentTime, 7 * MS_PER_SEC,
+ 'Increasing Animation.playbackRate should not change the currentTime ' +
+ 'of a playing animation');
+ });
+}, 'Test the initial effect of setting playbackRate on currentTime');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(null, 100 * MS_PER_SEC);
+ animation.playbackRate = 2;
+ var previousTimelineCurrentTime;
+ var previousAnimationCurrentTime;
+ return animation.ready.then(function() {
+ previousAnimationCurrentTime = animation.currentTime;
+ previousTimelineCurrentTime = animation.timeline.currentTime;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_playbackrate(animation,
+ previousAnimationCurrentTime,
+ previousTimelineCurrentTime,
+ 'animation.currentTime should be 2 times faster than timeline.');
+ });
+}, 'Test the effect of setting playbackRate on currentTime');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate(null, 100 * MS_PER_SEC);
+ animation.playbackRate = 2;
+ var previousTimelineCurrentTime;
+ var previousAnimationCurrentTime;
+ return animation.ready.then(function() {
+ previousAnimationCurrentTime = animation.currentTime;
+ previousTimelineCurrentTime = animation.timeline.currentTime;
+ animation.playbackRate = 1;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(animation.playbackRate, 1,
+ 'sanity check: animation.playbackRate is still 1.');
+ assert_playbackrate(animation,
+ previousAnimationCurrentTime,
+ previousTimelineCurrentTime,
+ 'animation.currentTime should be the same speed as timeline now.');
+ });
+}, 'Test the effect of setting playbackRate while playing animation');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/ready.html b/testing/web-platform/tests/web-animations/interfaces/Animation/ready.html
new file mode 100644
index 000000000..815fe3da7
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/ready.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.ready</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-ready">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ var originalReadyPromise = animation.ready;
+ var pauseReadyPromise;
+
+ return animation.ready.then(function() {
+ assert_equals(animation.ready, originalReadyPromise,
+ 'Ready promise is the same object when playing completes');
+ animation.pause();
+ assert_not_equals(animation.ready, originalReadyPromise,
+ 'A new ready promise is created when pausing');
+ pauseReadyPromise = animation.ready;
+ // Wait for the promise to fulfill since if we abort the pause the ready
+ // promise object is reused.
+ return animation.ready;
+ }).then(function() {
+ animation.play();
+ assert_not_equals(animation.ready, pauseReadyPromise,
+ 'A new ready promise is created when playing');
+ });
+}, 'A new ready promise is created when play()/pause() is called');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ return animation.ready.then(function() {
+ var promiseBeforeCallingPlay = animation.ready;
+ animation.play();
+ assert_equals(animation.ready, promiseBeforeCallingPlay,
+ 'Ready promise has same object identity after redundant call'
+ + ' to play()');
+ });
+}, 'Redundant calls to play() do not generate new ready promise objects');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ return animation.ready.then(function(resolvedAnimation) {
+ assert_equals(resolvedAnimation, animation,
+ 'Object identity of Animation passed to Promise callback'
+ + ' matches the Animation object owning the Promise');
+ });
+}, 'The ready promise is fulfilled with its Animation');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+
+ var retPromise = animation.ready.then(function() {
+ assert_unreached('ready promise was fulfilled');
+ }).catch(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ });
+
+ animation.cancel();
+
+ return retPromise;
+}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ + ' calling cancel()');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ animation.pause();
+ // Set up listeners on pause-pending ready promise
+ var retPromise = animation.ready.then(function() {
+ assert_unreached('ready promise was fulfilled');
+ }).catch(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ });
+ animation.cancel();
+ return retPromise;
+ });
+}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ + ' calling cancel()');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/reverse.html b/testing/web-platform/tests/web-animations/interfaces/Animation/reverse.html
new file mode 100644
index 000000000..555226111
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/reverse.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.reverse()</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-reverse">
+<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(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
+ iterations: Infinity});
+
+ // Wait a frame because if currentTime is still 0 when we call
+ // reverse(), it will throw (per spec).
+ return animation.ready.then(waitForAnimationFrames(1)).then(function() {
+ assert_greater_than_equal(animation.currentTime, 0,
+ 'currentTime expected to be greater than 0, one frame after starting');
+ animation.currentTime = 50 * MS_PER_SEC;
+ var previousPlaybackRate = animation.playbackRate;
+ animation.reverse();
+ assert_equals(animation.playbackRate, -previousPlaybackRate,
+ 'playbackRate should be inverted');
+ });
+}, 'reverse() inverts playbackRate');
+
+promise_test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
+ iterations: Infinity});
+ animation.currentTime = 50 * MS_PER_SEC;
+ animation.pause();
+
+ return animation.ready.then(function() {
+ animation.reverse();
+ return animation.ready;
+ }).then(function() {
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" after reverse()');
+ });
+}, 'reverse() starts to play when pausing animation');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.currentTime = 50 * MS_PER_SEC;
+ animation.reverse();
+
+ assert_equals(animation.currentTime, 50 * MS_PER_SEC,
+ 'reverse() should not change the currentTime ' +
+ 'if the currentTime is in the middle of animation duration');
+}, 'reverse() maintains the same currentTime');
+
+test(function(t) {
+ var div = createDiv(t);
+ var 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');
+}, 'reverse() when playbackRate > 0 and currentTime > effect end');
+
+test(function(t) {
+ var div = createDiv(t);
+ var 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');
+}, 'reverse() when playbackRate > 0 and currentTime < 0');
+
+test(function(t) {
+ var div = createDiv(t);
+ var 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');
+}, 'reverse() when playbackRate < 0 and currentTime < 0');
+
+test(function(t) {
+ var div = createDiv(t);
+ var 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');
+}, 'reverse() when playbackRate < 0 and currentTime > effect end');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
+ iterations: Infinity});
+ animation.currentTime = -200 * MS_PER_SEC;
+
+ assert_throws('InvalidStateError',
+ function () { animation.reverse(); },
+ 'reverse() should throw InvalidStateError ' +
+ 'if the playbackRate > 0 and the currentTime < 0 ' +
+ 'and the target effect is positive infinity');
+}, 'reverse() when playbackRate > 0 and currentTime < 0 ' +
+ 'and the target effect end is positive infinity');
+
+test(function(t) {
+ var div = createDiv(t);
+ var 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');
+}, 'reverse() when playbackRate < 0 and currentTime < 0 ' +
+ 'and the target effect end is positive infinity');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation = div.animate({}, 100 * MS_PER_SEC);
+ animation.playbackRate = 0;
+ animation.currentTime = 50 * MS_PER_SEC;
+ animation.reverse();
+
+ 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');
+ t.done();
+}, 'reverse() when playbackRate == 0');
+
+test(function(t) {
+ var div = createDiv(t);
+ var animation =
+ new Animation(new KeyframeEffect(div, null, 100 * MS_PER_SEC), null);
+
+ assert_throws('InvalidStateError', function() { animation.reverse(); });
+}, 'Reversing an animation without an active timeline throws an ' +
+ 'InvalidStateError');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/startTime.html b/testing/web-platform/tests/web-animations/interfaces/Animation/startTime.html
new file mode 100644
index 000000000..6dfd7cf29
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/startTime.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.startTime tests</title>
+<link rel="help"
+href="https://w3c.github.io/web-animations/#dom-animation-starttime">
+<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';
+
+test(function(t) {
+ var animation = new Animation(new KeyframeEffect(createDiv(t), null),
+ document.timeline);
+ assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'startTime of a newly created (idle) animation is unresolved');
+
+test(function(t) {
+ var animation = new Animation(new KeyframeEffect(createDiv(t), null),
+ document.timeline);
+ animation.play();
+ assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'startTime of a play-pending animation is unresolved');
+
+test(function(t) {
+ var animation = new Animation(new KeyframeEffect(createDiv(t), null),
+ document.timeline);
+ animation.pause();
+ assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'startTime of a pause-pending animation is unresolved');
+
+test(function(t) {
+ var animation = createDiv(t).animate(null);
+ assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'startTime of a play-pending animation created using Element.animate'
+ + ' shortcut is unresolved');
+
+promise_test(function(t) {
+ var animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ return animation.ready.then(function() {
+ assert_greater_than(animation.startTime, 0, 'startTime when running');
+ });
+}, 'startTime is resolved when running');
+
+test(function(t) {
+ var animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ animation.cancel();
+ assert_equals(animation.startTime, null);
+ assert_equals(animation.currentTime, null);
+}, 'startTime and currentTime are unresolved when animation is cancelled');
+
+</script>
+</body>