summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-easing
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/css/css-easing
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-easing')
-rw-r--r--testing/web-platform/tests/css/css-easing/META.yml4
-rw-r--r--testing/web-platform/tests/css/css-easing/cubic-bezier-timing-functions-output.html224
-rw-r--r--testing/web-platform/tests/css/css-easing/linear-timing-functions-output.tentative.html100
-rw-r--r--testing/web-platform/tests/css/css-easing/linear-timing-functions-syntax.tentative.html39
-rw-r--r--testing/web-platform/tests/css/css-easing/step-timing-functions-output.html318
-rw-r--r--testing/web-platform/tests/css/css-easing/step-timing-functions-syntax.html34
-rw-r--r--testing/web-platform/tests/css/css-easing/testcommon.js65
-rw-r--r--testing/web-platform/tests/css/css-easing/timing-functions-syntax-computed.html36
-rw-r--r--testing/web-platform/tests/css/css-easing/timing-functions-syntax-invalid.html26
-rw-r--r--testing/web-platform/tests/css/css-easing/timing-functions-syntax-valid.html35
10 files changed, 881 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-easing/META.yml b/testing/web-platform/tests/css/css-easing/META.yml
new file mode 100644
index 0000000000..2c412b40f0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/META.yml
@@ -0,0 +1,4 @@
+spec: https://drafts.csswg.org/css-easing/
+suggested_reviewers:
+ - birtles
+ - BorisChiou
diff --git a/testing/web-platform/tests/css/css-easing/cubic-bezier-timing-functions-output.html b/testing/web-platform/tests/css/css-easing/cubic-bezier-timing-functions-output.html
new file mode 100644
index 0000000000..4e14ef3bb3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/cubic-bezier-timing-functions-output.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="assert"
+content="This test checks the output of Cubic Bézier functions" />
+<title>Tests for the output of Cubic Bézier timing functions</title>
+<link rel="help"
+href="https://drafts.csswg.org/css-easing/#cubic-bezier-timing-functions">
+<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';
+
+// Precision of major rendering engines' layout systems.
+const epsilon = 0.02;
+
+function assert_style_left_at(animation, time, easingFunction) {
+ animation.currentTime = time;
+ var portion = time / animation.effect.getTiming()['duration'];
+ assert_approx_equals(pxToNum(getComputedStyle(animation.effect.target).left),
+ easingFunction(portion) * 100,
+ epsilon,
+ 'The left of the animation should be approximately ' +
+ easingFunction(portion) * 100 + ' at ' + time + 'ms');
+}
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate(
+ // http://cubic-bezier.com/#.5,1,.5,0
+ [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+ var keyframeEasing = function(x) {
+ assert_greater_than_equal(x, 0.0,
+ 'This function should be called in [0, 1.0] range');
+ assert_less_than_equal(x, 1.0,
+ 'This function should be called in [0, 1.0] range');
+ return cubicBezier(0.5, 1, 0.5, 0)(x);
+ }
+ var keyframeEasingExtrapolated = function(x) {
+ assert_greater_than(x, 1.0,
+ 'This function should be called in (1.0, infinity) range');
+ // p3x + (p2y - p3y) / (p2x - p3x) * (x - p3x)
+ return 1.0 + (0 - 1) / (0.5 - 1) * (x - 1.0);
+ }
+ var effectEasing = function(x) {
+ return cubicBezier(0, 1.5, 1, 1.5)(x);
+ }
+
+ // The effect-easing produces values greater than 1 in (0.23368794, 1)
+ assert_style_left_at(anim, 0, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+ assert_style_left_at(anim, 230, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+ assert_style_left_at(anim, 240, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ // Near the extreme point of the effect-easing function
+ assert_style_left_at(anim, 700, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ assert_style_left_at(anim, 990, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ assert_style_left_at(anim, 1000, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+}, 'cubic-bezier easing with input progress greater than 1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate(
+ // http://cubic-bezier.com/#0,1.5,1,1.5
+ [ { left: '0px', easing: 'cubic-bezier(0, 1.5, 1, 1.5)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+ var easing = function(x) {
+ assert_greater_than_equal(x, 0.0,
+ 'This function should be called in [0, 1.0] range');
+ assert_less_than_equal(x, 1.0,
+ 'This function should be called in [0, 1.0] range');
+ return cubicBezier(0, 1.5, 1, 1.5)(x);
+ }
+ var easingExtrapolated = function(x) {
+ assert_greater_than(x, 1.0,
+ 'This function should be called in negative range');
+ // For cubic-bezier(0, 1.5, 1, 1.5), the tangent at the
+ // endpoint (x = 1.0) is infinity so we should just return 1.0.
+ return 1.0;
+ }
+
+ // The effect-easing produces values greater than 1 in (0.23368794, 1)
+ assert_style_left_at(anim, 0, function(x) {
+ return easing(easing(x))
+ });
+ assert_style_left_at(anim, 230, function(x) {
+ return easing(easing(x))
+ });
+ assert_style_left_at(anim, 240, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ // Near the extreme point of the effect-easing function
+ assert_style_left_at(anim, 700, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ assert_style_left_at(anim, 990, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ assert_style_left_at(anim, 1000, function(x) {
+ return easing(easing(x))
+ });
+}, 'cubic-bezier easing with input progress greater than 1 and where the ' +
+ 'tangent on the upper boundary is infinity');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate(
+ // http://cubic-bezier.com/#.5,1,.5,0
+ [ { left: '0px', easing: 'cubic-bezier(0.5, 1, 0.5, 0)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+ var keyframeEasing = function(x) {
+ assert_greater_than_equal(x, 0.0,
+ 'This function should be called in [0, 1.0] range');
+ assert_less_than_equal(x, 1.0,
+ 'This function should be called in [0, 1.0] range');
+ return cubicBezier(0.5, 1, 0.5, 0)(x);
+ }
+ var keyframeEasingExtrapolated = function(x) {
+ assert_less_than(x, 0.0,
+ 'This function should be called in negative range');
+ // p0x + (p1y - p0y) / (p1x - p0x) * (x - p0x)
+ return (1 / 0.5) * x;
+ }
+ var effectEasing = function(x) {
+ return cubicBezier(0, -0.5, 1, -0.5)(x);
+ }
+
+ // The effect-easing produces negative values in (0, 0.766312060)
+ assert_style_left_at(anim, 0, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+ assert_style_left_at(anim, 10, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ // Near the extreme point of the effect-easing function
+ assert_style_left_at(anim, 300, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ assert_style_left_at(anim, 750, function(x) {
+ return keyframeEasingExtrapolated(effectEasing(x));
+ });
+ assert_style_left_at(anim, 770, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+ assert_style_left_at(anim, 1000, function(x) {
+ return keyframeEasing(effectEasing(x));
+ });
+}, 'cubic-bezier easing with input progress less than 0');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate(
+ // http://cubic-bezier.com/#0,-0.5,1,-0.5
+ [ { left: '0px', easing: 'cubic-bezier(0, -0.5, 1, -0.5)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+ var easing = function(x) {
+ assert_greater_than_equal(x, 0.0,
+ 'This function should be called in [0, 1.0] range');
+ assert_less_than_equal(x, 1.0,
+ 'This function should be called in [0, 1.0] range');
+ return cubicBezier(0, -0.5, 1, -0.5)(x);
+ }
+ var easingExtrapolated = function(x) {
+ assert_less_than(x, 0.0,
+ 'This function should be called in negative range');
+ // For cubic-bezier(0, -0.5, 1, -0.5), the tangent at the
+ // endpoint (x = 0.0) is infinity so we should just return 0.0.
+ return 0.0;
+ }
+
+ // The effect-easing produces negative values in (0, 0.766312060)
+ assert_style_left_at(anim, 0, function(x) {
+ return easing(easing(x))
+ });
+ assert_style_left_at(anim, 10, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ // Near the extreme point of the effect-easing function
+ assert_style_left_at(anim, 300, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ assert_style_left_at(anim, 750, function(x) {
+ return easingExtrapolated(easing(x));
+ });
+ assert_style_left_at(anim, 770, function(x) {
+ return easing(easing(x))
+ });
+ assert_style_left_at(anim, 1000, function(x) {
+ return easing(easing(x))
+ });
+}, 'cubic-bezier easing with input progress less than 0 and where the ' +
+ 'tangent on the lower boundary is infinity');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/css-easing/linear-timing-functions-output.tentative.html b/testing/web-platform/tests/css/css-easing/linear-timing-functions-output.tentative.html
new file mode 100644
index 0000000000..82ea469052
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/linear-timing-functions-output.tentative.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="assert" content="This test checks the output of linear timing functions" />
+<title>Tests for the output of linear timing functions</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/6533">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/util.js"></script>
+<script src="testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+function assert_style_left_at(animation, time, expected_y) {
+ animation.currentTime = time;
+ assert_approx_equals(pxToNum(getComputedStyle(animation.effect.target).left),
+ expected_y * 100,
+ 0.01,
+ 'The left of the animation should be approximately ' +
+ expected_y * 100 + ' at ' + time + 'ms');
+}
+
+function assert_animations_equal_at(actual_animation, expected_animation, time) {
+ actual_animation.currentTime = time;
+ var actual_left = pxToNum(getComputedStyle(actual_animation.effect.target).left);
+ expected_animation.currentTime = time;
+ var expected_left = pxToNum(getComputedStyle(expected_animation.effect.target).left);
+ assert_approx_equals(actual_left,
+ expected_left,
+ 0.01,
+ 'The left of the animation should be approximately ' +
+ expected_left + ' at ' + time + 'ms');
+}
+
+function create_animated_div(t, easing_function) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ return target.animate(
+ [ { left: '0px' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: easing_function });
+}
+
+test(function(t) {
+ var anim = create_animated_div(t, 'linear(0, 1.5, 1)');
+
+ assert_style_left_at(anim, 0, 0.0);
+ assert_style_left_at(anim, 250, 0.75);
+ assert_style_left_at(anim, 500, 1.5);
+ assert_style_left_at(anim, 750, 1.25);
+ assert_style_left_at(anim, 1000, 1.00);
+}, 'linear function easing with output greater than 1');
+
+test(function(t) {
+ var anim = create_animated_div(t, 'linear(1, -0.5, 0)');
+
+ assert_style_left_at(anim, 0, 1.0);
+ assert_style_left_at(anim, 250, 0.25);
+ assert_style_left_at(anim, 500, -0.5);
+ assert_style_left_at(anim, 750, -0.25);
+ assert_style_left_at(anim, 1000, 0.00);
+}, 'linear function easing with output less than 1');
+
+test(function(t) {
+ var anim = create_animated_div(t, 'linear(0.2 0% 20%, 0.4 20% 40%, 0.6 40% 60%, 0.8 60% 80%, 1.0 80% 100%)');
+ var equiv = create_animated_div(t, 'steps(5, jump-start)');
+
+ assert_animations_equal_at(anim, equiv, 0);
+ assert_animations_equal_at(anim, equiv, 200);
+ assert_animations_equal_at(anim, equiv, 400);
+ assert_animations_equal_at(anim, equiv, 600);
+ assert_animations_equal_at(anim, equiv, 800);
+ assert_animations_equal_at(anim, equiv, 1000);
+}, 'linear function easing, steps equivalent');
+
+test(function(t) {
+ var anim = create_animated_div(t, 'linear(0, 0.1 -10%, 1)');
+ var equiv = create_animated_div(t, 'linear(0, 0.1 0%, 1)');
+
+ assert_animations_equal_at(anim, equiv, 0);
+ assert_animations_equal_at(anim, equiv, 100);
+ assert_animations_equal_at(anim, equiv, 550);
+ assert_animations_equal_at(anim, equiv, 1000);
+}, 'linear function easing, input value being unspecified in the first entry implies zero');
+
+test(function(t) {
+ var anim = create_animated_div(t, 'linear(0, 0.9 110%, 1)');
+ var equiv = create_animated_div(t, 'linear(0, 0.9 110%, 1 110%)');
+
+ assert_animations_equal_at(anim, equiv, 0);
+ assert_animations_equal_at(anim, equiv, 450);
+ assert_animations_equal_at(anim, equiv, 900);
+ assert_animations_equal_at(anim, equiv, 950);
+ assert_animations_equal_at(anim, equiv, 1000);
+}, 'linear function easing, input value being unspecified in the last entry implies max input value');
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/css-easing/linear-timing-functions-syntax.tentative.html b/testing/web-platform/tests/css/css-easing/linear-timing-functions-syntax.tentative.html
new file mode 100644
index 0000000000..99b680d0bd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/linear-timing-functions-syntax.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Easing: getComputedStyle().animationTimingFunction with linear(...)</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/6533">
+<meta name="assert" content="animation-timing-function: linear(...) parsing tests">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_valid_value("animation-timing-function", "linear(0 0%, 1 100%)");
+test_valid_value("animation-timing-function", "linear(0 0% 50%, 1 50% 100%)", "linear(0 0%, 0 50%, 1 50%, 1 100%)");
+test_valid_value("animation-timing-function", "linear(0, 0.5 25% 75%, 1 100% 100%)", "linear(0 0%, 0.5 25%, 0.5 75%, 1 100%, 1 100%)");
+test_valid_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 1.004, 0.998, 1 100% 100%)", "linear(0 0%, 1.3 11.1111%, 1 22.2222%, 0.92 33.3333%, 1 44.4444%, 0.99 55.5556%, 1 66.6667%, 1.004 77.7778%, 0.998 88.8889%, 1 100%, 1 100%)");
+
+test_invalid_value("animation-timing-function", "linear()");
+test_invalid_value("animation-timing-function", "linear(0)");
+test_invalid_value("animation-timing-function", "linear(100%)");
+test_invalid_value("animation-timing-function", "linear(0% 1 50%)");
+test_invalid_value("animation-timing-function", "linear(0 0% 100%)");
+test_invalid_value("animation-timing-function", "linear(0% 100% 0)");
+test_invalid_value("animation-timing-function", "linear(0 calc(50px - 50%), 0 calc(50em + 50em))");
+test_invalid_value("animation-timing-function", "linear(0 calc(50%, 50%), 0 calc(50% + 50%))");
+
+test_computed_value("animation-timing-function", "linear(0, 1)", "linear(0 0%, 1 100%)");
+test_computed_value("animation-timing-function", "linear(0 calc(0%), 0 calc(100%))", "linear(0 0%, 0 100%)");
+test_computed_value("animation-timing-function", "linear(0 calc(50% - 50%), 0 calc(50% + 50%))", "linear(0 0%, 0 100%)");
+test_computed_value("animation-timing-function", "linear(0 calc(min(50%, 60%)), 0 100%)", "linear(0 50%, 0 100%)");
+test_computed_value("animation-timing-function", "linear(0 0% 50%, 1 50% 100%)", "linear(0 0%, 0 50%, 1 50%, 1 100%)");
+test_computed_value("animation-timing-function", "linear(0, 0.5 25% 75%, 1 100% 100%)", "linear(0 0%, 0.5 25%, 0.5 75%, 1 100%, 1 100%)");
+test_computed_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 0.998, 1 100% 100%)", "linear(0 0%, 1.3 12.5%, 1 25%, 0.92 37.5%, 1 50%, 0.99 62.5%, 1 75%, 0.998 87.5%, 1 100%, 1 100%)");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/css-easing/step-timing-functions-output.html b/testing/web-platform/tests/css/css-easing/step-timing-functions-output.html
new file mode 100644
index 0000000000..978ac25df6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/step-timing-functions-output.html
@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="assert"
+content="This test checks the output of step timing functions" />
+<title>Tests for the output of step timing functions</title>
+<link rel="help"
+href="https://drafts.csswg.org/css-easing/#step-timing-functions">
+<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 target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-start' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+
+ // The bezier function produces values greater than 1 (but always less than 2)
+ // in (0.23368794, 1)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 230;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 250;
+ assert_equals(getComputedStyle(target).left, '200px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-start easing with input progress greater than 1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-start' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 3, 1, 3)' });
+
+ // The bezier function produces values:
+ // Input -> Output
+ // 0.0 0.0
+ // 0.114 ~ 0.245 1.5~2.0, so current step is 2, jumps is 1 => 2.0
+ // 0.245 ~ 0.6 2.0~2.4, so current step is 3, jumps is 1 => 3.0
+ // 0.6 ~ 0.882 2.4~2.0, so current step is 3, jumps is 1 => 3.0
+ // 0.882 ~ 0.976 2.0~1.5, so current step is 2, jumps is 1 => 2.0
+ // 1.0 1.0
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 114;
+ assert_equals(getComputedStyle(target).left, '200px');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(target).left, '300px');
+ anim.currentTime = 900;
+ assert_equals(getComputedStyle(target).left, '200px');
+}, 'step-start easing with input progress greater than 2');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-start' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+
+ // The bezier function produces negative values (but always greater than -1)
+ // in (0, 0.766312060)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 750;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 800;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-start easing with input progress less than 0');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-start' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -2, 1, -2)' });
+
+ // The bezier function produces values less than -1 (but always greater than
+ // -2) in the range (~0.118, ~0.755)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 100;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(target).left, '-100px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-start easing with input progress less than -1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-end' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+
+ // The bezier function produces values greater than 1 (but always less than 2)
+ // in (0.23368794, 1)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 230;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 250;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-end easing with input progress greater than 1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-end' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 3, 1, 3)' });
+
+ // The bezier function produces values:
+ // Input -> Output
+ // 0.0 0.0
+ // 0.114 ~ 0.245 1.5~2.0, so current step is 1, jumps is 1 => 1.0
+ // 0.245 ~ 0.6 2.0~2.4, so current step is 2, jumps is 1 => 2.0
+ // 0.6 ~ 0.882 2.4~2.0, so current step is 2, jumps is 1 => 2.0
+ // 0.882 ~ 0.976 2.0~1.5, so current step is 1, jumps is 1 => 1.0
+ // 1.0 1.0
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 114;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(target).left, '200px');
+ anim.currentTime = 900;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-end easing with input progress greater than 2');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'step-end' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+
+ // The bezier function produces negative values (but always greater than -1)
+ // in (0, 0.766312060)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 750;
+ assert_equals(getComputedStyle(target).left, '-100px');
+ anim.currentTime = 800;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'step-end easing with input progress less than 0');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(1, jump-both)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+
+ // The bezier function produces values greater than 1 (but always less than 2)
+ // in (0.23368794, 1)
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '50px');
+ anim.currentTime = 230;
+ assert_equals(getComputedStyle(target).left, '50px');
+ anim.currentTime = 250;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'steps(1, jump-both) easing with input progress greater than 1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(1, jump-both)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 3, 1, 3)' });
+
+ // The bezier function produces values:
+ // Input -> Output
+ // 0.0 0.0, so current step is 1, jumps is 2 => 0.5
+ // 0.114 ~ 0.245 1.5~2.0, so current step is 2, jumps is 2 => 1.0
+ // 0.245 ~ 0.6 2.0~2.4, so current step is 3, jumps is 2 => 1.5
+ // 0.6 ~ 0.882 2.4~2.0, so current step is 3, jumps is 2 => 1.5
+ // 0.882 ~ 0.976 2.0~1.5, so current step is 2, jumps is 2 => 1.0
+ // 1.0 1.0
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '50px');
+ anim.currentTime = 114;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(target).left, '150px');
+ anim.currentTime = 900;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'steps(1, jump-both) easing with input progress greater than 2');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(1, jump-both)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+
+ // The bezier function produces negative values (but always greater than -0.5)
+ // in (0, 0.766312060).
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '50px');
+ anim.currentTime = 750;
+ // current step is 0, jumps is 2.
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 800;
+ assert_equals(getComputedStyle(target).left, '50px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'steps(1, jump-both) easing with input progress less than 0');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(2, jump-none)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 1.5, 1, 1.5)' });
+
+ // The bezier function produces values between 0.5 and 1 in
+ // (~0.0442, 0.23368), and values between 1 and 2 in (0.23368794, 1).
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 45;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 230;
+ assert_equals(getComputedStyle(target).left, '100px');
+ anim.currentTime = 250;
+ assert_equals(getComputedStyle(target).left, '200px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'steps(2, jump-none) easing with input progress greater than 1');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(2, jump-none)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, 3, 1, 3)' });
+
+ // The bezier function produces values:
+ // Input -> Output
+ // 0.0 0.0, so current step is 0, jumps is 1 => 0.0
+ // 0.114 ~ 0.245 1.5~2.0, so current step is 3, jumps is 1 => 3.0
+ // 0.245 ~ 0.6 2.0~2.4, so current step is 4, jumps is 1 => 4.0
+ // 0.6 ~ 0.882 2.4~2.0, so current step is 4, jumps is 1 => 4.0
+ // 0.882 ~ 0.976 2.0~1.5, so current step is 3, jumps is 1 => 3.0
+ // 1.0 1.0
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 114;
+ assert_equals(getComputedStyle(target).left, '300px');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(target).left, '400px');
+ anim.currentTime = 900;
+ assert_equals(getComputedStyle(target).left, '300px');
+}, 'steps(2, jump-none) easing with input progress greater than 2');
+
+test(function(t) {
+ var target = createDiv(t);
+ target.style.position = 'absolute';
+ var anim = target.animate([ { left: '0px', easing: 'steps(2, jump-none)' },
+ { left: '100px' } ],
+ { duration: 1000,
+ fill: 'forwards',
+ easing: 'cubic-bezier(0, -0.5, 1, -0.5)' });
+
+ // The bezier function produces negative values (but always greater than -0.5)
+ // in (0, 0.766312060).
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 750;
+ // current step is -1, jumps is 1.
+ assert_equals(getComputedStyle(target).left, '-100px');
+ anim.currentTime = 800;
+ assert_equals(getComputedStyle(target).left, '0px');
+ anim.currentTime = 1000;
+ assert_equals(getComputedStyle(target).left, '100px');
+}, 'steps(2, jump-none) easing with input progress less than 0');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/css-easing/step-timing-functions-syntax.html b/testing/web-platform/tests/css/css-easing/step-timing-functions-syntax.html
new file mode 100644
index 0000000000..4e8b21e441
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/step-timing-functions-syntax.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="assert"
+content="This test checks the syntax output of step timing functions" />
+<title>Step timing function syntax tests</title>
+<link rel="help"
+ href="https://drafts.csswg.org/css-easing-1/#step-timing-functions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="testcommon.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+"use strict";
+
+test_valid_value("animation-timing-function", "step-start", "steps(1, start)");
+test_valid_value("animation-timing-function", "step-end", "steps(1)");
+test_valid_value("animation-timing-function", "steps(1, start)");
+test_valid_value("animation-timing-function", "steps(1, end)", "steps(1)");
+test_valid_value("animation-timing-function", "steps(1, jump-start)");
+test_valid_value("animation-timing-function", "steps(1, jump-end)", "steps(1)");
+test_valid_value("animation-timing-function", "steps(1, jump-both)");
+test_valid_value("animation-timing-function", "steps(2, jump-none)");
+
+test_invalid_value("animation-timing-function", "steps(0, start)");
+test_invalid_value("animation-timing-function", "steps(0, end)");
+test_invalid_value("animation-timing-function", "steps(0, jump-start)");
+test_invalid_value("animation-timing-function", "steps(0, jump-end)");
+test_invalid_value("animation-timing-function", "steps(0, jump-both)");
+test_invalid_value("animation-timing-function", "steps(1, jump-none)");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/css-easing/testcommon.js b/testing/web-platform/tests/css/css-easing/testcommon.js
new file mode 100644
index 0000000000..9fd25b8650
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/testcommon.js
@@ -0,0 +1,65 @@
+'use strict';
+
+// Creates a <div> element, appends it to the document body and
+// removes the created element during test cleanup.
+function createDiv(test, doc) {
+ return createElement(test, 'div', doc);
+}
+
+// Creates an element with the given |tagName|, appends it to the document body
+// and removes the created element during test cleanup.
+// If |tagName| is null or undefined, creates a <div> element.
+function createElement(test, tagName, doc) {
+ if (!doc) {
+ doc = document;
+ }
+ var element = doc.createElement(tagName || 'div');
+ doc.body.appendChild(element);
+ test.add_cleanup(function() {
+ element.remove();
+ });
+ return element;
+}
+
+// Convert px unit value to a Number
+function pxToNum(str) {
+ return Number(String(str).match(/^(-?[\d.]+)px$/)[1]);
+}
+
+// Cubic bezier with control points (0, 0), (x1, y1), (x2, y2), and (1, 1).
+function cubicBezier(x1, y1, x2, y2) {
+ function xForT(t) {
+ var omt = 1-t;
+ return 3 * omt * omt * t * x1 + 3 * omt * t * t * x2 + t * t * t;
+ }
+
+ function yForT(t) {
+ var omt = 1-t;
+ return 3 * omt * omt * t * y1 + 3 * omt * t * t * y2 + t * t * t;
+ }
+
+ function tForX(x) {
+ // Binary subdivision.
+ var mint = 0, maxt = 1;
+ for (var i = 0; i < 30; ++i) {
+ var guesst = (mint + maxt) / 2;
+ var guessx = xForT(guesst);
+ if (x < guessx) {
+ maxt = guesst;
+ } else {
+ mint = guesst;
+ }
+ }
+ return (mint + maxt) / 2;
+ }
+
+ return function bezierClosure(x) {
+ if (x == 0) {
+ return 0;
+ }
+ if (x == 1) {
+ return 1;
+ }
+ return yForT(tForX(x));
+ }
+}
diff --git a/testing/web-platform/tests/css/css-easing/timing-functions-syntax-computed.html b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-computed.html
new file mode 100644
index 0000000000..ad37d2769e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-computed.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Easing: getComputedStyle().animationTimingFunction</title>
+<link rel="help" href="https://drafts.csswg.org/css-easing/#timing-functions">
+<meta name="assert" content="animation-timing-function computed value is a computed <easing-function> list.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("animation-timing-function", "linear");
+
+test_computed_value("animation-timing-function", "ease");
+test_computed_value("animation-timing-function", "ease-in");
+test_computed_value("animation-timing-function", "ease-out");
+test_computed_value("animation-timing-function", "ease-in-out");
+test_computed_value("animation-timing-function", "cubic-bezier(0.1, 0.2, 0.8, 0.9)");
+test_computed_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3)");
+test_computed_value("animation-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
+
+
+test_computed_value("animation-timing-function", "steps(4, start)");
+test_computed_value("animation-timing-function", "steps(2, end)", "steps(2)");
+test_computed_value("animation-timing-function", "steps(2, jump-start)");
+test_computed_value("animation-timing-function", "steps(2, jump-end)", "steps(2)");
+test_computed_value("animation-timing-function", "steps(2, jump-both)");
+test_computed_value("animation-timing-function", "steps(2, jump-none)");
+
+test_computed_value("animation-timing-function", "linear, ease, linear");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/css-easing/timing-functions-syntax-invalid.html b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-invalid.html
new file mode 100644
index 0000000000..b279369b3b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-invalid.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Easing: parsing animation-timing-function with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-easing/#timing-functions">
+<meta name="assert" content="animation-timing-function supports only the grammar '<timing-function> #'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("animation-timing-function", "auto");
+test_invalid_value("animation-timing-function", "ease-in ease-out");
+test_invalid_value("animation-timing-function", "cubic-bezier(1, 2, 3)");
+test_invalid_value("animation-timing-function", "cubic-bezier(1, 2, 3, infinite)");
+test_invalid_value("animation-timing-function", "cubic-bezier(1, 2, 3, 4, 5)");
+test_invalid_value("animation-timing-function", "cubic-bezier(-0.1, 0.1, 0.5, 0.9)");
+test_invalid_value("animation-timing-function", "cubic-bezier(0.5, 0.1, 1.1, 0.9)");
+
+test_invalid_value("animation-timing-function", "initial, cubic-bezier(0, -2, 1, 3)");
+test_invalid_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3), initial");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/css-easing/timing-functions-syntax-valid.html b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-valid.html
new file mode 100644
index 0000000000..fbeffea73f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-easing/timing-functions-syntax-valid.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Easing: parsing animation-timing-function with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-easing/#timing-functions">
+<meta name="assert" content="animation-timing-function supports the full grammar '<timing-function> #'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("animation-timing-function", "linear");
+
+test_valid_value("animation-timing-function", "ease");
+test_valid_value("animation-timing-function", "ease-in");
+test_valid_value("animation-timing-function", "ease-out");
+test_valid_value("animation-timing-function", "ease-in-out");
+test_valid_value("animation-timing-function", "cubic-bezier(0.1, 0.2, 0.8, 0.9)");
+test_valid_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3)");
+test_valid_value("animation-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)");
+
+
+test_valid_value("animation-timing-function", "steps(4, start)");
+test_valid_value("animation-timing-function", "steps(2, end)", "steps(2)");
+test_valid_value("animation-timing-function", "steps(2, jump-start)");
+test_valid_value("animation-timing-function", "steps(2, jump-end)", "steps(2)");
+test_valid_value("animation-timing-function", "steps(2, jump-both)");
+test_valid_value("animation-timing-function", "steps(2, jump-none)");
+
+test_valid_value("animation-timing-function", "linear, ease, linear");
+</script>
+</body>
+</html>