summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/timing-model/animation-effects
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/web-animations/timing-model/animation-effects
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/web-animations/timing-model/animation-effects')
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html141
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animation-effects/current-iteration.html620
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animation-effects/local-time.html29
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html149
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animation-effects/simple-iteration-progress.html600
5 files changed, 1539 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html b/testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html
new file mode 100644
index 0000000000..a2feb2323c
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/active-time.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Active time</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#calculating-the-active-time">
+<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(t => {
+ const tests = [ { fill: 'none', progress: null },
+ { fill: 'backwards', progress: 0 },
+ { fill: 'forwards', progress: null },
+ { fill: 'both', progress: 0 } ];
+ for (const test of tests) {
+ const anim = createDiv(t).animate(null, { delay: 1, fill: test.fill });
+ assert_equals(anim.effect.getComputedTiming().progress, test.progress,
+ `Progress in before phase when using '${test.fill}' fill`);
+ }
+}, 'Active time in before phase');
+
+test(t => {
+ const anim = createDiv(t).animate(null, 1000);
+ anim.currentTime = 500;
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5);
+}, 'Active time in active phase and no start delay is the local time');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000, delay: 500 });
+ anim.currentTime = 1000;
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5);
+}, 'Active time in active phase and positive start delay is the local time'
+ + ' minus the start delay');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000, delay: -500 });
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5);
+}, 'Active time in active phase and negative start delay is the local time'
+ + ' minus the start delay');
+
+test(t => {
+ const anim = createDiv(t).animate(null);
+ assert_equals(anim.effect.getComputedTiming().progress, null);
+}, 'Active time in after phase with no fill is unresolved');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { fill: 'backwards' });
+ assert_equals(anim.effect.getComputedTiming().progress, null);
+}, 'Active time in after phase with backwards-only fill is unresolved');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500, // Should have no effect
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 2);
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3);
+}, 'Active time in after phase with forwards fill is the active duration');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 0,
+ iterations: Infinity,
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity);
+ assert_equals(anim.effect.getComputedTiming().progress, 1);
+}, 'Active time in after phase with forwards fill, zero-duration, and '
+ + ' infinite iteration count is the active duration');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500,
+ endDelay: 4000,
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 2);
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3);
+}, 'Active time in after phase with forwards fill and positive end delay'
+ + ' is the active duration');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500,
+ endDelay: -800,
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 1);
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5);
+}, 'Active time in after phase with forwards fill and negative end delay'
+ + ' is the active duration + end delay');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500,
+ endDelay: -2500,
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 0);
+ assert_equals(anim.effect.getComputedTiming().progress, 0);
+}, 'Active time in after phase with forwards fill and negative end delay'
+ + ' greater in magnitude than the active duration is zero');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500,
+ endDelay: -4000,
+ fill: 'forwards' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 0);
+ assert_equals(anim.effect.getComputedTiming().progress, 0);
+}, 'Active time in after phase with forwards fill and negative end delay'
+ + ' greater in magnitude than the sum of the active duration and start delay'
+ + ' is zero');
+
+test(t => {
+ const anim = createDiv(t).animate(null, { duration: 1000,
+ iterations: 2.3,
+ delay: 500,
+ fill: 'both' });
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, 2);
+ assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3);
+}, 'Active time in after phase with \'both\' fill is the active duration');
+
+test(t => {
+ // Create an effect with a non-zero duration so we ensure we're not just
+ // testing the after-phase behavior.
+ const effect = new KeyframeEffect(null, null, 1);
+ assert_equals(effect.getComputedTiming().progress, null);
+}, 'Active time when the local time is unresolved, is unresolved');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animation-effects/current-iteration.html b/testing/web-platform/tests/web-animations/timing-model/animation-effects/current-iteration.html
new file mode 100644
index 0000000000..24464ce05f
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/current-iteration.html
@@ -0,0 +1,620 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Current iteration</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#current-iteration">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<script src="../../resources/effect-tests.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+function runTests(tests, description) {
+ for (const currentTest of tests) {
+ let testParams = Object.entries(currentTest.input)
+ .map(([attr, value]) => `${attr}:${value}`)
+ .join(' ');
+ if (currentTest.playbackRate !== undefined) {
+ testParams += ` playbackRate:${currentTest.playbackRate}`;
+ }
+
+ test(t => {
+ const div = createDiv(t);
+ const anim = div.animate({}, currentTest.input);
+ if (currentTest.playbackRate !== undefined) {
+ anim.playbackRate = currentTest.playbackRate;
+ }
+
+ assert_computed_timing_for_each_phase(
+ anim,
+ 'currentIteration',
+ { before: currentTest.before,
+ activeBoundary: currentTest.active,
+ after: currentTest.after },
+ );
+ }, `${description}: ${testParams}`);
+ }
+}
+
+async_test(t => {
+ const div = createDiv(t);
+ const anim = div.animate({ opacity: [ 0, 1 ] }, { delay: 1 });
+ assert_equals(anim.effect.getComputedTiming().currentIteration, null);
+ anim.finished.then(t.step_func(() => {
+ assert_equals(anim.effect.getComputedTiming().currentIteration, null);
+ t.done();
+ }));
+}, 'Test currentIteration during before and after phase when fill is none');
+
+
+// --------------------------------------------------------------------
+//
+// Zero iteration duration tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: 2
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: 2
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: 2
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: 3
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: 3
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: 3
+ }
+], 'Test zero iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is an integer
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 2
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 2
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3
+ }
+], 'Test integer iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is a fraction
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 3
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 3
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2,
+ after: 5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: 6
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3,
+ after: 6
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3
+ }
+], 'Test fractional iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is Infinity
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: Infinity
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ after: Infinity
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 2,
+ active: 2
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ after: Infinity
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 3,
+ active: 3
+ }
+], 'Test infinity iterations');
+
+
+// --------------------------------------------------------------------
+//
+// End delay tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: 50 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -200 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: 50 },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: 1,
+ iterationStart: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 2,
+ active: 2,
+ after: 2
+ },
+
+ {
+ input: { iterations: 1,
+ iterationStart: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 2,
+ active: 2,
+ after: 2
+ },
+], 'Test end delay');
+
+
+// --------------------------------------------------------------------
+//
+// Negative playback rate tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { duration: 1,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 1,
+ delay: 1,
+ iterations: 2,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ active: 1,
+ after: 1
+ },
+
+ {
+ input: { duration: 0,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 0,
+ iterations: 0,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ after: 0
+ },
+], 'Test negative playback rate');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animation-effects/local-time.html b/testing/web-platform/tests/web-animations/timing-model/animation-effects/local-time.html
new file mode 100644
index 0000000000..79437d9f54
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/local-time.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Local time</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#local-time">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+test(t => {
+ const anim = createDiv(t).animate(null, 10 * MS_PER_SEC);
+ for (const seconds of [-1, 0, 5, 10, 20]) {
+ anim.currentTime = seconds * MS_PER_SEC;
+ assert_equals(
+ anim.effect.getComputedTiming().localTime,
+ seconds * MS_PER_SEC
+ );
+ }
+}, 'Local time is current time for animation effects associated with an animation');
+
+test(t => {
+ const effect = new KeyframeEffect(createDiv(t), null, 10 * MS_PER_SEC);
+ assert_equals(effect.getComputedTiming().localTime, null);
+}, 'Local time is unresolved for animation effects not associated with an animation');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html b/testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html
new file mode 100644
index 0000000000..a33dbf517e
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/phases-and-states.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Phases and states</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#animation-effect-phases-and-states">
+<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';
+
+// --------------------------------------------------------------------
+//
+// Phases
+//
+// --------------------------------------------------------------------
+
+test(t => {
+ const animation = createDiv(t).animate(null, 1);
+
+ for (const test of [{ currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'active' },
+ { currentTime: 1, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for a simple animation effect');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1, delay: 1 });
+
+ for (const test of [{ currentTime: 0, phase: 'before' },
+ { currentTime: 1, phase: 'active' },
+ { currentTime: 2, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a positive start delay');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1, delay: -1 });
+
+ for (const test of [{ currentTime: -2, phase: 'before' },
+ { currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative start delay');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1, endDelay: 1 });
+
+ for (const test of [{ currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'active' },
+ { currentTime: 1, phase: 'after' },
+ { currentTime: 2, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a positive end delay');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 2, endDelay: -1 });
+
+ for (const test of [{ currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'active' },
+ { currentTime: 0.9, phase: 'active' },
+ { currentTime: 1, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative end delay lesser'
+ + ' in magnitude than the active duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1, endDelay: -1 });
+
+ for (const test of [{ currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'after' },
+ { currentTime: 1, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative end delay equal'
+ + ' in magnitude to the active duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1, endDelay: -2 });
+
+ for (const test of [{ currentTime: -2, phase: 'before' },
+ { currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative end delay'
+ + ' greater in magnitude than the active duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 2,
+ delay: 1,
+ endDelay: -1 });
+
+ for (const test of [{ currentTime: 0, phase: 'before' },
+ { currentTime: 1, phase: 'active' },
+ { currentTime: 2, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a positive start delay'
+ + ' and a negative end delay lesser in magnitude than the active duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1,
+ delay: -1,
+ endDelay: -1 });
+
+ for (const test of [{ currentTime: -2, phase: 'before' },
+ { currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative start delay'
+ + ' and a negative end delay equal in magnitude to the active duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, { duration: 1,
+ delay: -1,
+ endDelay: -2 });
+
+ for (const test of [{ currentTime: -3, phase: 'before' },
+ { currentTime: -2, phase: 'before' },
+ { currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for an animation effect with a negative start delay'
+ + ' and a negative end delay equal greater in magnitude than the active'
+ + ' duration');
+
+test(t => {
+ const animation = createDiv(t).animate(null, 1);
+ animation.playbackRate = -1;
+
+ for (const test of [{ currentTime: -1, phase: 'before' },
+ { currentTime: 0, phase: 'before' },
+ { currentTime: 1, phase: 'active' },
+ { currentTime: 2, phase: 'after' }]) {
+ assert_phase_at_time(animation, test.phase, test.currentTime);
+ }
+}, 'Phase calculation for a simple animation effect with negative playback'
+ + ' rate');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animation-effects/simple-iteration-progress.html b/testing/web-platform/tests/web-animations/timing-model/animation-effects/simple-iteration-progress.html
new file mode 100644
index 0000000000..3c42f79a71
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animation-effects/simple-iteration-progress.html
@@ -0,0 +1,600 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Simple iteration progress</title>
+<link rel="help"
+ href="https://drafts.csswg.org/web-animations/#simple-iteration-progress">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<script src="../../resources/effect-tests.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+function runTests(tests, description) {
+ for (const currentTest of tests) {
+ let testParams = Object.entries(currentTest.input)
+ .map(([attr, value]) => `${attr}:${value}`)
+ .join(' ');
+ if (currentTest.playbackRate !== undefined) {
+ testParams += ` playbackRate:${currentTest.playbackRate}`;
+ }
+
+ test(t => {
+ const div = createDiv(t);
+ const anim = div.animate({}, currentTest.input);
+ if (currentTest.playbackRate !== undefined) {
+ anim.playbackRate = currentTest.playbackRate;
+ }
+
+ assert_computed_timing_for_each_phase(
+ anim,
+ 'progress',
+ { before: currentTest.before,
+ activeBoundary: currentTest.active,
+ after: currentTest.after },
+ );
+ }, `${description}: ${testParams}`);
+ }
+}
+
+
+// --------------------------------------------------------------------
+//
+// Zero iteration duration tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 0,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0
+ }
+], 'Test zero iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is an integer
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ }
+], 'Test integer iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is a fraction
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5,
+ after: 1
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 3.5,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ }
+], 'Test fractional iterations');
+
+
+// --------------------------------------------------------------------
+//
+// Tests where the iteration count is Infinity
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 0,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 2.5,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0.5,
+ active: 0.5
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: 0,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ after: 1
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: 100,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ },
+
+ {
+ input: { iterations: Infinity,
+ iterationStart: 3,
+ duration: Infinity,
+ delay: 1,
+ fill: 'both' },
+ before: 0,
+ active: 0
+ }
+], 'Test infinity iterations');
+
+
+// --------------------------------------------------------------------
+//
+// End delay tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: 50 },
+ before: 0,
+ active: 0,
+ after: 1
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 0,
+ active: 0,
+ after: 0.5
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -200 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: 50 },
+ before: 0.5,
+ active: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 0.5,
+ active: 0.5,
+ after: 0
+ },
+
+ {
+ input: { iterationStart: 0.5,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0.5,
+ active: 0.5,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+
+ {
+ input: { iterations: 1,
+ iterationStart: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -50 },
+ before: 0,
+ active: 0,
+ after: 0.5
+ },
+
+ {
+ input: { iterations: 1,
+ iterationStart: 2,
+ duration: 100,
+ delay: 1,
+ fill: 'both',
+ endDelay: -100 },
+ before: 0,
+ active: 0,
+ after: 0
+ },
+], 'Test end delay');
+
+
+// --------------------------------------------------------------------
+//
+// Negative playback rate tests
+//
+// --------------------------------------------------------------------
+
+runTests([
+ {
+ input: { duration: 1,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ active: 1,
+ after: 1
+ },
+
+ {
+ input: { duration: 0,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ after: 1
+ },
+
+ {
+ input: { duration: 0,
+ iterations: 0,
+ delay: 1,
+ fill: 'both' },
+ playbackRate: -1,
+ before: 0,
+ after: 0
+ },
+], 'Test negative playback rate');
+
+</script>
+</body>