Port flaky animations/responsive tests to WPT

This patch ports 3 tests that were marked as flaky on Mac13 debug to
WPT. Eventually all non-WPT tests not depending on Chrome specific
behavior should be ported. Simply prioritizing based on entries in
TestExpectations.

What this set of tests has in common is that they all test
responsiveness of an animation with neutral keyframes (based on
underlying value) to style changes.  One is a ref-test and the
remaining two are computed style tests.

Bug: 329062956
Change-Id: I730e2defb613a831d50edf8a4868ab3fa218f73f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5374664
Reviewed-by: Mustaq Ahmed <mustaq@chromium.org>
Commit-Queue: Kevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1274234}
diff --git a/web-animations/responsive/background-position-responsive.html b/web-animations/responsive/background-position-responsive.html
new file mode 100644
index 0000000..7b1a6cd
--- /dev/null
+++ b/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/web-animations/responsive/box-shadow-responsive.html b/web-animations/responsive/box-shadow-responsive.html
new file mode 100644
index 0000000..4f5325d
--- /dev/null
+++ b/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/web-animations/responsive/neutral-keyframe-ref.html b/web-animations/responsive/neutral-keyframe-ref.html
new file mode 100644
index 0000000..3893330
--- /dev/null
+++ b/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/web-animations/responsive/neutral-keyframe.html b/web-animations/responsive/neutral-keyframe.html
new file mode 100644
index 0000000..813aa7c
--- /dev/null
+++ b/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/web-animations/responsive/responsive-test.js b/web-animations/responsive/responsive-test.js
new file mode 100644
index 0000000..feca532
--- /dev/null
+++ b/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);
+}