<!doctype html>
<head>
<meta charset=utf-8>
<title>Bug 1254419 - Test the values returned by
       KeyframeEffect.getProperties()</title>
<script type="application/javascript" src="../testharness.js"></script>
<script type="application/javascript" src="../testharnessreport.js"></script>
<script type="application/javascript" src="../testcommon.js"></script>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1254419"
  target="_blank">Mozilla Bug 1254419</a>
<div id="log"></div>
<style>

:root {
  --var-100px: 100px;
  --var-100px-200px: 100px 200px;
}
div {
  font-size: 10px; /* For calculating em-based units */
}
</style>
<script>
'use strict';

var gTests = [

  // ---------------------------------------------------------------------
  //
  // Tests for property-indexed specifications
  //
  // ---------------------------------------------------------------------

  { desc:     'a one-property two-value property-indexed specification',
    frames:   { left: ['10px', '20px'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] } ]
  },
  { desc:     'a one-shorthand-property two-value property-indexed'
              + ' specification',
    frames:   { margin: ['10px', '10px 20px 30px 40px'] },
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '10px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] } ]
  },
  { desc:     'a two-property (one shorthand and one of its longhand'
              + ' components) two-value property-indexed specification',
    frames:   { marginTop: ['50px', '60px'],
                margin: ['10px', '10px 20px 30px 40px'] },
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '50px', 'replace', 'linear'),
                            valueFormat(1, '60px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] } ]
  },
  { desc:     'a two-property property-indexed specification with different'
              + ' numbers of values',
    frames:   { left: ['10px', '20px', '30px'],
                top: ['40px', '50px'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.5, '20px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '40px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] } ]
  },
  { desc:     'a property-indexed specification with an invalid value',
    frames:   { left: ['10px', '20px', '30px', '40px', '50px'],
                top:  ['15px', '25px', 'invalid', '45px', '55px'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.25, '20px', 'replace', 'linear'),
                            valueFormat(0.5, '30px', 'replace', 'linear'),
                            valueFormat(0.75, '40px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '15px', 'replace', 'linear'),
                            valueFormat(0.25, '25px', 'replace', 'linear'),
                            valueFormat(0.75, '45px', 'replace', 'linear'),
                            valueFormat(1, '55px', 'replace') ] } ]
  },
  { desc:     'a one-property two-value property-indexed specification that'
              + ' needs to stringify its values',
    frames:   { opacity: [0, 1] },
    expected: [ { property: 'opacity',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] } ]
  },
  { desc:     'a property-indexed keyframe where a lesser shorthand precedes'
              + ' a greater shorthand',
    frames:   { borderLeft: [ '1px solid rgb(1, 2, 3)',
                              '2px solid rgb(4, 5, 6)' ],
                border:     [ '3px dotted rgb(7, 8, 9)',
                              '4px dashed rgb(10, 11, 12)' ] },
    expected: [ { property: 'border-bottom-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-left-color',
                  values: [ valueFormat(0, 'rgb(1, 2, 3)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(4, 5, 6)', 'replace') ] },
                { property: 'border-right-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-top-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-bottom-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-left-width',
                  values: [ valueFormat(0, '1px', 'replace', 'linear'),
                            valueFormat(1, '2px', 'replace') ] },
                { property: 'border-right-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-top-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-bottom-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-left-style',
                  values: [ valueFormat(0, 'solid', 'replace', 'linear'),
                            valueFormat(1, 'solid', 'replace') ] },
                { property: 'border-right-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-top-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-image-outset',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '0', 'replace') ] },
                { property: 'border-image-repeat',
                  values: [ valueFormat(0, 'stretch', 'replace', 'linear'),
                            valueFormat(1, 'stretch', 'replace') ] },
                { property: 'border-image-slice',
                  values: [ valueFormat(0, '100%', 'replace', 'linear'),
                            valueFormat(1, '100%', 'replace') ] },
                { property: 'border-image-source',
                  values: [ valueFormat(0, 'none', 'replace', 'linear'),
                            valueFormat(1, 'none', 'replace') ] },
                { property: 'border-image-width',
                  values: [ valueFormat(0, '1', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] },
    ]
  },
  { desc:     'a property-indexed keyframe where a greater shorthand precedes'
              + ' a lesser shorthand',
    frames:   { border:     [ '3px dotted rgb(7, 8, 9)',
                              '4px dashed rgb(10, 11, 12)' ],
                borderLeft: [ '1px solid rgb(1, 2, 3)',
                              '2px solid rgb(4, 5, 6)' ] },
    expected: [ { property: 'border-bottom-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-left-color',
                  values: [ valueFormat(0, 'rgb(1, 2, 3)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(4, 5, 6)', 'replace') ] },
                { property: 'border-right-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-top-color',
                  values: [ valueFormat(0, 'rgb(7, 8, 9)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(10, 11, 12)', 'replace') ] },
                { property: 'border-bottom-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-left-width',
                  values: [ valueFormat(0, '1px', 'replace', 'linear'),
                            valueFormat(1, '2px', 'replace') ] },
                { property: 'border-right-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-top-width',
                  values: [ valueFormat(0, '3px', 'replace', 'linear'),
                            valueFormat(1, '4px', 'replace') ] },
                { property: 'border-bottom-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-left-style',
                  values: [ valueFormat(0, 'solid', 'replace', 'linear'),
                            valueFormat(1, 'solid', 'replace') ] },
                { property: 'border-right-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-top-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-image-outset',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '0', 'replace') ] },
                { property: 'border-image-repeat',
                  values: [ valueFormat(0, 'stretch', 'replace', 'linear'),
                            valueFormat(1, 'stretch', 'replace') ] },
                { property: 'border-image-slice',
                  values: [ valueFormat(0, '100%', 'replace', 'linear'),
                            valueFormat(1, '100%', 'replace') ] },
                { property: 'border-image-source',
                  values: [ valueFormat(0, 'none', 'replace', 'linear'),
                            valueFormat(1, 'none', 'replace') ] },
                { property: 'border-image-width',
                  values: [ valueFormat(0, '1', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] },
    ]
  },

  // ---------------------------------------------------------------------
  //
  // Tests for keyframe sequences
  //
  // ---------------------------------------------------------------------

  { desc:     'a keyframe sequence specification with repeated values at'
              + ' offset 0/1 with different easings',
    frames:   [ { offset: 0.0, left: '100px', easing: 'ease' },
                { offset: 0.0, left: '200px', easing: 'ease' },
                { offset: 0.5, left: '300px', easing: 'linear' },
                { offset: 1.0, left: '400px', easing: 'ease-out' },
                { offset: 1.0, left: '500px', easing: 'step-end' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '100px', 'replace'),
                            valueFormat(0, '200px', 'replace', 'ease'),
                            valueFormat(0.5, '300px', 'replace', 'linear'),
                            valueFormat(1, '400px', 'replace'),
                            valueFormat(1, '500px', 'replace') ] } ]
  },
  { desc:     'a one-property two-keyframe sequence',
    frames:   [ { offset: 0, left: '10px' },
                { offset: 1, left: '20px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] } ]
  },
  { desc:     'a two-property two-keyframe sequence',
    frames:   [ { offset: 0, left: '10px', top: '30px' },
                { offset: 1, left: '20px', top: '40px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '30px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] } ]
  },
  { desc:     'a one shorthand property two-keyframe sequence',
    frames:   [ { offset: 0, margin: '10px' },
                { offset: 1, margin: '20px 30px 40px 50px' } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] } ]
  },
  { desc:     'a two-property (a shorthand and one of its component longhands)'
              + ' two-keyframe sequence',
    frames:   [ { offset: 0, margin: '10px', marginTop: '20px' },
                { offset: 1, marginTop: '70px',
                             margin: '30px 40px 50px 60px' } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '20px', 'replace', 'linear'),
                            valueFormat(1, '70px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '60px', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence with duplicate values for a given interior'
              + ' offset',
    frames:   [ { offset: 0.0, left: '10px' },
                { offset: 0.5, left: '20px' },
                { offset: 0.5, left: '30px' },
                { offset: 0.5, left: '40px' },
                { offset: 1.0, left: '50px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.5, '20px', 'replace'),
                            valueFormat(0.5, '40px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence with duplicate values for offsets 0 and 1',
    frames:   [ { offset: 0, left: '10px' },
                { offset: 0, left: '20px' },
                { offset: 0, left: '30px' },
                { offset: 1, left: '40px' },
                { offset: 1, left: '50px' },
                { offset: 1, left: '60px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace'),
                            valueFormat(0, '30px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace'),
                            valueFormat(1, '60px', 'replace') ] } ]
  },
  { desc:     'a two-property four-keyframe sequence',
    frames:   [ { offset: 0, left: '10px' },
                { offset: 0, top: '20px' },
                { offset: 1, top: '30px' },
                { offset: 1, left: '40px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '40px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '20px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] } ]
  },
  { desc:     'a one-property keyframe sequence with some omitted offsets',
    frames:   [ { offset: 0.00, left: '10px' },
                { offset: 0.25, left: '20px' },
                { left: '30px' },
                { left: '40px' },
                { offset: 1.00, left: '50px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.25, '20px', 'replace', 'linear'),
                            valueFormat(0.5, '30px', 'replace', 'linear'),
                            valueFormat(0.75, '40px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] } ]
  },
  { desc:     'a two-property keyframe sequence with some omitted offsets',
    frames:   [ { offset: 0.00, left: '10px', top: '20px' },
                { offset: 0.25, left: '30px' },
                { left: '40px' },
                { left: '50px', top: '60px' },
                { offset: 1.00, left: '70px', top: '80px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.25, '30px', 'replace', 'linear'),
                            valueFormat(0.5, '40px', 'replace', 'linear'),
                            valueFormat(0.75, '50px', 'replace', 'linear'),
                            valueFormat(1, '70px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '20px', 'replace', 'linear'),
                            valueFormat(0.75, '60px', 'replace', 'linear'),
                            valueFormat(1, '80px', 'replace') ] } ]
  },
  { desc:     'a one-property keyframe sequence with all omitted offsets',
    frames:   [ { left: '10px' },
                { left: '20px' },
                { left: '30px' },
                { left: '40px' },
                { left: '50px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(0.25, '20px', 'replace', 'linear'),
                            valueFormat(0.5, '30px', 'replace', 'linear'),
                            valueFormat(0.75, '40px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence with different easing values, but the'
              + ' same easing value for a given offset',
    frames:   [ { offset: 0.0, easing: 'ease',     left: '10px'},
                { offset: 0.0, easing: 'ease',     top: '20px'},
                { offset: 0.5, easing: 'linear',   left: '30px' },
                { offset: 0.5, easing: 'linear',   top: '40px' },
                { offset: 1.0, easing: 'step-end', left: '50px' },
                { offset: 1.0, easing: 'step-end', top: '60px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'ease'),
                            valueFormat(0.5, '30px', 'replace', 'linear'),
                            valueFormat(1, '50px', 'replace') ] },
                { property: 'top',
                  values: [ valueFormat(0, '20px', 'replace', 'ease'),
                            valueFormat(0.5, '40px', 'replace', 'linear'),
                            valueFormat(1, '60px', 'replace') ] } ]
  },
  { desc:     'a one-property two-keyframe sequence that needs to'
              + ' stringify its values',
    frames:   [ { offset: 0, opacity: 0 },
                { offset: 1, opacity: 1 } ],
    expected: [ { property: 'opacity',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence where shorthand precedes longhand',
    frames:   [ { offset: 0, margin: '10px', marginRight: '20px' },
                { offset: 1, margin: '30px' } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '20px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence where longhand precedes shorthand',
    frames:   [ { offset: 0, marginRight: '20px', margin: '10px' },
                { offset: 1, margin: '30px' } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '20px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '30px', 'replace') ] } ]
  },
  { desc:     'a keyframe sequence where lesser shorthand precedes greater'
              + ' shorthand',
    frames:   [ { offset: 0, borderLeft: '1px solid rgb(1, 2, 3)',
                             border: '2px dotted rgb(4, 5, 6)' },
                { offset: 1, border: '3px dashed rgb(7, 8, 9)' } ],
    expected: [ { property: 'border-bottom-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-left-color',
                  values: [ valueFormat(0, 'rgb(1, 2, 3)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-right-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-top-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-bottom-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-left-width',
                  values: [ valueFormat(0, '1px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-right-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-top-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-bottom-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-left-style',
                  values: [ valueFormat(0, 'solid', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-right-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-top-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-image-outset',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '0', 'replace') ] },
                { property: 'border-image-repeat',
                  values: [ valueFormat(0, 'stretch', 'replace', 'linear'),
                            valueFormat(1, 'stretch', 'replace') ] },
                { property: 'border-image-slice',
                  values: [ valueFormat(0, '100%', 'replace', 'linear'),
                            valueFormat(1, '100%', 'replace') ] },
                { property: 'border-image-source',
                  values: [ valueFormat(0, 'none', 'replace', 'linear'),
                            valueFormat(1, 'none', 'replace') ] },
                { property: 'border-image-width',
                  values: [ valueFormat(0, '1', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] },
    ]
  },
  { desc:     'a keyframe sequence where greater shorthand precedes'
              + ' lesser shorthand',
    frames:   [ { offset: 0, border: '2px dotted rgb(4, 5, 6)',
                             borderLeft: '1px solid rgb(1, 2, 3)' },
                { offset: 1, border: '3px dashed rgb(7, 8, 9)' } ],
    expected: [ { property: 'border-bottom-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-left-color',
                  values: [ valueFormat(0, 'rgb(1, 2, 3)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-right-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-top-color',
                  values: [ valueFormat(0, 'rgb(4, 5, 6)', 'replace', 'linear'),
                            valueFormat(1, 'rgb(7, 8, 9)', 'replace') ] },
                { property: 'border-bottom-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-left-width',
                  values: [ valueFormat(0, '1px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-right-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-top-width',
                  values: [ valueFormat(0, '2px', 'replace', 'linear'),
                            valueFormat(1, '3px', 'replace') ] },
                { property: 'border-bottom-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-left-style',
                  values: [ valueFormat(0, 'solid', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-right-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-top-style',
                  values: [ valueFormat(0, 'dotted', 'replace', 'linear'),
                            valueFormat(1, 'dashed', 'replace') ] },
                { property: 'border-image-outset',
                  values: [ valueFormat(0, '0', 'replace', 'linear'),
                            valueFormat(1, '0', 'replace') ] },
                { property: 'border-image-repeat',
                  values: [ valueFormat(0, 'stretch', 'replace', 'linear'),
                            valueFormat(1, 'stretch', 'replace') ] },
                { property: 'border-image-slice',
                  values: [ valueFormat(0, '100%', 'replace', 'linear'),
                            valueFormat(1, '100%', 'replace') ] },
                { property: 'border-image-source',
                  values: [ valueFormat(0, 'none', 'replace', 'linear'),
                            valueFormat(1, 'none', 'replace') ] },
                { property: 'border-image-width',
                  values: [ valueFormat(0, '1', 'replace', 'linear'),
                            valueFormat(1, '1', 'replace') ] },
    ]
  },

  // ---------------------------------------------------------------------
  //
  // Tests for unit conversion
  //
  // ---------------------------------------------------------------------

  { desc:     'em units are resolved to px values',
    frames:   { left: ['10em', '20em'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '100px', 'replace', 'linear'),
                            valueFormat(1, '200px', 'replace') ] } ]
  },
  { desc:     'calc() expressions are resolved to the equivalent units',
    frames:   { left: ['calc(10em + 10px)', 'calc(10em + 10%)'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '110px', 'replace', 'linear'),
                            valueFormat(1, 'calc(10% + 100px)', 'replace') ] } ]
  },

  // ---------------------------------------------------------------------
  //
  // Tests for CSS variable handling conversion
  //
  // ---------------------------------------------------------------------

  { desc:     'CSS variables are resolved to their corresponding values',
    frames:   { left: ['10px', 'var(--var-100px)'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '100px', 'replace') ] } ]
  },
  { desc:     'CSS variables in calc() expressions are resolved',
    frames:   { left: ['10px', 'calc(var(--var-100px) / 2 - 10%)'] },
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, 'calc(-10% + 50px)', 'replace') ] } ]
  },
  { desc:     'CSS variables in shorthands are resolved to their corresponding'
              + ' values',
    frames:   { margin: ['10px', 'var(--var-100px-200px)'] },
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '100px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '200px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '100px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '200px', 'replace') ] } ]
  },

  // ---------------------------------------------------------------------
  //
  // Tests for properties that parse correctly but which we fail to
  // convert to computed values.
  //
  // ---------------------------------------------------------------------

  { desc:     'a missing property in initial keyframe',
    frames:   [ { },
                { margin: '5px' } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] } ]
  },
  { desc:     'a missing property in initial keyframe and there are some ' +
              'keyframes with the same offset',
    frames:   [ { },
                { margin: '10px', offset: 0.5 },
                { margin: '20px', offset: 0.5 },
                { margin: '30px'} ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(0.5, '10px',  'replace'),
                            valueFormat(0.5, '20px',  'replace', 'linear'),
                            valueFormat(1,   '30px',  'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(0.5, '10px',  'replace'),
                            valueFormat(0.5, '20px',  'replace', 'linear'),
                            valueFormat(1,   '30px',  'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(0.5, '10px',  'replace'),
                            valueFormat(0.5, '20px',  'replace', 'linear'),
                            valueFormat(1,   '30px',  'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(0.5, '10px',  'replace'),
                            valueFormat(0.5, '20px',  'replace', 'linear'),
                            valueFormat(1,   '30px',  'replace') ] } ]
  },
  { desc:     'a missing property in final keyframe',
    frames:   [ { margin: '5px' },
                { } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] } ]
  },
  { desc:     'a missing property in final keyframe and there are some ' +
              'keyframes with the same offsets',
    frames:   [ { margin: '5px' },
                { margin: '10px', offset: 0.5 },
                { margin: '20px', offset: 0.5 },
                { } ],
    expected: [ { property: 'margin-top',
                  values: [ valueFormat(0,   '5px', 'replace', 'linear'),
                            valueFormat(0.5, '10px', 'replace'),
                            valueFormat(0.5, '20px', 'replace', 'linear'),
                            valueFormat(1,   undefined, 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(0.5, '10px', 'replace'),
                            valueFormat(0.5, '20px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(0.5, '10px', 'replace'),
                            valueFormat(0.5, '20px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(0.5, '10px', 'replace'),
                            valueFormat(0.5, '20px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] } ]
  },
  { desc:     'a missing property in final keyframe where it forms the last'
              + ' segment in the series',
    frames:   [ { margin: '5px' },
                { marginLeft: '5px',
                  marginRight: '5px',
                  marginBottom: '5px' } ],
    expected: [ { property: 'margin-bottom',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-top',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] } ]
  },
  { desc:     'a missing property in initial keyframe along with other values',
    frames:   [ {                left: '10px' },
                { margin: '5px', left: '20px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'margin-top',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '5px', 'replace') ] } ]
  },
  { desc:     'a missing property in final keyframe along with other values',
    frames:   [ { margin: '5px', left: '10px' },
                {                left: '20px' } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '20px', 'replace') ] },
                { property: 'margin-top',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-right',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-bottom',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] },
                { property: 'margin-left',
                  values: [ valueFormat(0, '5px', 'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] } ]
  },
  { desc:     'missing properties in both of initial and final keyframe',
    frames:   [ { left: '5px', offset: 0.5 } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0,   undefined, 'replace', 'linear'),
                            valueFormat(0.5, '5px',     'replace', 'linear'),
                            valueFormat(1,   undefined, 'replace') ] } ]
  },
  { desc:     'missing propertes in both of initial and final keyframe along '
              + 'with other values',
    frames:   [ { left:  '5px',  offset: 0 },
                { right: '5px',  offset: 0.5 },
                { left:  '10px', offset: 1 } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '5px',  'replace', 'linear'),
                            valueFormat(1, '10px', 'replace') ] },
                { property: 'right',
                  values: [ valueFormat(0,   undefined, 'replace', 'linear'),
                            valueFormat(0.5, '5px',     'replace', 'linear'),
                            valueFormat(1,   undefined, 'replace') ] } ]
  },

  { desc:     'a missing property in final keyframe with duplicate offset ' +
              + 'along with other values',
    frames:   [ { left: '5px',  right: '5px', offset: 0 },
                { left: '8px',  right: '8px', offset: 0 },
                { left: '10px',               offset: 1 } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '5px',  'replace'),
                            valueFormat(0, '8px',  'replace', 'linear'),
                            valueFormat(1, '10px', 'replace') ] },
                { property: 'right',
                  values: [ valueFormat(0, '5px',     'replace'),
                            valueFormat(0, '8px',     'replace', 'linear'),
                            valueFormat(1, undefined, 'replace') ] } ]
  },

  { desc:     'a missing property in initial keyframe with duplicate offset '
              + 'along with other values',
    frames:   [ { left: '10px',              offset: 0 },
                { left: '8px', right: '8px', offset: 1 },
                { left: '5px', right: '5px', offset: 1 } ],
    expected: [ { property: 'left',
                  values: [ valueFormat(0, '10px', 'replace', 'linear'),
                            valueFormat(1, '8px',  'replace'),
                            valueFormat(1, '5px',  'replace') ] },
                { property: 'right',
                  values: [ valueFormat(0, undefined, 'replace', 'linear'),
                            valueFormat(1, '8px',     'replace'),
                            valueFormat(1, '5px',     'replace') ] } ]
  },
];

SpecialPowers.pushPrefEnv(
  {
    set: [
      ["dom.animations-api.core.enabled", true],
      ["dom.animations-api.implicit-keyframes.enabled", true],
    ],
  },
  function() {
    gTests.forEach(function(subtest) {
      test(function(t) {
        var div = addDiv(t);
        var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
        // Flush styles since getProperties currently does not. Rather, it
        // returns the actual properties in use at the current time.
        // However, we want to test what these properties will look like
        // after the next restyle.
        getComputedStyle(div).opacity;
        assert_properties_equal(
          animation.effect.getProperties(),
          subtest.expected
        );
      }, subtest.desc);
    });

    done();
  }
);

</script>
</body>
