summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /testing/web-platform/tests/web-animations
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/web-animations')
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html81
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html166
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html1
-rw-r--r--testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html1
-rw-r--r--testing/web-platform/tests/web-animations/responsive/background-position-responsive.html53
-rw-r--r--testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html61
-rw-r--r--testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html16
-rw-r--r--testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html41
-rw-r--r--testing/web-platform/tests/web-animations/responsive/responsive-test.js65
-rw-r--r--testing/web-platform/tests/web-animations/responsive/width.html88
10 files changed, 572 insertions, 1 deletions
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html b/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html
new file mode 100644
index 0000000000..38f37cbfeb
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.progress</title>
+<link rel="help"
+href="https://drafts.csswg.org/web-animations-2/#the-progress-of-an-animation">
+<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 animation = new Animation(null);
+ animation.startTime = document.timeline.currentTime;
+ assert_time_equals_literal(animation.currentTime, 0, 'currentTime is zero');
+ assert_equals(animation.progress, null, 'progress is unresolved');
+}, 'progress of a newly created animation without an effect is unresolved');
+
+test(t => {
+ // currentTime should be unresolved because the animation has no associated
+ // timeline.
+ const animation = new Animation(new KeyframeEffect(createDiv(t), null), null);
+ assert_equals(animation.currentTime, null, 'currentTime is unresolved');
+ assert_equals(animation.progress, null, 'progress is unresolved');
+}, 'progress of an animation whose currentTime is unresolved is unresolved.');
+
+test(t => {
+ const animation = new Animation(new KeyframeEffect(createDiv(t), null,
+ { duration: 0 }), document.timeline);
+ // Set the startTime to 20 seconds into the future.
+ animation.startTime = document.timeline.currentTime + 20 * MS_PER_SEC;
+ assert_less_than(animation.currentTime, 0, 'currentTime is negative');
+ assert_approx_equals(animation.progress, 0, 0.01, 'progress is zero');
+}, "progress of an animation whose effect's endTime is zero is zero if its " +
+ "currentTime is negative.");
+
+test(t => {
+ const animation = new Animation(new KeyframeEffect(createDiv(t), null,
+ { duration: 0 }), document.timeline);
+ animation.startTime = document.timeline.currentTime;
+ assert_time_equals_literal(animation.currentTime, 0, 'currentTime is zero');
+ assert_approx_equals(animation.progress, 1, 0.01, 'progress is one');
+
+ animation.startTime = document.timeline.currentTime - 20 * MS_PER_SEC;
+ assert_greater_than(animation.currentTime, 0, 'currentTime greater than zero');
+ assert_approx_equals(animation.progress, 1, 0.01, 'progress is one');
+}, "progress of an animation whose effect's endTime is zero is one if its " +
+ "currentTime is non-negative.");
+
+test(t => {
+ const animation = new Animation(new KeyframeEffect(createDiv(t), null,
+ { duration: Infinity }), document.timeline);
+
+ animation.startTime = document.timeline.currentTime - 20 * MS_PER_SEC;
+ assert_greater_than(animation.currentTime, 0, 'currentTime is positive');
+ assert_approx_equals(animation.progress, 0, 0.01, 'progress is zero');
+}, "progress of an animation whose effect's endTime is infinity is zero.");
+
+test(t => {
+ const animation = new Animation(new KeyframeEffect(createDiv(t), null,
+ 200 * MS_PER_SEC), document.timeline);
+ animation.playbackRate = 2;
+
+ animation.startTime = document.timeline.currentTime - 50 * MS_PER_SEC;
+ assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC, 'currentTime is 100s');
+ assert_approx_equals(animation.progress, 0.5, 0.01, 'progress is zero');
+
+ animation.startTime = document.timeline.currentTime - 100 * MS_PER_SEC;
+ assert_time_equals_literal(animation.currentTime, 200 * MS_PER_SEC, 'currentTime is 200s');
+ assert_approx_equals(animation.progress, 1, 0.01, 'progress is one');
+
+ animation.startTime = document.timeline.currentTime - 150 * MS_PER_SEC;
+ assert_time_equals_literal(animation.currentTime, 300 * MS_PER_SEC, 'currentTime is 300s');
+ assert_approx_equals(animation.progress, 1, 0.01, 'progress is still one');
+}, "progress of an animation is calculated by currentTime / effect endTime.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html b/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html
new file mode 100644
index 0000000000..b40e72482b
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation.progress</title>
+<link rel="help"
+href="https://drafts.csswg.org/web-animations-2/#the-progress-of-an-animation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<script src="../../../scroll-animations/scroll-timelines/testcommon.js"></script>
+<body>
+ <style>
+ .scroller {
+ overflow-x: hidden;
+ overflow-y: auto;
+ height: 200px;
+ width: 100px;
+ will-change: transform;
+ }
+ .contents {
+ height: 1200px;
+ width: 100%;
+ }
+ #target {
+ height: 100px;
+ width: 100px;
+ background-color: green;
+ margin-top: -1000px;
+ }
+ </style>
+<div id="log"></div>
+<script>
+'use strict';
+
+promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.source;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+ assert_equals(animation.currentTime, null,
+ "The current time is null in Idle state.");
+ assert_equals(animation.progress, null,
+ "The progress is null since the currentTime is unresolved.");
+
+ animation.play();
+ assert_true(animation.pending, "Animation is in the pending state.");
+ assert_equals(animation.currentTime, null,
+ "The current time remains null while in the pending state.");
+ assert_equals(animation.progress, null,
+ "The progress is null since the currentTime is unresolved.");
+
+ await animation.ready;
+ // Verify initial start and current times once ready.
+ assert_percents_equal(animation.currentTime, 0,
+ "The current time is resolved when ready.");
+ assert_equals(animation.progress, 0, "The progress is should be zero.");
+
+ scroller.scrollTop = 0.4 * maxScroll;
+
+ await waitForNextFrame();
+ assert_percents_equal(animation.currentTime, 40,
+ "currentTime reflects progress as a percentage");
+ assert_approx_equals(animation.progress, 0.4, 0.01,
+ "The progress is should match the scroll progress.");
+}, "animation.progress reflects the progress of a scroll animation as a "+
+ "number between 0 and 1");
+
+promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.source;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+ animation.play();
+ await animation.ready;
+ // Verify initial start and current times once ready.
+ assert_percents_equal(animation.currentTime, 0,
+ "The current time is resolved when ready.");
+ assert_equals(animation.progress, 0, "The progress is should be zero.");
+
+ scroller.scrollTop = 0.4 * maxScroll;
+ await waitForNextFrame();
+
+ let timing = animation.effect.getComputedTiming();
+ // iteration duration should be 100%, progress should reflect 40%.
+ assert_percents_equal(timing.duration, 100);
+ assert_percents_equal(animation.currentTime, 40,
+ "currentTime reflects progress as a percentage");
+ assert_approx_equals(animation.progress, 0.4, 0.01,
+ "The progress is should match the scroll progress.");
+ timing = animation.effect.getComputedTiming();
+
+ animation.effect.updateTiming({ iterations: 2 });
+
+ timing = animation.effect.getComputedTiming();
+ // iteration duration should be 50%, progress should still reflect 40%
+ // as it measures currentTime / effect endTime.
+ assert_percents_equal(timing.duration, 50);
+ assert_percents_equal(animation.currentTime, 40,
+ "currentTime reflects progress as a percentage");
+ assert_approx_equals(animation.progress, 0.4, 0.01,
+ "The progress is should match the scroll progress.");
+}, "animation.progress reflects the overall progress of a scroll animation " +
+ "with multiple iterations.");
+
+promise_test(async t => {
+ const scroller = createScroller(t);
+ const view_timeline = createViewTimeline(t);
+
+ const animation = createAnimation(t);
+ animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
+ animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
+ animation.timeline = view_timeline;
+
+ await animation.ready;
+
+ // Cover range is [0, 300]
+ // Contain range is [100, 200]
+ // rangeStart offset of 10% => 110px
+ // rangeEnd offset of 90% => 190px
+ const target_pct = 40;
+ const target_scroll_top = 110 + (target_pct / 100) * (190 - 110);
+ scroller.scrollTop = target_scroll_top;
+ await waitForNextFrame();
+
+ const start_time = 110 / 300 * 100;
+ const timeline_time = target_scroll_top / 300 * 100;
+ const expected_current_time = timeline_time - start_time;
+ assert_percents_equal(animation.currentTime, expected_current_time,
+ "currentTime reflects progress as a percentage");
+ assert_approx_equals(animation.progress, target_pct / 100, 0.01,
+ "progress should reflect fraction of view timeline range scroll through.");
+}, "animation.progress reflects the overall progress of a scroll animation " +
+ "that uses a view-timeline.");
+
+promise_test(async t => {
+ const scroller = createScroller(t);
+ const view_timeline = createViewTimeline(t);
+
+ const animation = createAnimation(t);
+ animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
+ animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
+ animation.timeline = view_timeline;
+
+ await animation.ready;
+
+ // Cover range is [0, 300]
+ // Contain range is [100, 200]
+ // rangeStart offset of 10% => 110px
+ // rangeEnd offset of 10% => 190px
+
+ scroller.scrollTop = 100;
+ await waitForNextFrame();
+ let timing = animation.effect.getComputedTiming();
+ assert_less_than(animation.currentTime.value, 0, "currentTime is negative");
+ assert_approx_equals(animation.progress, 0, 0.01, "progress is zero when " +
+ "scroll offset is less than range start.");
+
+ scroller.scrollTop = 200;
+ await waitForNextFrame();
+ assert_approx_equals(animation.currentTime.value, timing.endTime.value, 0.01,
+ "currentTime has reached endTime");
+ assert_approx_equals(animation.progress, 1, 0.01, "progress is one when " +
+ "scroll offset goes past than range end.");
+}, "progresss of a view-timeline is bounded between 0 and 1.");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html b/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html
index 0ec21657e3..d2d6e9d4c9 100644
--- a/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html
+++ b/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html
@@ -169,6 +169,7 @@ const tests = {
// no effect.
rangeStart: UsePropertyTest(animation => animation.rangeStart),
rangeEnd: UsePropertyTest(animation => animation.rangeEnd),
+ progress: UsePropertyTest(animation => animation.progress),
replaceState: UsePropertyTest(animation => animation.replaceState),
ready: UsePropertyTest(animation => animation.ready),
finished: UsePropertyTest(animation => {
diff --git a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html
index 271a47b301..2252248e23 100644
--- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html
+++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html
@@ -37,7 +37,6 @@ const gNonAnimatableProps = [
'transitionTimingFunction',
'contain',
'direction',
- 'display',
'textCombineUpright',
'textOrientation',
'unicodeBidi',
diff --git a/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html b/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html
new file mode 100644
index 0000000000..7b1a6cd4db
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Animation with neutral keyframe is responsive to change in underlying style</title>
+ <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="responsive-test.js"></script>
+</head>
+<body></body>
+<script type="text/javascript">
+ promise_test(async t => {
+ const responsiveTest =
+ createResponsiveTest(t, {
+ property: 'backgroundPosition',
+ to: '100px 100px'
+ });
+ await responsiveTest.ready;
+ responsiveTest.underlyingValue = '20px 60px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: '40px 70px'},
+ {at: 0.75, is: '80px 90px'},
+ ]);
+ responsiveTest.underlyingValue = '60px 20px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: '70px 40px'},
+ {at: 0.75, is: '90px 80px'},
+ ]);
+ }, 'Animating from a neutral keyframe when the underlying style is ' +
+ 'explicitly set');
+
+ promise_test(async t => {
+ const responsiveTest =
+ createResponsiveTest(t, {
+ property: 'backgroundPosition',
+ from: 'inherit',
+ to: '100px 100px'
+ });
+ await responsiveTest.ready;
+ responsiveTest.inheritedValue = '20px 60px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: '40px 70px'},
+ {at: 0.75, is: '80px 90px'},
+ ]);
+ responsiveTest.inheritedValue = '60px 20px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: '70px 40px'},
+ {at: 0.75, is: '90px 80px'},
+ ]);
+ }, 'Animating from a neutral keyframe when the underlying style is ' +
+ 'inherited from the parent');
+</script>
diff --git a/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html b/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html
new file mode 100644
index 0000000000..4f5325dca6
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Animation with neutral keyframe is responsive to change in underlying style</title>
+ <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="responsive-test.js"></script>
+</head>
+<body></body>
+<script type="text/javascript">
+ promise_test(async t => {
+ const responsiveTest =
+ createResponsiveTest(t, {
+ property: 'boxShadow',
+ from: 'green 20px 20px 20px 20px',
+ to: 'inherit',
+ });
+ await responsiveTest.ready;
+ responsiveTest.inheritedValue = 'blue 0px 0px 0px 0px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: 'rgb(0, 96, 64) 15px 15px 15px 15px'},
+ {at: 0.75, is: 'rgb(0, 32, 191) 5px 5px 5px 5px'},
+ ]);
+ responsiveTest.inheritedValue = 'yellow 100px 100px 100px 100px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: 'rgb(64, 160, 0) 40px 40px 40px 40px'},
+ {at: 0.75, is: 'rgb(191, 223, 0) 80px 80px 80px 80px'},
+ ]);
+ }, 'Animating to inherit responsive to change in style');
+
+ promise_test(async t => {
+ const responsiveTest =
+ createResponsiveTest(t, {
+ property: 'boxShadow',
+ from: 'inherit',
+ to: 'green 20px 20px 20px 20px',
+ });
+ await responsiveTest.ready;
+ responsiveTest.inheritedValue =
+ 'blue 0px 0px 0px 0px, yellow 100px 100px 100px 100px';
+ responsiveTest.assertResponsive([
+ {
+ at: 0.25,
+ is: 'rgb(0, 32, 191) 5px 5px 5px 5px, ' +
+ 'rgba(255, 255, 0, 0.75) 75px 75px 75px 75px'
+ },
+ {
+ at: 0.75,
+ is: 'rgb(0, 96, 64) 15px 15px 15px 15px, ' +
+ 'rgba(255, 255, 0, 0.25) 25px 25px 25px 25px'
+ },
+ ]);
+ responsiveTest.inheritedValue = 'yellow 100px 100px 100px 100px';
+ responsiveTest.assertResponsive([
+ {at: 0.25, is: 'rgb(191, 223, 0) 80px 80px 80px 80px'},
+ {at: 0.75, is: 'rgb(64, 160, 0) 40px 40px 40px 40px'},
+ ]);
+ }, 'Animating from inherit responsive to change in style');
+</script>
diff --git a/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html
new file mode 100644
index 0000000000..3893330fb9
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style type="text/css">
+ #block {
+ background-color: green;
+ height: 100px;
+ width: 100px;
+ transform: translate(100px);
+ }
+ </style>
+</head>
+<body>
+ <div id="block"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html
new file mode 100644
index 0000000000..813aa7c04c
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Animation with neutral keyframe is responsive to change in underlying style</title>
+ <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect">
+ <link rel="match" href="neutral-keyframe-ref.html">
+ <script src="/common/reftest-wait.js"></script>
+ <script src="/web-animations/testcommon.js"></script>
+ <style type="text/css">
+ #block {
+ background-color: green;
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div id="block"></div>
+</body>
+<script>
+ window.onload = async () => {
+ const target = document.getElementById('block');
+ const anim = target.animate({ translate: '200px' },
+ {
+ duration: 10000,
+ easing: 'steps(1,jump-end)'
+ });
+ await anim.ready;
+ await waitForNextFrame();
+ // The neutral keyframe value changes from transform 'none' to '100px'.
+ // Since using jump-end for the easing function, the animated value is the
+ // underlying (neutral) value. If the box is not translated, then the change
+ // to the underlying value is not taking effect.
+ target.style.translate = '100px';
+ await waitForNextFrame();
+ takeScreenshot();
+ };
+</script>
+</html>
diff --git a/testing/web-platform/tests/web-animations/responsive/responsive-test.js b/testing/web-platform/tests/web-animations/responsive/responsive-test.js
new file mode 100644
index 0000000000..feca53259c
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/responsive-test.js
@@ -0,0 +1,65 @@
+class ResponsiveTest {
+ constructor(target, property, keyframes) {
+ this.property = property;
+ this.target = target;
+ this.duration = 1000;
+ this.anim = target.animate(keyframes, this.duration);
+ this.anim.pause();
+ }
+
+ get ready() {
+ return new Promise(resolve => {
+ this.anim.ready.then(resolve);
+ });
+ }
+
+ set underlyingValue(value) {
+ this.target.style[this.property] = value;
+ }
+
+ set inheritedValue(value) {
+ this.target.parentElement.style[this.property] = value;
+ }
+
+ // The testCases are of the form:
+ // [{at: <fractional_progress>, is: <computed style> }, ...]
+ assertResponsive(testCases) {
+ for (let i = 0; i < testCases.length; i++) {
+ const testCase = testCases[i];
+ this.anim.currentTime = this.duration * testCase.at;
+ assert_equals(getComputedStyle(this.target)[this.property], testCase.is,
+ `${this.property} at ${testCase.at}`);
+ }
+ }
+}
+
+// Creates a test that allows setting the underlying style of the target
+// element or its parent.
+// Options are of the form:
+// property: required property in camelcase form as used in the
+// web animation API.
+// from: optional starting keyframe as a string.
+// to: optional ending keyframe as a string.
+function createResponsiveTest(test, options) {
+ const parent = document.createElement('div');
+ const target = document.createElement('div');
+ document.body.appendChild(parent);
+ parent.appendChild(target);
+ const property = options.property;
+ const keyframes = [];
+ const createKeyframe = (value) => {
+ const keyframe = {};
+ keyframe[property] = value;
+ return keyframe;
+ }
+ if (options.from) {
+ keyframes.push(createKeyframe(options.from));
+ }
+ if (options.to) {
+ keyframes.push(createKeyframe(options.to));
+ }
+ test.add_cleanup(() => {
+ parent.remove();
+ });
+ return new ResponsiveTest(target, property, keyframes);
+}
diff --git a/testing/web-platform/tests/web-animations/responsive/width.html b/testing/web-platform/tests/web-animations/responsive/width.html
new file mode 100644
index 0000000000..9f5bf5a29d
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/responsive/width.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+body { width: 500px }
+
+#child { width: 20px }
+
+</style>
+
+<div id='container'>
+ <div id='element'>
+ <div id='child'>
+ </div>
+ </div>
+</div>
+
+<script>
+var element = document.getElementById('element');
+var container = document.getElementById('container');
+
+test(function() {
+ element.style.fontSize = '10px';
+ var player = element.animate([{width: '3em'}, {width: '5em'}], 10);
+ player.pause();
+ player.currentTime = 5;
+ element.style.fontSize = '20px';
+ assert_equals(getComputedStyle(element).width, '80px');
+}, 'width responsive to style changes');
+
+test(function() {
+ element.style.fontSize = '10px';
+ var player = element.animate([{width: '40px'}, {width: 'calc(40px - 2em)'}], 10);
+ player.pause();
+
+ player.currentTime = 5;
+ element.style.fontSize = '40px';
+ assert_equals(getComputedStyle(element).width, '20px');
+
+ player.currentTime = 7.5;
+ assert_equals(getComputedStyle(element).width, '10px');
+}, 'width clamped to 0px on keyframes');
+
+test(function() {
+ container.style.width = 'auto';
+ var player = element.animate([{width: 'inherit'}, {width: '20px'}], 4000);
+ player.pause();
+
+ player.currentTime = 1000;
+ assert_equals(getComputedStyle(element).width, '500px');
+
+ container.style.width = '100px';
+ assert_equals(getComputedStyle(element).width, '80px');
+
+ container.style.width = 'min-content';
+ assert_equals(getComputedStyle(element).width, '20px');
+
+ container.style.width = '100px';
+ assert_equals(getComputedStyle(element).width, '80px');
+
+ container.style.width = 'max-content';
+ assert_equals(getComputedStyle(element).width, '20px');
+
+ container.style.width = '100px';
+ assert_equals(getComputedStyle(element).width, '80px');
+
+ container.style.width = 'fit-content';
+ assert_equals(getComputedStyle(element).width, '20px');
+}, 'width responsive to inherited changes from keyword');
+
+test(function() {
+ container.style.fontSize = '10px';
+ container.style.width = '100px';
+ var player = element.animate([{width: 'inherit'}, {width: '200px'}], 4000);
+ player.pause();
+
+ player.currentTime = 1000;
+ assert_equals(getComputedStyle(element).width, '125px');
+
+ container.style.width = '120px';
+ assert_equals(getComputedStyle(element).width, '140px');
+
+ container.style.width = '100px';
+ assert_equals(getComputedStyle(element).width, '125px');
+}, 'width responsive to inherited changes from length');
+
+</script>