Bug 1309752 - Part 5: Add tests for logical properties animation. r?birtles draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Wed, 26 Apr 2017 17:47:01 +0900
changeset 568592 31b6126e97f38834f571e07f7ff7a253b466ba4c
parent 568591 f00929df4c31b8c7e1d2d935046efac477d15a83
child 625958 23b76b233472044a16fa633912baff18e99c406f
push id55908
push userbmo:dakatsuka@mozilla.com
push dateWed, 26 Apr 2017 09:58:37 +0000
reviewersbirtles
bugs1309752
milestone55.0a1
Bug 1309752 - Part 5: Add tests for logical properties animation. r?birtles Add tests for logical properties animation and writing-mode animation. MozReview-Commit-ID: 9ryG78wLm33
testing/web-platform/tests/web-animations/animation-model/animation-types/addition-per-property.html
testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js
testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/addition-per-property.html
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/addition-per-property.html
@@ -12,47 +12,54 @@ html {
   font-size: 10px;
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-for (var property in gCSSProperties) {
-  if (!isSupported(property)) {
-    continue;
-  }
-
-  var animationTypes = gCSSProperties[property].types;
-  var setupFunction = gCSSProperties[property].setup;
+function testAddition(property, animationTypes, setupFunction,
+                      logicalProperty, mode) {
   animationTypes.forEach(function(animationType) {
     var typeObject;
     var animationTypeString;
     if (typeof animationType === 'string') {
       typeObject = types[animationType];
       animationTypeString = animationType;
     } else if (typeof animationType === 'object' &&
                animationType.type && typeof animationType.type === 'string') {
       typeObject = types[animationType.type];
       animationTypeString = animationType.type;
     }
 
     // First, test that the animation type object has 'testAddition'.
     // We use test() function here so that we can continue the remainder tests
     // even if this test fails.
-    test(function(t) {
-      assert_own_property(typeObject, 'testAddition', animationTypeString +
-                          ' should have testAddition property');
-      assert_equals(typeof typeObject.testAddition, 'function',
-                    'testAddition method should be a function');
-    }, property + ' (type: ' + animationTypeString +
-       ') has testAddition function');
-
+    if (!logicalProperty) {
+      test(function(t) {
+        assert_own_property(typeObject, 'testAddition', animationTypeString +
+                            ' should have testAddition property');
+        assert_equals(typeof typeObject.testAddition, 'function',
+                      'testAddition method should be a function');
+      }, property + ' (type: ' + animationTypeString +
+         ') has testAddition function');
+    }
     if (typeObject.testAddition &&
         typeof typeObject.testAddition === 'function') {
       typeObject.testAddition(property,
                               setupFunction,
-                              animationType.options);
+                              animationType.options,
+                              logicalProperty,
+                              mode);
     }
   });
 }
+
+for (var property in gCSSProperties) {
+  if (!isSupported(property)) {
+    continue;
+  }
+
+  testAddition(property, gCSSProperties[property].types,
+               gCSSProperties[property].setup);
+}
 </script>
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
@@ -127,9 +127,108 @@ test(function(t) {
                 + ' time');
   anim.currentTime = 960;
   assert_equals(getComputedStyle(div).fontStyle, 'oblique',
                 'Animation produces \'to\' value at 96% of the iteration'
                 + ' time');
 }, 'Test the 50% switch point for discrete animation is based on the'
    + ' keyframe easing');
 
+
+test(function(t) {
+  // Test for writingMode and logical property which depends on writingMode.
+  var div = createDiv(t);
+  div.style.width = '200px';
+  div.style.height = '20px';
+  var anim = div.animate({ writingMode: ['vertical-lr', 'horizontal-tb'],
+                           blockSize: ['100px', '10px'] },
+                         { duration: 1000, fill: 'both' });
+  anim.currentTime = 0;
+  assert_equals(getComputedStyle(div).width, '100px',
+                '\'width\' shoud apply \'block-size\' property at time is 0');
+  assert_equals(getComputedStyle(div).height, '20px',
+                '\'height\' shoud not apply \'block-size\' property '
+                + 'at time is 0');
+
+  anim.currentTime = 250;
+  assert_equals(getComputedStyle(div).width, '125px',
+                '\'width\' shoud apply \'block-size\' property '
+                + 'at time is 250');
+  assert_equals(getComputedStyle(div).height, '20px',
+                '\'height\' shoud not apply \'block-size\' property '
+                + 'at time is 250');
+
+  anim.currentTime = 500;
+  assert_equals(getComputedStyle(div).width, '200px',
+                '\'width\' shoud not apply \'block-size\' property '
+                + 'at time is 500');
+  assert_equals(getComputedStyle(div).height, '15px',
+                '\'height\' shoud apply \'block-size\' property '
+                + 'at time is 500');
+
+  anim.currentTime = 750;
+  assert_equals(getComputedStyle(div).width, '200px',
+                '\'width\' shoud not apply \'block-size\' property '
+                + 'at time is 750');
+  assert_equals(getComputedStyle(div).height, '12.5px',
+                '\'height\' shoud apply \'block-size\' property '
+                + 'at time is 750');
+
+  anim.currentTime = 1000;
+  assert_equals(getComputedStyle(div).width, '200px',
+                '\'width\' shoud not apply \'block-size\' property '
+                + 'at time is 1000');
+  assert_equals(getComputedStyle(div).height, '10px',
+                '\'height\' shoud apply \'block-size\' property '
+                + 'at time is 1000');
+
+}, 'Test for writing-mode and logical property');
+
+test(function(t) {
+  // Test for writingMode and logical property which depends on writingMode.
+  var div = createDiv(t);
+  div.style.width = '200px';
+  div.style.height = '300px';
+  var anim = div.animate({ writingMode: ['vertical-lr'],
+                           blockSize: ['100px'] },
+                         { duration: 1000, fill: 'both' });
+  anim.currentTime = 0;
+  assert_equals(getComputedStyle(div).width, '200px',
+                '\'width\' shoud not apply \'block-size\' property '
+                + 'at time is 0');
+  assert_equals(getComputedStyle(div).height, '300px',
+                '\'height\' shoud apply \'block-size\' property at time is 0');
+
+  anim.currentTime = 250;
+  assert_equals(getComputedStyle(div).width, '200px',
+                '\'width\' shoud not apply \'block-size\' property '
+                + 'at time is 250');
+  assert_equals(getComputedStyle(div).height, '250px',
+                '\'height\' shoud apply \'block-size\' property '
+                + 'at time is 250');
+
+  anim.currentTime = 500;
+  assert_equals(getComputedStyle(div).width, '150px',
+                '\'width\' shoud apply \'block-size\' property '
+                + 'at time is 500');
+  assert_equals(getComputedStyle(div).height, '300px',
+                '\'height\' shoud not apply \'block-size\' property '
+                + 'at time is 500');
+
+  anim.currentTime = 750;
+  assert_equals(getComputedStyle(div).width, '125px',
+                '\'width\' shoud apply \'block-size\' property '
+                + 'at time is 750');
+  assert_equals(getComputedStyle(div).height, '300px',
+                '\'height\' shoud not apply \'block-size\' property '
+                + 'at time is 750');
+
+  anim.currentTime = 1000;
+  assert_equals(getComputedStyle(div).width, '100px',
+                '\'width\' shoud apply \'block-size\' property '
+                + 'at time is 1000');
+  assert_equals(getComputedStyle(div).height, '300px',
+                '\'height\' shoud not apply \'block-size\' property '
+                + 'at time is 1000');
+
+}, 'Test for writing-mode and logical property with missing keyframes');
+
 </script>
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/interpolation-per-property.html
@@ -12,47 +12,56 @@ html {
   font-size: 10px;
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-for (var property in gCSSProperties) {
-  if (!isSupported(property)) {
-    continue;
-  }
-
-  var animationTypes = gCSSProperties[property].types;
-  var setupFunction = gCSSProperties[property].setup;
+function testInterpolation(property, animationTypes,
+                           setupFunction, logicalProperty, mode) {
   animationTypes.forEach(function(animationType) {
     var typeObject;
     var animationTypeString;
     if (typeof animationType === 'string') {
       typeObject = types[animationType];
       animationTypeString = animationType;
     } else if (typeof animationType === 'object' &&
                animationType.type && typeof animationType.type === 'string') {
       typeObject = types[animationType.type];
       animationTypeString = animationType.type;
     }
 
     // First, test that the animation type object has 'testInterpolation'.
     // We use test() function() here so that we can continue the remainder tests
     // even if this test fails.
-    test(function(t) {
-      assert_own_property(typeObject, 'testInterpolation', animationTypeString +
-                          ' should have testInterpolation property');
-      assert_equals(typeof typeObject.testInterpolation, 'function',
-                    'testInterpolation method should be a function');
-    }, property + ' (type: ' + animationTypeString +
-       ') has testInterpolation function');
+    if (!logicalProperty) {
+      test(function(t) {
+        assert_own_property(typeObject, 'testInterpolation',
+                            animationTypeString +
+                            ' should have testInterpolation property');
+        assert_equals(typeof typeObject.testInterpolation, 'function',
+                      'testInterpolation method should be a function');
+      }, property + ' (type: ' + animationTypeString +
+         ') has testInterpolation function');
+    }
 
     if (typeObject.testInterpolation &&
         typeof typeObject.testInterpolation === 'function') {
       typeObject.testInterpolation(property,
                                    setupFunction,
-                                   animationType.options);
+                                   animationType.options,
+                                   logicalProperty,
+                                   mode);
     }
   });
 }
+
+for (var property in gCSSProperties) {
+  if (!isSupported(property)) {
+    continue;
+  }
+
+  testInterpolation(property, gCSSProperties[property].types,
+                    gCSSProperties[property].setup);
+}
 </script>
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js
@@ -85,47 +85,103 @@ var gCSSProperties = {
   'background-size': {
     // https://drafts.csswg.org/css-backgrounds-3/#background-size
     types: [
     ]
   },
   'block-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-block-size
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'width' },
+            { writingMode: 'horizontal-tb', physical: 'height' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-end-color': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-color
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-right-color' },
+            { writingMode: 'horizontal-tb', physical: 'border-bottom-color' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-end-style': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-style
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-right-style' },
+            { writingMode: 'horizontal-tb', physical: 'border-bottom-style' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-end-width': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-width
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-right-width' },
+            { writingMode: 'horizontal-tb', physical: 'border-bottom-width' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-start-color': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-color
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-left-color' },
+            { writingMode: 'horizontal-tb', physical: 'border-top-color' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-start-style': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-style
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-left-style' },
+            { writingMode: 'horizontal-tb', physical: 'border-top-style' },
+          ]
+        }
+      }
+    ],
   },
   'border-block-start-width': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-width
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-left-width' },
+            { writingMode: 'horizontal-tb', physical: 'border-top-width' },
+          ]
+        }
+      }
+    ],
   },
   'border-bottom-color': {
     // https://drafts.csswg.org/css-backgrounds-3/#border-bottom-color
     types: [ 'color' ]
   },
   'border-bottom-left-radius': {
     // https://drafts.csswg.org/css-backgrounds-3/#border-bottom-left-radius
     types: [
@@ -155,42 +211,126 @@ var gCSSProperties = {
     // https://drafts.csswg.org/css-tables/#propdef-border-collapse
     types: [
       { type: 'discrete', options: [ [ 'collapse', 'separate' ] ] }
     ]
   },
   'border-inline-end-color': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-color
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-bottom-color' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-top-color' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-top-color' },
+            { writingMode: 'horizontal-tb', physical: 'border-right-color' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'border-left-color' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-left-color' },
+          ]
+        }
+      }
+    ],
   },
   'border-inline-end-style': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-style
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-bottom-style' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-top-style' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-top-style' },
+            { writingMode: 'horizontal-tb', physical: 'border-right-style' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'border-left-style' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-left-style' },
+          ]
+        }
+      }
+    ],
   },
   'border-inline-end-width': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-width
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-bottom-width' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-top-width' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-top-width' },
+            { writingMode: 'horizontal-tb', physical: 'border-right-width' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'border-left-width' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              textOrientation: 'upright', physical: 'border-left-width' },
+          ]
+        }
+      }
+    ],
   },
   'border-inline-start-color': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-color
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-top-color' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-bottom-color' },
+            { writingMode: 'horizontal-tb', physical: 'border-left-color' },
+            { writingMode: 'horizontal-lr', direction: 'rtl',
+              physical: 'border-right-color' },
+          ]
+        }
+      }
+    ],
   },
   'border-inline-start-style': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-style
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-top-style' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-bottom-style' },
+            { writingMode: 'horizontal-tb', physical: 'border-left-style' },
+            { writingMode: 'horizontal-lr', direction: 'rtl',
+              physical: 'border-right-style' },
+          ]
+        }
+      }
+    ],
   },
   'border-inline-start-width': {
     // https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-width
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'border-top-width' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'border-bottom-width' },
+            { writingMode: 'horizontal-tb', physical: 'border-left-width' },
+            { writingMode: 'horizontal-lr', direction: 'rtl',
+              physical: 'border-right-width' },
+          ]
+        }
+      }
+    ],
   },
   'border-image-outset': {
     // https://drafts.csswg.org/css-backgrounds-3/#border-image-outset
     types: [
       { type: 'discrete', options: [ [ '1 1 1 1', '5 5 5 5' ] ] }
     ]
   },
   'border-image-repeat': {
@@ -288,18 +428,22 @@ var gCSSProperties = {
     setup: t => {
       var element = createElement(t);
       element.style.borderTopStyle = 'solid';
       return element;
     }
   },
   'bottom': {
     // https://drafts.csswg.org/css-position/#propdef-bottom
-    types: [
-    ]
+    types: [ 'length' ],
+    setup: t => {
+      var div = createElement(t);
+      div.style.position = 'relative';
+      return div;
+    }
   },
   'box-decoration-break': {
     // https://drafts.csswg.org/css-break/#propdef-box-decoration-break
     types: [
       { type: 'discrete', options: [ [ 'slice', 'clone' ] ] }
     ]
   },
   'box-shadow': {
@@ -682,18 +826,17 @@ var gCSSProperties = {
   'grid-template-rows': {
     // https://drafts.csswg.org/css-template/#grid-template-rows
     types: [
       { type: 'discrete', options: [ [ '1px', '5px' ] ] }
     ]
   },
   'height': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-height
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'hyphens': {
     // https://drafts.csswg.org/css-text-3/#propdef-hyphens
     types: [
       { type: 'discrete', options: [ [ 'manual', 'auto' ] ] }
     ]
   },
   'image-orientation': {
@@ -717,17 +860,25 @@ var gCSSProperties = {
     // https://drafts.csswg.org/css-inline/#propdef-initial-letter
     types: [
       { type: 'discrete', options: [ [ '1 2', '3 4' ] ] }
     ]
   },
   'inline-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-inline-size
     types: [
-    ]
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'height' },
+            { writingMode: 'horizontal-tb', physical: 'width' },
+          ]
+        }
+      }
+    ],
   },
   'isolation': {
     // https://drafts.fxtf.org/compositing-1/#propdef-isolation
     types: [
       { type: 'discrete', options: [ [ 'auto', 'isolate' ] ] }
     ]
   },
   'justify-content': {
@@ -745,18 +896,22 @@ var gCSSProperties = {
   'justify-self': {
     // https://drafts.csswg.org/css-align/#propdef-justify-self
     types: [
       { type: 'discrete', options: [ [ 'baseline', 'last baseline' ] ] }
     ]
   },
   'left': {
     // https://drafts.csswg.org/css-position/#propdef-left
-    types: [
-    ]
+    types: [ 'length' ],
+    setup: t => {
+      var div = createElement(t);
+      div.style.position = 'relative';
+      return div;
+    }
   },
   'letter-spacing': {
     // https://drafts.csswg.org/css-text-3/#propdef-letter-spacing
     types: [ 'length' ]
   },
   'lighting-color': {
     // https://drafts.fxtf.org/filters/#LightingColorProperty
     types: [ 'color' ]
@@ -784,52 +939,80 @@ var gCSSProperties = {
     // https://drafts.csswg.org/css-lists-3/#propdef-list-style-type
     types: [
       { type: 'discrete', options: [ [ 'circle', 'square' ] ] }
     ]
   },
   'margin-block-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-margin-block-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'margin-right' },
+            { writingMode: 'horizontal-tb', physical: 'margin-bottom' },
+          ]
+        }
+      }
     ]
   },
   'margin-block-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-margin-block-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'margin-left' },
+            { writingMode: 'horizontal-tb', physical: 'margin-top' },
+          ]
+        }
+      }
     ]
   },
   'margin-bottom': {
     // https://drafts.csswg.org/css-box/#propdef-margin-bottom
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'margin-inline-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-margin-inline-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'margin-bottom' },
+            { writingMode: 'horizontal-tb', physical: 'margin-right' },
+          ]
+        }
+      }
     ]
   },
   'margin-inline-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-margin-inline-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'margin-top' },
+            { writingMode: 'horizontal-tb', physical: 'margin-left' },
+          ]
+        }
+      }
     ]
   },
   'margin-left': {
     // https://drafts.csswg.org/css-box/#propdef-margin-left
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'margin-right': {
     // https://drafts.csswg.org/css-box/#propdef-margin-right
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'margin-top': {
     // https://drafts.csswg.org/css-box/#propdef-margin-top
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'marker-end': {
     // https://svgwg.org/specs/markers/#MarkerEndProperty
     types: [
       { type: 'discrete',
         options: [ [ 'url("http://localhost/test-1")',
                      'url("http://localhost/test-2")' ] ] }
     ]
@@ -920,52 +1103,80 @@ var gCSSProperties = {
     // https://drafts.fxtf.org/css-masking-1/#propdef-mask-type
     types: [
       { type: 'discrete', options: [ [ 'alpha', 'luminance' ] ] }
     ]
   },
   'max-block-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-max-block-size
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'max-width' },
+            { writingMode: 'horizontal-tb', physical: 'max-height' },
+          ]
+        }
+      }
     ]
   },
   'max-height': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-max-height
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'max-inline-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-max-inline-size
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'max-height' },
+            { writingMode: 'horizontal-tb', physical: 'max-width' },
+          ]
+        }
+      }
     ]
   },
   'max-width': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-max-width
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'min-block-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-min-block-size
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'min-width' },
+            { writingMode: 'horizontal-tb', physical: 'min-height' },
+          ]
+        }
+      }
     ]
   },
   'min-height': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-min-height
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'min-inline-size': {
     // https://drafts.csswg.org/css-logical-props/#propdef-min-inline-size
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'min-height' },
+            { writingMode: 'horizontal-tb', physical: 'min-width' },
+          ]
+        }
+      }
     ]
   },
   'min-width': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-min-width
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'mix-blend-mode': {
     // https://drafts.fxtf.org/compositing-1/#propdef-mix-blend-mode
     types: [
       { type: 'discrete', options: [ [ 'multiply', 'screen' ] ] }
     ]
   },
   'object-fit': {
@@ -977,31 +1188,71 @@ var gCSSProperties = {
   'object-position': {
     // https://drafts.csswg.org/css-images-3/#propdef-object-position
     types: [
     ]
   },
   'offset-block-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-offset-block-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'right' },
+            { writingMode: 'horizontal-tb', physical: 'bottom' },
+          ]
+        }
+      }
     ]
   },
   'offset-block-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-offset-block-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'left' },
+            { writingMode: 'horizontal-tb', physical: 'top' },
+          ]
+        }
+      }
     ]
   },
   'offset-inline-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-offset-inline-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'bottom' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'top' },
+            { writingMode: 'horizontal-tb', physical: 'right' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'left' },
+          ]
+        }
+      }
     ]
   },
   'offset-inline-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-offset-inline-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'top' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'bottom' },
+            { writingMode: 'horizontal-tb', physical: 'left' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'right' },
+          ]
+        }
+      }
     ]
   },
   'opacity': {
     // https://drafts.csswg.org/css-color/#propdef-opacity
     types: [
     ]
   },
   'order': {
@@ -1058,52 +1309,88 @@ var gCSSProperties = {
     // https://drafts.csswg.org/css-overflow-3/#propdef-overflow-y
     types: [
       { type: 'discrete', options: [ [ 'visible', 'hidden' ] ] }
     ]
   },
   'padding-block-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-padding-block-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'padding-right' },
+            { writingMode: 'horizontal-tb', physical: 'padding-bottom' },
+          ]
+        }
+      }
     ]
   },
   'padding-block-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-padding-block-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'padding-left' },
+            { writingMode: 'horizontal-tb', physical: 'padding-top' },
+          ]
+        }
+      }
     ]
   },
   'padding-bottom': {
     // https://drafts.csswg.org/css-box/#propdef-padding-bottom
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'padding-inline-end': {
     // https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-end
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'padding-bottom' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'padding-top' },
+            { writingMode: 'horizontal-tb', physical: 'padding-right' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'padding-left' },
+          ]
+        }
+      }
     ]
   },
   'padding-inline-start': {
     // https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-start
     types: [
+      { type: 'logical',
+        options: {
+          modes: [
+            { writingMode: 'vertical-lr', physical: 'padding-top' },
+            { writingMode: 'vertical-lr', direction: 'rtl',
+              physical: 'padding-bottom' },
+            { writingMode: 'horizontal-tb', physical: 'padding-left' },
+            { writingMode: 'horizontal-tb', direction: 'rtl',
+              physical: 'padding-right' },
+          ]
+        }
+      }
     ]
   },
   'padding-left': {
     // https://drafts.csswg.org/css-box/#propdef-padding-left
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'padding-right': {
     // https://drafts.csswg.org/css-box/#propdef-padding-right
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'padding-top': {
     // https://drafts.csswg.org/css-box/#propdef-padding-top
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'page-break-after': {
     // https://drafts.csswg.org/css-break-3/#propdef-break-after
     types: [
       { type: 'discrete', options: [ [ 'always', 'auto' ] ] }
     ]
   },
   'page-break-before': {
@@ -1153,18 +1440,22 @@ var gCSSProperties = {
   'resize': {
     // https://drafts.csswg.org/css-ui/#propdef-resize
     types: [
       { type: 'discrete', options: [ [ 'both', 'horizontal' ] ] }
     ]
   },
   'right': {
     // https://drafts.csswg.org/css-position/#propdef-right
-    types: [
-    ]
+    types: [ 'length' ],
+    setup: t => {
+      var div = createElement(t);
+      div.style.position = 'relative';
+      return div;
+    }
   },
   'ruby-align': {
     // https://drafts.csswg.org/css-ruby-1/#propdef-ruby-align
     types: [
       { type: 'discrete', options: [ [ 'start', 'center' ] ] }
     ]
   },
   'ruby-position': {
@@ -1365,18 +1656,22 @@ var gCSSProperties = {
   'touch-action': {
     // https://w3c.github.io/pointerevents/#the-touch-action-css-property
     types: [
       { type: 'discrete', options: [ [ 'auto', 'none' ] ] }
     ]
   },
   'top': {
     // https://drafts.csswg.org/css-position/#propdef-top
-    types: [
-    ]
+    types: [ 'length' ],
+    setup: t => {
+      var div = createElement(t);
+      div.style.position = 'relative';
+      return div;
+    }
   },
   'transform': {
     // https://drafts.csswg.org/css-transforms/#propdef-transform
     types: [ 'transformList' ]
   },
   'transform-box': {
     // https://drafts.csswg.org/css-transforms/#propdef-transform-box
     types: [
@@ -1418,18 +1713,17 @@ var gCSSProperties = {
   'white-space': {
     // https://drafts.csswg.org/css-text-4/#propdef-white-space
     types: [
       { type: 'discrete', options: [ [ 'pre', 'nowrap' ] ] }
     ]
   },
   'width': {
     // https://drafts.csswg.org/css21/visudet.html#propdef-width
-    types: [
-    ]
+    types: [ 'length' ]
   },
   'word-break': {
     // https://drafts.csswg.org/css-text-3/#propdef-word-break
     types: [
       { type: 'discrete', options: [ [ 'keep-all', 'break-all' ] ] }
     ]
   },
   'word-spacing': {
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
@@ -1,130 +1,165 @@
+function getPropertyDescription(physical, logical, mode) {
+  if (!logical) {
+    return property;
+  }
+  let desc = logical + ' for';
+  if (mode.writingMode) {
+    desc += ' writing-mode: ' + mode.writingMode
+  }
+  if (mode.textOrientation) {
+    desc += ' text-orientation: ' + mode.textOrientation
+  }
+  if (mode.direction) {
+    desc += ' direction: ' + mode.direction
+  }
+  desc += ' (physical property: '+ physical +')'
+  return desc;
+}
+
 const discreteType = {
-  testInterpolation: function(property, setup, options) {
+  testInterpolation: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     options.forEach(function(keyframes) {
       var [ from, to ] = keyframes;
       test(function(t) {
-        var idlName = propertyToIDL(property);
+        var idlName = propertyToIDL(logical || property);
+        var computedIdlName = propertyToIDL(property);
         var target = createTestElement(t, setup);
         var animation = target.animate({ [idlName]: [from, to] },
                                        { duration: 1000, fill: 'both' });
-        testAnimationSamples(animation, idlName,
+        testAnimationSamples(animation, computedIdlName,
                              [{ time: 0,    expected: from.toLowerCase() },
                               { time: 499,  expected: from.toLowerCase() },
                               { time: 500,  expected: to.toLowerCase() },
                               { time: 1000, expected: to.toLowerCase() }]);
-      }, property + ' uses discrete animation when animating between "'
+      }, propertyDesc + ' uses discrete animation when animating between "'
          + from + '" and "' + to + '" with linear easing');
 
       test(function(t) {
         // Easing: http://cubic-bezier.com/#.68,0,1,.01
         // With this curve, we don't reach the 50% point until about 95% of
         // the time has expired.
-        var idlName = propertyToIDL(property);
+        var idlName = propertyToIDL(logical || property);
+        var computedIdlName = propertyToIDL(property);
         var keyframes = {};
         var target = createTestElement(t, setup);
-        var animation = target.animate({ [idlName]: [from, to] },
-                                       { duration: 1000, fill: 'both',
-                                         easing: 'cubic-bezier(0.68,0,1,0.01)' });
-        testAnimationSamples(animation, idlName,
+        var animation =
+          target.animate({ [idlName]: [from, to] },
+                         { duration: 1000, fill: 'both',
+                           easing: 'cubic-bezier(0.68,0,1,0.01)' });
+        testAnimationSamples(animation, computedIdlName,
                              [{ time: 0,    expected: from.toLowerCase() },
                               { time: 940,  expected: from.toLowerCase() },
                               { time: 960,  expected: to.toLowerCase() }]);
-      }, property + ' uses discrete animation when animating between "'
+      }, propertyDesc + ' uses discrete animation when animating between "'
          + from + '" and "' + to + '" with effect easing');
 
       test(function(t) {
         // Easing: http://cubic-bezier.com/#.68,0,1,.01
         // With this curve, we don't reach the 50% point until about 95% of
         // the time has expired.
-        var idlName = propertyToIDL(property);
+        var idlName = propertyToIDL(logical || property);
+        var computedIdlName = propertyToIDL(property);
         var target = createTestElement(t, setup);
-        var animation = target.animate({ [idlName]: [from, to],
-                                         easing: 'cubic-bezier(0.68,0,1,0.01)' },
-                                       { duration: 1000, fill: 'both' });
-        testAnimationSamples(animation, idlName,
+        var animation =
+          target.animate({ [idlName]: [from, to],
+                           easing: 'cubic-bezier(0.68,0,1,0.01)' },
+                         { duration: 1000, fill: 'both' });
+        testAnimationSamples(animation, computedIdlName,
                              [{ time: 0,    expected: from.toLowerCase() },
                               { time: 940,  expected: from.toLowerCase() },
                               { time: 960,  expected: to.toLowerCase() }]);
-      }, property + ' uses discrete animation when animating between "'
-         + from + '" and "' + to + '" with keyframe easing');
+      }, propertyDesc + ' uses discrete animation when animating between "'
+       + from + '" and "' + to + '" with keyframe easing');
     });
   },
 
-  testAddition: function(property, setup, options) {
+  testAddition: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     options.forEach(function(keyframes) {
       var [ from, to ] = keyframes;
       test(function(t) {
-        var idlName = propertyToIDL(property);
+        var idlName = propertyToIDL(logical || property);
+        var computedIdlName = propertyToIDL(property);
         var target = createTestElement(t, setup);
         target.animate({ [idlName]: [from, from] }, 1000);
         var animation = target.animate({ [idlName]: [to, to] },
                                        { duration: 1000, composite: 'add' });
-        testAnimationSamples(animation, idlName,
+        testAnimationSamples(animation, computedIdlName,
                              [{ time: 0, expected: to.toLowerCase() }]);
-      }, property + ': "' + to + '" onto "' + from + '"');
+      }, propertyDesc + ': "' + to + '" onto "' + from + '"');
 
       test(function(t) {
-        var idlName = propertyToIDL(property);
+        var idlName = propertyToIDL(logical || property);
+        var computedIdlName = propertyToIDL(property);
         var target = createTestElement(t, setup);
         target.animate({ [idlName]: [to, to] }, 1000);
         var animation = target.animate({ [idlName]: [from, from] },
                                        { duration: 1000, composite: 'add' });
-        testAnimationSamples(animation, idlName,
+        testAnimationSamples(animation, computedIdlName,
                              [{ time: 0, expected: from.toLowerCase() }]);
-      }, property + ': "' + from + '" onto "' + to + '"');
+      }, propertyDesc + ': "' + from + '" onto "' + to + '"');
     });
   },
 
 };
 
 const lengthType = {
-  testInterpolation: function(property, setup) {
+  testInterpolation: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['10px', '50px'] },
                                      { duration: 1000, fill: 'both' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,    expected: '10px' },
                             { time: 500,  expected: '30px' },
                             { time: 1000, expected: '50px' }]);
-    }, property + ' supports animating as a length');
+    }, propertyDesc + ' supports animating as a length');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['1rem', '5rem'] },
                                      { duration: 1000, fill: 'both' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,    expected: '10px' },
                             { time: 500,  expected: '30px' },
                             { time: 1000, expected: '50px' }]);
-    }, property + ' supports animating as a length of rem');
+    }, propertyDesc + ' supports animating as a length of rem');
   },
 
-  testAddition: function(property, setup) {
+  testAddition: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '10px';
       var animation = target.animate({ [idlName]: ['10px', '50px'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px' }]);
-    }, property + ': length');
+      testAnimationSamples(animation, computedIdlName,
+                           [{ time: 0, expected: '20px' }]);
+    }, propertyDesc + ': length');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = '1rem';
       var animation = target.animate({ [idlName]: ['1rem', '5rem'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName, [{ time: 0, expected: '20px' }]);
-    }, property + ': length of rem');
+      testAnimationSamples(animation, computedIdlName,
+                           [{ time: 0, expected: '20px' }]);
+    }, propertyDesc + ': length of rem');
   },
 
 };
 
 const percentageType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
@@ -443,147 +478,161 @@ const visibilityType = {
                            [{ time: 0,    expected: 'hidden' },
                             { time: 1000, expected: 'visible' }]);
     }, property + ': onto "hidden"');
   },
 
 };
 
 const colorType = {
-  testInterpolation: function(property, setup) {
+  testInterpolation: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['rgb(255, 0, 0)',
                                                    'rgb(0, 0, 255)'] },
                                      1000);
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 500,  expected: 'rgb(128, 0, 128)' }]);
-    }, property + ' supports animating as color of rgb()');
+    }, propertyDesc + ' supports animating as color of rgb()');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['#ff0000', '#0000ff'] },
                                      1000);
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 500,  expected: 'rgb(128, 0, 128)' }]);
-    }, property + ' supports animating as color of #RGB');
+    }, propertyDesc + ' supports animating as color of #RGB');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['hsl(0,   100%, 50%)',
                                                    'hsl(240, 100%, 50%)'] },
                                      1000);
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 500,  expected: 'rgb(128, 0, 128)' }]);
-    }, property + ' supports animating as color of hsl()');
+    }, propertyDesc + ' supports animating as color of hsl()');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['#ff000066', '#0000ffcc'] },
                                      1000);
                                              // R: 255 * (0.4 * 0.5) / 0.6 = 85
                                              // G: 255 * (0.8 * 0.5) / 0.6 = 170
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 500,  expected: 'rgba(85, 0, 170, 0.6)' }]);
-    }, property + ' supports animating as color of #RGBa');
+    }, propertyDesc + ' supports animating as color of #RGBa');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['rgba(255, 0, 0, 0.4)',
                                                    'rgba(0, 0, 255, 0.8)'] },
                                      1000);
-      testAnimationSamples(animation, idlName,      // Same as above.
+      testAnimationSamples(animation, computedIdlName,      // Same as above.
                            [{ time: 500,  expected: 'rgba(85, 0, 170, 0.6)' }]);
-    }, property + ' supports animating as color of rgba()');
+    }, propertyDesc + ' supports animating as color of rgba()');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       var animation = target.animate({ [idlName]: ['hsla(0,   100%, 50%, 0.4)',
                                                    'hsla(240, 100%, 50%, 0.8)'] },
                                      1000);
-      testAnimationSamples(animation, idlName,      // Same as above.
+      testAnimationSamples(animation, computedIdlName,      // Same as above.
                            [{ time: 500,  expected: 'rgba(85, 0, 170, 0.6)' }]);
-    }, property + ' supports animating as color of hsla()');
+    }, propertyDesc + ' supports animating as color of hsla()');
   },
 
-  testAddition: function(property, setup) {
+  testAddition: function(property, setup, options, logical, mode) {
+    const propertyDesc = getPropertyDescription(property, logical, mode);
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['rgb(255, 0, 0)',
                                                    'rgb(0, 0, 255)'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,   expected: 'rgb(255, 128, 128)' },
                             // The value at 50% is interpolated
                             // from 'rgb(128+255, 128, 128)'
                             // to   'rgb(128,     128, 128+255)'.
                             { time: 500, expected: 'rgb(255, 128, 255)' }]);
-    }, property + ' supports animating as color of rgb() with overflowed ' +
+    }, propertyDesc + ' supports animating as color of rgb() with overflowed ' +
        'from and to values');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['#ff0000', '#0000ff'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,  expected: 'rgb(255, 128, 128)' }]);
-    }, property + ' supports animating as color of #RGB');
+    }, propertyDesc + ' supports animating as color of #RGB');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['hsl(0,   100%, 50%)',
                                                    'hsl(240, 100%, 50%)'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,  expected: 'rgb(255, 128, 128)' }]);
-    }, property + ' supports animating as color of hsl()');
+    }, propertyDesc + ' supports animating as color of hsl()');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['#ff000066', '#0000ffcc'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,
+      testAnimationSamples(animation, computedIdlName,
                            [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
-    }, property + ' supports animating as color of #RGBa');
+    }, propertyDesc + ' supports animating as color of #RGBa');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['rgba(255, 0, 0, 0.4)',
                                                    'rgba(0, 0, 255, 0.8)'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,      // Same as above.
+      testAnimationSamples(animation, computedIdlName,      // Same as above.
                            [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
-    }, property + ' supports animating as color of rgba()');
+    }, propertyDesc + ' supports animating as color of rgba()');
 
     test(function(t) {
-      var idlName = propertyToIDL(property);
+      var idlName = propertyToIDL(logical || property);
+      var computedIdlName = propertyToIDL(property);
       var target = createTestElement(t, setup);
       target.style[idlName] = 'rgb(128, 128, 128)';
       var animation = target.animate({ [idlName]: ['hsla(0,   100%, 50%, 0.4)',
                                                    'hsla(240, 100%, 50%, 0.8)'] },
                                      { duration: 1000, composite: 'add' });
-      testAnimationSamples(animation, idlName,      // Same as above.
+      testAnimationSamples(animation, computedIdlName,      // Same as above.
                            [{ time: 0,  expected: 'rgb(230, 128, 128)' }]);
-    }, property + ' supports animating as color of hsla()');
+    }, propertyDesc + ' supports animating as color of hsla()');
   },
 
 };
 
 const transformListType = {
   testInterpolation: function(property, setup) {
     test(function(t) {
       var idlName = propertyToIDL(property);
@@ -1019,24 +1068,55 @@ const positionType = {
                                      { duration: 1000, composite: 'add' });
       testAnimationSamples(
         animation, idlName,
         [{ time: 0, expected: calcFromPercentage(idlName, '130% 130%') }]);
     }, property + ': position of percentage');
   },
 };
 
+const logicalType = {
+  testInterpolation: function(property, setup, options) {
+    options.modes.forEach(mode => {
+      const physicalCSSProperty = gCSSProperties[mode.physical];
+      const setupFunction = t => {
+        const element = createTestElement(t, physicalCSSProperty.setup);
+        element.style.writingMode = mode.writingMode;
+        element.style.textOrientation = mode.textOrientation;
+        element.style.direction = mode.direction;
+        return element;
+      };
+      testInterpolation(mode.physical, physicalCSSProperty.types,
+                        setupFunction, property, mode);
+    });
+  },
+  testAddition: function(property, setup, options) {
+    options.modes.forEach(mode => {
+      const physicalCSSProperty = gCSSProperties[mode.physical];
+      const setupFunction = t => {
+        const element = createTestElement(t, physicalCSSProperty.setup);
+        element.style.writingMode = mode.writingMode;
+        element.style.textOrientation = mode.textOrientation;
+        element.style.direction = mode.direction;
+        return element;
+      };
+      testAddition(mode.physical, physicalCSSProperty.types,
+                   setupFunction, property, mode);
+    });
+  },
+};
+
 const types = {
   color: colorType,
   discrete: discreteType,
   filterList: filterListType,
   integer: integerType,
   length: lengthType,
   percentage: percentageType,
   lengthPercentageOrCalc: lengthPercentageOrCalcType,
   positiveNumber: positiveNumberType,
   transformList: transformListType,
   visibility: visibilityType,
   boxShadowList: boxShadowListType,
   textShadowList: textShadowListType,
   position: positionType,
+  logical: logicalType,
 };
-