Update web-platform-tests to revision 0a518aaff73532a26e175789f7e75fa99593ac64

This commit is contained in:
Ms2ger 2016-04-21 11:33:07 +02:00
parent 9c172f49d0
commit abcd4b654f
92 changed files with 2869 additions and 642 deletions

View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>easing tests</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-easing">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<script src="../resources/effect-easing-tests.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
function assert_progress(animation, currentTime, easingFunction) {
animation.currentTime = currentTime;
var portion = currentTime / animation.effect.timing.duration;
assert_approx_equals(animation.effect.getComputedTiming().progress,
easingFunction(portion),
0.01,
'The progress of the animation should be approximately ' +
easingFunction(portion) + ' at ' + currentTime + 'ms');
}
gEffectEasingTests.forEach(function(options) {
test(function(t) {
var target = createDiv(t);
var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ],
{ duration: 1000 * MS_PER_SEC,
fill: 'forwards' });
anim.effect.timing.easing = options.easing;
assert_equals(anim.effect.timing.easing, options.easing);
var easing = options.easingFunction;
assert_progress(anim, 0, easing);
assert_progress(anim, 250 * MS_PER_SEC, easing);
assert_progress(anim, 500 * MS_PER_SEC, easing);
assert_progress(anim, 750 * MS_PER_SEC, easing);
assert_progress(anim, 1000 * MS_PER_SEC, easing);
}, options.desc);
});
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
assert_throws({ name: 'TypeError' },
function() {
anim.effect.timing.easing = '';
});
assert_throws({ name: 'TypeError' },
function() {
anim.effect.timing.easing = 'test';
});
}, 'Test invalid easing value');
test(function(t) {
var delay = 1000 * MS_PER_SEC;
var target = createDiv(t);
var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ],
{ duration: 1000 * MS_PER_SEC,
fill: 'both',
delay: delay,
easing: 'steps(2, start)' });
anim.effect.timing.easing = 'steps(2, end)';
assert_equals(anim.effect.getComputedTiming().progress, 0,
'easing replace to steps(2, end) at before phase');
anim.currentTime = delay + 750 * MS_PER_SEC;
assert_equals(anim.effect.getComputedTiming().progress, 0.5,
'change currentTime to active phase');
anim.effect.timing.easing = 'steps(2, start)';
assert_equals(anim.effect.getComputedTiming().progress, 1,
'easing replace to steps(2, start) at active phase');
anim.currentTime = delay + 1500 * MS_PER_SEC;
anim.effect.timing.easing = 'steps(2, end)';
assert_equals(anim.effect.getComputedTiming().progress, 1,
'easing replace to steps(2, end) again at after phase');
}, 'Change the easing while the animation is running');
</script>
</body>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>fill tests</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-fill">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
'use strict';
["none", "forwards", "backwards", "both", ].forEach(function(fill){
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100);
anim.effect.timing.fill = fill;
assert_equals(anim.effect.timing.fill, fill, 'set fill ' + fill);
assert_equals(anim.effect.getComputedTiming().fill, fill, 'getComputedTiming() after set fill ' + fill);
}, 'set fill ' + fill);
});
</script>
</body>

View file

@ -0,0 +1,136 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests for discrete animation</title>
<link rel="help" href="http://w3c.github.io/web-animations/#animatable-as-string-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
'use strict';
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ fontStyle: [ 'normal', 'italic' ] },
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).fontStyle, 'normal',
'Animation produces \'from\' value at start of interval');
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
assert_equals(getComputedStyle(div).fontStyle, 'normal',
'Animation produces \'from\' value just before the middle of'
+ ' the interval');
anim.currentTime++;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'to\' value at exact middle of'
+ ' the interval');
anim.finish();
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'to\' value during forwards fill');
}, 'Test animating discrete values');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
var anim = div.animate({ height: [ 'auto', '200px' ] },
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value at start of interval');
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value just before the middle of'
+ ' the interval');
anim.currentTime++;
assert_equals(getComputedStyle(div).height, '200px',
'Animation produces \'to\' value at exact middle of'
+ ' the interval');
anim.finish();
assert_equals(getComputedStyle(div).height, '200px',
'Animation produces \'to\' value during forwards fill');
}, 'Test discrete animation is used when interpolation fails');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
var anim = div.animate({ height: [ 'auto',
'200px',
'300px',
'auto',
'400px' ] },
{ duration: 1000, fill: 'forwards' });
// There are five values, so there are four pairs to try to interpolate.
// We test at the middle of each pair.
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value at start of interval');
anim.currentTime = 125;
assert_equals(getComputedStyle(div).height, '200px',
'First non-interpolable pair uses discrete interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, '250px',
'Second interpolable pair uses linear interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, originalHeight,
'Third non-interpolable pair uses discrete interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, '400px',
'Fourth non-interpolable pair uses discrete interpolation');
}, 'Test discrete animation is used only for pairs of values that cannot'
+ ' be interpolated');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
// 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 anim = div.animate({ fontStyle: [ 'italic', 'oblique' ] },
{ duration: 1000, fill: 'forwards',
easing: 'cubic-bezier(0.68,0,1,0.01)' });
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at start of interval');
anim.currentTime = 940;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at 94% of the iteration'
+ ' 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'
+ ' effect easing');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
// 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 anim = div.animate([ { fontStyle: 'italic',
easing: 'cubic-bezier(0.68,0,1,0.01)' },
{ fontStyle: 'oblique' } ],
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at start of interval');
anim.currentTime = 940;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at 94% of the iteration'
+ ' 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');
</script>

View file

@ -0,0 +1,120 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests for not animatable properties</title>
<link rel="help" href="https://w3c.github.io/web-animations/#not-animatable-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ display: [ 'inline', 'inline-block' ] }, 1000);
assert_equals(anim.effect.getFrames().length, 0,
'Animation specified using property-indexed notation but'
+ ' consisting of only non-animatable properties should not'
+ ' contain any keyframes');
}, '\'display\' property cannot be animated using property-indexed notation');
test(function(t) {
var div = createDiv(t);
var anim = div.animate([ { display: 'inline' }, { display: 'inline-block' } ],
1000);
assert_equals(anim.effect.getFrames().length, 2,
'Animation specified using a keyframe sequence where each'
+ ' keyframe contains only non-animatable properties should'
+ ' return an equal number of (empty) keyframes');
assert_false(anim.effect.getFrames()[0].hasOwnProperty('display'),
'Initial keyframe should not have the \'display\' property');
assert_false(anim.effect.getFrames()[1].hasOwnProperty('display'),
'Final keyframe should not have the \'display\' property');
}, '\'display\' property cannot be animated using a keyframe sequence');
test(function(t) {
var properties = {
// CSS Animations properties
animation: [ 'anim 1s', 'anim 2s' ],
animationName: [ 'abc', 'xyz' ],
animationTimingFunction: [ 'ease', 'steps(2)' ],
animationDelay: [ '1s', '2s' ],
animationIterationCount: [ 1, 2 ],
animationDirection: [ 'normal', 'reverse' ],
animationFillMode: [ 'forwards', 'backwards' ],
animationPlayState: [ 'paused', 'running' ],
// CSS Transitions properties
transition: [ 'all 1s', 'all 2s' ],
transitionDelay: [ '1s', '2s' ],
transitionDuration: [ '1s', '2s' ],
transitionProperty: [ 'all', 'opacity' ],
transitionTimingFunction: [ 'ease', 'ease-out' ]
};
var div = createDiv(t);
var anim = div.animate(properties, 1000);
assert_equals(anim.effect.getFrames().length, 0,
'Animation specified using property-indexed notation but'
+ ' consisting of only non-animatable properties should not'
+ ' contain any keyframes');
}, 'CSS animations and CSS transitions properties cannot be animated using'
+ ' property-indexed notation');
test(function(t) {
var frames = [
{
animation: 'anim 1s',
animationName: 'abc',
animationTimingFunction: 'ease',
animationDelay: '1s',
animationIterationCount: 1,
animationDirection: 'normal',
animationFillMode: 'forwards',
animationPlayState: 'paused',
transition: 'all 1s',
transitionDelay: '1s',
transitionDuration: '1s',
transitionProperty: 'opacity',
transitionTimingFunction: 'ease'
},
{
animation: 'anim 2s',
animationName: 'xyz',
animationTimingFunction: 'steps(2)',
animationDelay: '2s',
animationIterationCount: 2,
animationDirection: 'reverse',
animationFillMode: 'backwards',
animationPlayState: 'running',
transition: 'all 2s',
transitionDelay: '2s',
transitionDuration: '2s',
transitionProperty: 'all',
transitionTimingFunction: 'ease-out'
}
];
var defaultKeyframeProperties = [ 'computedOffset', 'easing', 'offset' ];
var div = createDiv(t);
var anim = div.animate(frames, 1000);
assert_equals(anim.effect.getFrames().length, 2,
'Animation specified using a keyframe sequence where each'
+ ' keyframe contains only non-animatable properties should'
+ ' return an equal number of (empty) keyframes');
assert_array_equals(Object.keys(anim.effect.getFrames()[0]),
defaultKeyframeProperties,
'Initial keyframe should not contain any properties other'
+ ' than the default keyframe properties');
assert_array_equals(Object.keys(anim.effect.getFrames()[1]),
defaultKeyframeProperties,
'Final keyframe should not contain any properties other'
+ ' than the default keyframe properties');
}, 'CSS animations and CSS transitions properties cannot be animated using'
+ ' a sequence of keyframes');
</script>

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests that property values respond to changes to their context</title>
<link rel="help" href="https://w3c.github.io/web-animations/#keyframes-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
test(function(t) {
var div = createDiv(t);
div.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.currentTime = 500;
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value before updating font-size');
div.style.fontSize = '20px';
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size');
}, 'Effect values reflect changes to font-size on element');
test(function(t) {
var parentDiv = createDiv(t);
var div = createDiv(t);
parentDiv.appendChild(div);
parentDiv.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.currentTime = 500;
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value before updating font-size on parent element');
parentDiv.style.fontSize = '20px';
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size on parent element');
}, 'Effect values reflect changes to font-size on parent element');
promise_test(function(t) {
var parentDiv = createDiv(t);
var div = createDiv(t);
parentDiv.appendChild(div);
parentDiv.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.pause();
animation.currentTime = 500;
parentDiv.style.fontSize = '20px';
return animation.ready.then(function() {
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size on parent element');
});
}, 'Effect values reflect changes to font-size when computed style is not'
+ ' immediately flushed');
promise_test(function(t) {
var divWith10pxFontSize = createDiv(t);
divWith10pxFontSize.style.fontSize = '10px';
var divWith20pxFontSize = createDiv(t);
divWith20pxFontSize.style.fontSize = '20px';
var div = createDiv(t);
div.remove(); // Detach
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.pause();
return animation.ready.then(function() {
animation.currentTime = 500;
divWith10pxFontSize.appendChild(div);
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value after attaching to font-size:10px parent');
divWith20pxFontSize.appendChild(div);
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after attaching to font-size:20px parent');
});
}, 'Effect values reflect changes to font-size from reparenting');
</script>
</body>

View file

@ -0,0 +1,371 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.finished</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-finished">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
return animation.ready.then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing starts');
animation.pause();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise does not change when pausing');
animation.play();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise does not change when play() unpauses');
animation.currentTime = 100 * MS_PER_SEC;
return animation.finished;
}).then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing completes');
});
}, 'Test pausing then playing does not change the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing completes');
animation.play();
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise changes when replaying animation');
previousFinishedPromise = animation.finished;
animation.play();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same after redundant play() call');
});
}, 'Test restarting a finished animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise;
animation.finish();
return animation.finished.then(function() {
previousFinishedPromise = animation.finished;
animation.playbackRate = -1;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should be replaced when reversing a ' +
'finished promise');
animation.currentTime = 0;
return animation.finished;
}).then(function() {
previousFinishedPromise = animation.finished;
animation.play();
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise is replaced after play() call on ' +
'finished, reversed animation');
});
}, 'Test restarting a reversed finished animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.currentTime = 100 * MS_PER_SEC + 1000;
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is unchanged jumping past end of ' +
'finished animation');
});
}, 'Test redundant finishing of animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
// Setup callback to run if finished promise is resolved
var finishPromiseResolved = false;
animation.finished.then(function() {
finishPromiseResolved = true;
});
return animation.ready.then(function() {
// Jump to mid-way in interval and pause
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
return animation.ready;
}).then(function() {
// Jump to the end
// (But don't use finish() since that should unpause as well)
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
}).then(function() {
assert_false(finishPromiseResolved,
'Finished promise should not resolve when paused');
});
}, 'Finished promise does not resolve when paused');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
// Setup callback to run if finished promise is resolved
var finishPromiseResolved = false;
animation.finished.then(function() {
finishPromiseResolved = true;
});
return animation.ready.then(function() {
// Jump to mid-way in interval and pause
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
// Jump to the end
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
}).then(function() {
assert_false(finishPromiseResolved,
'Finished promise should not resolve when pause-pending');
});
}, 'Finished promise does not resolve when pause-pending');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.finish();
return animation.finished.then(function(resolvedAnimation) {
assert_equals(resolvedAnimation, animation,
'Object identity of animation passed to Promise callback'
+ ' matches the animation object owning the Promise');
});
}, 'The finished promise is fulfilled with its Animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
// Set up listeners on finished promise
var retPromise = animation.finished.then(function() {
assert_unreached('finished promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'finished promise is rejected with AbortError');
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change after the original is ' +
'rejected');
});
animation.cancel();
return retPromise;
}, 'finished promise is rejected when an animation is cancelled by calling ' +
'cancel()');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.cancel();
assert_not_equals(animation.finished, previousFinishedPromise,
'A new finished promise should be created when'
+ ' cancelling a finished animation');
});
}, 'cancelling an already-finished animation replaces the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.cancel();
// The spec says we still create a new finished promise and reject the old
// one even if we're already idle. That behavior might change, but for now
// test that we do that.
var retPromise = animation.finished.catch(function(err) {
assert_equals(err.name, 'AbortError',
'finished promise is rejected with AbortError');
});
// Redundant call to cancel();
var previousFinishedPromise = animation.finished;
animation.cancel();
assert_not_equals(animation.finished, previousFinishedPromise,
'A redundant call to cancel() should still generate a new'
+ ' finished promise');
return retPromise;
}, 'cancelling an idle animation still replaces the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
const HALF_DUR = 100 * MS_PER_SEC / 2;
const QUARTER_DUR = 100 * MS_PER_SEC / 4;
var gotNextFrame = false;
var currentTimeBeforeShortening;
animation.currentTime = HALF_DUR;
return animation.ready.then(function() {
currentTimeBeforeShortening = animation.currentTime;
animation.effect.timing.duration = QUARTER_DUR;
// Below we use gotNextFrame to check that shortening of the animation
// duration causes the finished promise to resolve, rather than it just
// getting resolved on the next animation frame. This relies on the fact
// that the promises are resolved as a micro-task before the next frame
// happens.
waitForAnimationFrames(1).then(function() {
gotNextFrame = true;
});
return animation.finished;
}).then(function() {
assert_false(gotNextFrame, 'shortening of the animation duration should ' +
'resolve the finished promise');
assert_equals(animation.currentTime, currentTimeBeforeShortening,
'currentTime should be unchanged when duration shortened');
var previousFinishedPromise = animation.finished;
animation.effect.timing.duration = 100 * MS_PER_SEC;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change after lengthening the ' +
'duration causes the animation to become active');
});
}, 'Test finished promise changes for animation duration changes');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var retPromise = animation.ready.then(function() {
animation.playbackRate = 0;
animation.currentTime = 100 * MS_PER_SEC + 1000;
return waitForAnimationFrames(2);
});
animation.finished.then(t.step_func(function() {
assert_unreached('finished promise should not resolve when playbackRate ' +
'is zero');
}));
return retPromise;
}, 'Test finished promise changes when playbackRate == 0');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.playbackRate = -1;
return animation.finished;
});
}, 'Test finished promise resolves when reaching to the natural boundary.');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.currentTime = 0;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change once a prior ' +
'finished promise resolved and the animation ' +
'falls out finished state');
});
}, 'Test finished promise changes when a prior finished promise resolved ' +
'and the animation falls out finished state');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
assert_equals(animation.finished, previousFinishedPromise,
'No new finished promise generated when finished state ' +
'is checked asynchronously');
}, 'Test no new finished promise generated when finished state ' +
'is checked asynchronously');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
animation.currentTime = 100 * MS_PER_SEC / 2;
assert_not_equals(animation.finished, previousFinishedPromise,
'New finished promise generated when finished state ' +
'is checked synchronously');
}, 'Test new finished promise generated when finished state ' +
'is checked synchronously');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var resolvedFinished = false;
animation.finished.then(function() {
resolvedFinished = true;
});
return animation.ready.then(function() {
animation.finish();
animation.currentTime = 100 * MS_PER_SEC / 2;
}).then(function() {
assert_true(resolvedFinished,
'Animation.finished should be resolved even if ' +
'the finished state is changed soon');
});
}, 'Test synchronous finished promise resolved even if finished state ' +
'is changed soon');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var resolvedFinished = false;
animation.finished.then(function() {
resolvedFinished = true;
});
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.finish();
}).then(function() {
assert_true(resolvedFinished,
'Animation.finished should be resolved soon after finish() is ' +
'called even if there are other asynchronous promises just before it');
});
}, 'Test synchronous finished promise resolved even if asynchronous ' +
'finished promise happens just before synchronous promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.finished.then(t.step_func(function() {
assert_unreached('Animation.finished should not be resolved');
}));
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
});
}, 'Test finished promise is not resolved when the animation ' +
'falls out finished state immediately');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.finished.then(t.step_func(function() {
assert_unreached('Animation.finished should not be resolved');
}));
animation.currentTime = 0;
});
}, 'Test finished promise is not resolved once the animation ' +
'falls out finished state even though the current finished ' +
'promise is generated soon after animation state became finished');
</script>
</body>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.id</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-id">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
assert_equals(animation.id, '', 'id for CSS Animation is initially empty');
animation.id = 'anim'
assert_equals(animation.id, 'anim', 'animation.id reflects the value set');
}, 'Animation.id for CSS Animations');
</script>
</body>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.oncancel</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-oncancel">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then().catch(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.oncancel = t.step_func_done(function(event) {
assert_equals(event.currentTime, null,
'event.currentTime should be null');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is rejected');
});
animation.cancel();
}, 'oncancel event is fired when animation.cancel() is called.');
</script>
</body>

View file

@ -0,0 +1,122 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.onfinish</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-onfinish">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 0,
'event.currentTime should be zero');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.playbackRate = -1;
}, 'onfinish event is fired when the currentTime < 0 and ' +
'the playbackRate < 0');
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 100 * MS_PER_SEC,
'event.currentTime should be the effect end');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.currentTime = 100 * MS_PER_SEC;
}, 'onfinish event is fired when the currentTime > 0 and ' +
'the playbackRate > 0');
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 100 * MS_PER_SEC,
'event.currentTime should be the effect end');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.finish();
}, 'onfinish event is fired when animation.finish() is called');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when paused');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
return animation.ready.then(function() {
animation.playbackRate = 0;
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when the playbackRate is zero');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when the animation falls out ' +
'finished state immediately');
</script>
</body>

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.pause()</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-pause">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
var previousCurrentTime = animation.currentTime;
return animation.ready.then(waitForAnimationFrames(1)).then(function() {
assert_true(animation.currentTime >= previousCurrentTime,
'currentTime is initially increasing');
animation.pause();
return animation.ready;
}).then(function() {
previousCurrentTime = animation.currentTime;
return waitForAnimationFrames(1);
}).then(function() {
assert_equals(animation.currentTime, previousCurrentTime,
'currentTime does not increase after calling pause()');
});
}, 'pause() a running animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
// Go to idle state then pause
animation.cancel();
animation.pause();
assert_equals(animation.currentTime, 0, 'currentTime is set to 0');
assert_equals(animation.startTime, null, 'startTime is not set');
assert_equals(animation.playState, 'pending', 'initially pause-pending');
// Check it still resolves as expected
return animation.ready.then(function() {
assert_equals(animation.playState, 'paused',
'resolves to paused state asynchronously');
assert_equals(animation.currentTime, 0,
'keeps the initially set currentTime');
});
}, 'pause() from idle');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
animation.cancel();
animation.playbackRate = -1;
animation.pause();
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'currentTime is set to the effect end');
return animation.ready.then(function() {
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'keeps the initially set currentTime');
});
}, 'pause() from idle with a negative playbackRate');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 1000 * MS_PER_SEC,
iterations: Infinity});
animation.cancel();
animation.playbackRate = -1;
assert_throws('InvalidStateError',
function () { animation.pause(); },
'Expect InvalidStateError exception on calling pause() ' +
'from idle with a negative playbackRate and ' +
'infinite-duration animation');
}, 'pause() from idle with a negative playbackRate and endless effect');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
return animation.ready
.then(function(animation) {
animation.finish();
animation.pause();
return animation.ready;
}).then(function(animation) {
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'currentTime after pausing finished animation');
});
}, 'pause() on a finished animation');
</script>
</body>

View file

@ -0,0 +1,97 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.ready</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-ready">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var originalReadyPromise = animation.ready;
var pauseReadyPromise;
return animation.ready.then(function() {
assert_equals(animation.ready, originalReadyPromise,
'Ready promise is the same object when playing completes');
animation.pause();
assert_not_equals(animation.ready, originalReadyPromise,
'A new ready promise is created when pausing');
pauseReadyPromise = animation.ready;
// Wait for the promise to fulfill since if we abort the pause the ready
// promise object is reused.
return animation.ready;
}).then(function() {
animation.play();
assert_not_equals(animation.ready, pauseReadyPromise,
'A new ready promise is created when playing');
});
}, 'A new ready promise is created when play()/pause() is called');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
var promiseBeforeCallingPlay = animation.ready;
animation.play();
assert_equals(animation.ready, promiseBeforeCallingPlay,
'Ready promise has same object identity after redundant call'
+ ' to play()');
});
}, 'Redundant calls to play() do not generate new ready promise objects');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function(resolvedAnimation) {
assert_equals(resolvedAnimation, animation,
'Object identity of Animation passed to Promise callback'
+ ' matches the Animation object owning the Promise');
});
}, 'The ready promise is fulfilled with its Animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var retPromise = animation.ready.then(function() {
assert_unreached('ready promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'ready promise is rejected with AbortError');
});
animation.cancel();
return retPromise;
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ ' calling cancel()');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.pause();
// Set up listeners on pause-pending ready promise
var retPromise = animation.ready.then(function() {
assert_unreached('ready promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'ready promise is rejected with AbortError');
});
animation.cancel();
return retPromise;
});
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ ' calling cancel()');
</script>
</body>

View file

@ -0,0 +1,150 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.reverse()</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-reverse">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
// Wait a frame because if currentTime is still 0 when we call
// reverse(), it will throw (per spec).
return animation.ready.then(waitForAnimationFrames(1)).then(function() {
assert_greater_than_equal(animation.currentTime, 0,
'currentTime expected to be greater than 0, one frame after starting');
animation.currentTime = 50 * MS_PER_SEC;
var previousPlaybackRate = animation.playbackRate;
animation.reverse();
assert_equals(animation.playbackRate, -previousPlaybackRate,
'playbackRate should be inverted');
});
}, 'reverse() inverts playbackRate');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.currentTime = 50 * MS_PER_SEC;
animation.pause();
return animation.ready.then(function() {
animation.reverse();
return animation.ready;
}).then(function() {
assert_equals(animation.playState, 'running',
'Animation.playState should be "running" after reverse()');
});
}, 'reverse() starts to play when pausing animation');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = 50 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 50 * MS_PER_SEC,
'reverse() should not change the currentTime ' +
'if the currentTime is in the middle of animation duration');
}, 'reverse() maintains the same currentTime');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = 200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 100 * MS_PER_SEC,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime > effect end');
}, 'reverse() when playbackRate > 0 and currentTime > effect end');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 100 * MS_PER_SEC,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime < 0');
}, 'reverse() when playbackRate > 0 and currentTime < 0');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = -1;
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0');
}, 'reverse() when playbackRate < 0 and currentTime < 0');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = -1;
animation.currentTime = 200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime > effect end');
}, 'reverse() when playbackRate < 0 and currentTime > effect end');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.currentTime = -200 * MS_PER_SEC;
assert_throws('InvalidStateError',
function () { animation.reverse(); },
'reverse() should throw InvalidStateError ' +
'if the playbackRate > 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'reverse() when playbackRate > 0 and currentTime < 0 ' +
'and the target effect end is positive infinity');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.playbackRate = -1;
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'reverse() when playbackRate < 0 and currentTime < 0 ' +
'and the target effect end is positive infinity');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = 0;
animation.currentTime = 50 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.playbackRate, 0,
'reverse() should preserve playbackRate if the playbackRate == 0');
assert_equals(animation.currentTime, 50 * MS_PER_SEC,
'reverse() should not affect the currentTime if the playbackRate == 0');
t.done();
}, 'reverse() when playbackRate == 0');
</script>
</body>

View file

@ -37,17 +37,6 @@ function assert_frame_lists_equal(a, b) {
var gEmptyKeyframeListTests = [
[],
[{}],
[{ easing: "ease-in" }],
[{ unknown: "unknown" }, { unknown: "unknown" }],
[{ color: "invalid" }, { color: "invalid" }],
{ easing: "ease-in" },
{ unknown: "unknown" },
{ unknown: [] },
{ unknown: ["unknown"] },
{ unknown: ["unknown", "unknown"] },
{ animationName: ["none", "abc"] },
{ color: [] },
null,
undefined,
];
@ -79,7 +68,7 @@ test(function(t) {
"resulting easing for '" + easing + "'");
});
}, "easing values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in PropertyIndexedKeyframes");
"KeyframeEffectReadOnly constructor in a property-indexed keyframe");
test(function(t) {
gEasingValueTests.forEach(function(subtest) {
@ -93,7 +82,7 @@ test(function(t) {
"resulting easing for '" + easing + "'");
});
}, "easing values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in Keyframe");
"KeyframeEffectReadOnly constructor in regular keyframes");
test(function(t) {
gEasingValueTests.forEach(function(subtest) {
@ -135,7 +124,7 @@ test(function(t) {
});
});
}, "composite values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in PropertyIndexedKeyframes");
"KeyframeEffectReadOnly constructor in property-indexed keyframes");
test(function(t) {
var getFrames = function(composite) {
@ -155,7 +144,7 @@ test(function(t) {
});
});
}, "composite values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in Keyframe");
"KeyframeEffectReadOnly constructor in regular keyframes");
test(function(t) {
gGoodOptionsCompositeValueTests.forEach(function(composite) {
@ -176,79 +165,119 @@ test(function(t) {
"KeyframeEffectReadOnly constructor in KeyframeTimingOptions");
var gPropertyIndexedKeyframesTests = [
{ desc: "a one property two value PropertyIndexedKeyframes specification",
{ desc: "a one property two value property-indexed keyframes specification",
input: { left: ["10px", "20px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }] },
{ desc: "a one shorthand property two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "20px" }] },
{ desc: "a one shorthand property two value property-indexed keyframes"
+ " specification",
input: { margin: ["10px", "10px 20px 30px 40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "10px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "10px", marginRight: "20px", marginBottom: "30px", marginLeft: "40px" }] },
{ desc: "a two property (one shorthand and one of its longhand components) two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
margin: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
margin: "10px 20px 30px 40px" }] },
{ desc: "a two property (one shorthand and one of its longhand components)"
+ " two value property-indexed keyframes specification",
input: { marginTop: ["50px", "60px"],
margin: ["10px", "10px 20px 30px 40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "50px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "60px", marginRight: "20px", marginBottom: "30px", marginLeft: "40px" }] },
{ desc: "a two property two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
marginTop: "50px", margin: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
marginTop: "60px", margin: "10px 20px 30px 40px" }] },
{ desc: "a two property two value property-indexed keyframes specification",
input: { left: ["10px", "20px"],
top: ["30px", "40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "40px" }] },
{ desc: "a two property PropertyIndexedKeyframes specification with different numbers of values",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px", top: "30px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "20px", top: "40px" }] },
{ desc: "a two property property-indexed keyframes specification with"
+ " different numbers of values",
input: { left: ["10px", "20px", "30px"],
top: ["40px", "50px"] },
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", left: "10px", top: "40px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "20px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "30px", top: "50px" }] },
{ desc: "a PropertyIndexedKeyframes specification with an invalid value",
output: [{ offset: null, computedOffset: 0.0, easing: "linear",
left: "10px", top: "40px" },
{ offset: null, computedOffset: 0.5, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 1.0, easing: "linear",
left: "30px", top: "50px" }] },
{ desc: "a property-indexed keyframes specification with an invalid value",
input: { left: ["10px", "20px", "30px", "40px", "50px"],
top: ["15px", "25px", "invalid", "45px", "55px"] },
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px", top: "15px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px", top: "25px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px", top: "45px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px", top: "55px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification that needs to stringify its values",
output: [{ offset: null, computedOffset: 0.00, easing: "linear",
left: "10px", top: "15px" },
{ offset: null, computedOffset: 0.25, easing: "linear",
left: "20px", top: "25px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px", top: "invalid" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px", top: "45px" },
{ offset: null, computedOffset: 1.00, easing: "linear",
left: "50px", top: "55px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " that needs to stringify its values",
input: { opacity: [0, 1] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", opacity: "0" },
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }] },
{ desc: "a one property one value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
opacity: "0" },
{ offset: null, computedOffset: 1, easing: "linear",
opacity: "1" }] },
{ desc: "a one property one value property-indexed keyframes specification",
input: { left: ["10px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property one non-array value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property one non-array value property-indexed keyframes"
+ " specification",
input: { left: "10px" },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification where the first value is invalid",
output: [{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the first value is invalid",
input: { left: ["invalid", "10px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification where the second value is invalid",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "invalid" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the second value is invalid",
input: { left: ["10px", "invalid"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear" }] },
{ desc: "a two property PropertyIndexedKeyframes specification where one property is missing from the first Keyframe",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "invalid" }] },
{ desc: "a two property property-indexed keyframes specification where one"
+ " property is missing from the first keyframe",
input: [{ offset: 0, left: "10px" },
{ offset: 1, left: "20px", top: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "30px" }] },
{ desc: "a two property PropertyIndexedKeyframes specification where one property is missing from the last Keyframe",
{ offset: 1, computedOffset: 1, easing: "linear",
left: "20px", top: "30px" }] },
{ desc: "a two property property-indexed keyframes specification where one"
+ " property is missing from the last keyframe",
input: [{ offset: 0, left: "10px", top: "20px" },
{ offset: 1, left: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" , top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "30px" }] },
{ desc: "a PropertyIndexedKeyframes specification with repeated values at offset 0 with different easings",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
left: "10px" , top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
left: "30px" }] },
{ desc: "a property-indexed keyframes specification with repeated values"
+ " at offset 0 with different easings",
input: [{ 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" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease", left: "100px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease", left: "200px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "300px" },
{ offset: 1.0, computedOffset: 1.0, easing: "ease-out", left: "400px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "500px" }] },
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "100px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "200px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "300px" },
{ offset: 1.0, computedOffset: 1.0, easing: "ease-out",
left: "400px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
left: "500px" }] },
];
gPropertyIndexedKeyframesTests.forEach(function(subtest) {
@ -282,41 +311,56 @@ test(function(t) {
});
new KeyframeEffectReadOnly(target, [kf1, kf2]);
assert_array_equals(actualOrder, expectedOrder, "property access order");
}, "the KeyframeEffectReadOnly constructor reads Keyframe properties in the " +
}, "the KeyframeEffectReadOnly constructor reads keyframe properties in the " +
"expected order");
var gKeyframeSequenceTests = [
{ desc: "a one property two Keyframe sequence",
{ desc: "a one property two keyframe sequence",
input: [{ offset: 0, left: "10px" },
{ offset: 1, left: "20px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }] },
{ desc: "a two property two Keyframe sequence",
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }]
},
{ desc: "a two property two keyframe sequence",
input: [{ offset: 0, left: "10px", top: "30px" },
{ offset: 1, left: "20px", top: "40px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "40px" }] },
{ desc: "a one shorthand property two Keyframe sequence",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear",
left: "20px", top: "40px" }] },
{ desc: "a one shorthand property two keyframe sequence",
input: [{ offset: 0, margin: "10px" },
{ offset: 1, margin: "20px 30px 40px 50px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "10px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "20px", marginRight: "30px", marginBottom: "40px", marginLeft: "50px" }] },
{ desc: "a two property (a shorthand and one of its component longhands) two Keyframe sequence",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "20px 30px 40px 50px" }] },
{ desc: "a two property (a shorthand and one of its component longhands)"
+ " two keyframe sequence",
input: [{ offset: 0, margin: "10px", marginTop: "20px" },
{ offset: 1, marginTop: "70px", margin: "30px 40px 50px 60px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "20px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "70px", marginRight: "40px", marginBottom: "50px", marginLeft: "60px" }] },
{ desc: "a Keyframe sequence with duplicate values for a given interior offset",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px", marginTop: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
marginTop: "70px", margin: "30px 40px 50px 60px" }] },
{ desc: "a keyframe sequence with duplicate values for a given interior"
+ " offset",
input: [{ 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" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", left: "10px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "50px" }] },
{ desc: "a Keyframe sequence with duplicate values for offsets 0 and 1",
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear",
left: "10px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "30px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
left: "50px" }] },
{ desc: "a keyframe sequence with duplicate values for offsets 0 and 1",
input: [{ offset: 0, left: "10px" },
{ offset: 0, left: "20px" },
{ offset: 0, left: "30px" },
@ -324,50 +368,72 @@ var gKeyframeSequenceTests = [
{ offset: 1, left: "50px" },
{ offset: 1, left: "60px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 0, computedOffset: 0, easing: "linear", left: "20px" },
{ offset: 0, computedOffset: 0, easing: "linear", left: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "60px" }] },
{ desc: "a two property four Keyframe sequence",
{ offset: 1, computedOffset: 1, easing: "linear", left: "50px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "60px" }]
},
{ desc: "a two property four keyframe sequence",
input: [{ offset: 0, left: "10px" },
{ offset: 0, top: "20px" },
{ offset: 1, top: "30px" },
{ offset: 1, left: "40px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px", top: "30px" }] },
{ desc: "a one property Keyframe sequence with some omitted offsets",
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 0, computedOffset: 0, easing: "linear", top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px" }]
},
{ desc: "a one property keyframe sequence with some omitted offsets",
input: [{ offset: 0.00, left: "10px" },
{ offset: 0.25, left: "20px" },
{ left: "30px" },
{ left: "40px" },
{ offset: 1.00, left: "50px" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px" }] },
{ desc: "a two property Keyframe sequence with some omitted offsets",
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear",
left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear",
left: "50px" }] },
{ desc: "a two property keyframe sequence with some omitted offsets",
input: [{ 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" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px", top: "20px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "30px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "40px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "50px", top: "60px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "70px", top: "80px" }] },
{ desc: "a one property Keyframe sequence with all omitted offsets",
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear",
left: "10px", top: "20px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "40px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "50px", top: "60px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear",
left: "70px", top: "80px" }] },
{ desc: "a one property keyframe sequence with all omitted offsets",
input: [{ left: "10px" },
{ left: "20px" },
{ left: "30px" },
{ left: "40px" },
{ left: "50px" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px" }] },
{ desc: "a Keyframe sequence with different easing values, but the same easing value for a given offset",
output: [{ offset: null, computedOffset: 0.00, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 0.25, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px" },
{ offset: null, computedOffset: 1.00, easing: "linear",
left: "50px" }] },
{ desc: "a keyframe sequence with different easing values, but the same"
+ " easing value for a given offset",
input: [{ offset: 0.0, easing: "ease", left: "10px"},
{ offset: 0.0, easing: "ease", top: "20px"},
{ offset: 0.5, easing: "linear", left: "30px" },
@ -375,62 +441,79 @@ var gKeyframeSequenceTests = [
{ offset: 1.0, easing: "step-end", left: "50px" },
{ offset: 1.0, easing: "step-end", top: "60px" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "10px", top: "20px" },
left: "10px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease",
top: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "30px", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
left: "50px", top: "60px" }] },
{ desc: "a Keyframe sequence with different composite values, but the same composite value for a given offset",
left: "30px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
left: "50px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
top: "60px" }] },
{ desc: "a keyframe sequence with different composite values, but the"
+ " same composite value for a given offset",
input: [{ offset: 0.0, composite: "replace", left: "10px" },
{ offset: 0.0, composite: "replace", top: "20px" },
{ offset: 0.5, composite: "add", left: "30px" },
{ offset: 0.5, composite: "add", top: "40px" },
{ offset: 1.0, composite: "replace", left: "50px" },
{ offset: 1.0, composite: "replace", top: "60px" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", composite: "replace", left: "10px", top: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", composite: "add", left: "30px", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", composite: "replace", left: "50px", top: "60px" }] },
{ desc: "a one property two Keyframe sequence that needs to stringify its values",
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear",
composite: "replace", left: "10px" },
{ offset: 0.0, computedOffset: 0.0, easing: "linear",
composite: "replace", top: "20px" },
{ offset: 0.5, computedOffset: 0.0, easing: "linear",
composite: "add", left: "30px" },
{ offset: 0.5, computedOffset: 0.0, easing: "linear",
composite: "add", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
composite: "replace", left: "50px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
composite: "replace", top: "60px" }] },
{ desc: "a one property two keyframe sequence that needs to stringify"
+ " its values",
input: [{ offset: 0, opacity: 0 },
{ offset: 1, opacity: 1 }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", opacity: "0" },
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }] },
{ desc: "a Keyframe sequence where shorthand precedes longhand",
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }]
},
{ desc: "a keyframe sequence where shorthand precedes longhand",
input: [{ offset: 0, margin: "10px", marginRight: "20px" },
{ offset: 1, margin: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginBottom: "10px", marginLeft: "10px", marginRight: "20px", marginTop: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginBottom: "30px", marginLeft: "30px", marginRight: "30px", marginTop: "30px" }] },
{ desc: "a Keyframe sequence where longhand precedes shorthand",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px", marginRight: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "30px" }] },
{ desc: "a keyframe sequence where longhand precedes shorthand",
input: [{ offset: 0, marginRight: "20px", margin: "10px" },
{ offset: 1, margin: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginBottom: "10px", marginLeft: "10px", marginRight: "20px", marginTop: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginBottom: "30px", marginLeft: "30px", marginRight: "30px", marginTop: "30px" }] },
{ desc: "a Keyframe sequence where lesser shorthand precedes greater shorthand",
input: [{ offset: 0, borderLeft: "1px solid rgb(1, 2, 3)", border: "2px dotted rgb(4, 5, 6)" },
output: [{ offset: 0, computedOffset: 0, easing: "linear",
marginRight: "20px", margin: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "30px" }] },
{ desc: "a keyframe sequence where lesser shorthand precedes greater"
+ " shorthand",
input: [{ 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)" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear",
borderBottomColor: "rgb(4, 5, 6)", borderBottomWidth: "2px",
borderLeftColor: "rgb(1, 2, 3)", borderLeftWidth: "1px",
borderRightColor: "rgb(4, 5, 6)", borderRightWidth: "2px",
borderTopColor: "rgb(4, 5, 6)", borderTopWidth: "2px" },
borderLeft: "1px solid rgb(1, 2, 3)",
border: "2px dotted rgb(4, 5, 6)" },
{ offset: 1, computedOffset: 1, easing: "linear",
borderBottomColor: "rgb(7, 8, 9)", borderBottomWidth: "3px",
borderLeftColor: "rgb(7, 8, 9)", borderLeftWidth: "3px",
borderRightColor: "rgb(7, 8, 9)", borderRightWidth: "3px",
borderTopColor: "rgb(7, 8, 9)", borderTopWidth: "3px" }] },
{ desc: "a Keyframe sequence where greater shorthand precedes lesser shorthand",
input: [{ offset: 0, border: "2px dotted rgb(4, 5, 6)", borderLeft: "1px solid rgb(1, 2, 3)" },
border: "3px dashed rgb(7, 8, 9)" }] },
{ desc: "a keyframe sequence where greater shorthand precedes lesser"
+ " shorthand",
input: [{ 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)" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear",
borderBottomColor: "rgb(4, 5, 6)", borderBottomWidth: "2px",
borderLeftColor: "rgb(1, 2, 3)", borderLeftWidth: "1px",
borderRightColor: "rgb(4, 5, 6)", borderRightWidth: "2px",
borderTopColor: "rgb(4, 5, 6)", borderTopWidth: "2px" },
border: "2px dotted rgb(4, 5, 6)",
borderLeft: "1px solid rgb(1, 2, 3)" },
{ offset: 1, computedOffset: 1, easing: "linear",
borderBottomColor: "rgb(7, 8, 9)", borderBottomWidth: "3px",
borderLeftColor: "rgb(7, 8, 9)", borderLeftWidth: "3px",
borderRightColor: "rgb(7, 8, 9)", borderRightWidth: "3px",
borderTopColor: "rgb(7, 8, 9)", borderTopWidth: "3px" }] },
border: "3px dashed rgb(7, 8, 9)" }] },
];
gKeyframeSequenceTests.forEach(function(subtest) {
@ -467,7 +550,7 @@ gInvalidEasingInKeyframeSequenceTests.forEach(function(subtest) {
assert_throws(new TypeError, function() {
new KeyframeEffectReadOnly(target, subtest.input);
});
}, "Invalid easing [" + subtest.desc + "] in KeyframeSequence " +
}, "Invalid easing [" + subtest.desc + "] in keyframe sequence " +
"should be thrown");
});

View file

@ -6,6 +6,7 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<script src="../resources/effect-easing-tests.js"></script>
<body>
<div id="log"></div>
<div id="target"></div>
@ -22,54 +23,6 @@ function assert_style_left_at(animation, time, easingFunction) {
easingFunction(portion) * 100 + ' at ' + time + 'ms');
}
var gEffectEasingTests = [
{
desc: 'steps(start) function',
easing: 'steps(2, start)',
easingFunction: stepStart(2)
},
{
desc: 'steps(end) function',
easing: 'steps(2, end)',
easingFunction: stepEnd(2)
},
{
desc: 'linear function',
easing: 'linear', // cubic-bezier(0, 0, 1.0, 1.0)
easingFunction: cubicBezier(0, 0, 1.0, 1.0)
},
{
desc: 'ease function',
easing: 'ease', // cubic-bezier(0.25, 0.1, 0.25, 1.0)
easingFunction: cubicBezier(0.25, 0.1, 0.25, 1.0)
},
{
desc: 'ease-in function',
easing: 'ease-in', // cubic-bezier(0.42, 0, 1.0, 1.0)
easingFunction: cubicBezier(0.42, 0, 1.0, 1.0)
},
{
desc: 'ease-in-out function',
easing: 'ease-in-out', // cubic-bezier(0.42, 0, 0.58, 1.0)
easingFunction: cubicBezier(0.42, 0, 0.58, 1.0)
},
{
desc: 'ease-out function',
easing: 'ease-out', // cubic-bezier(0, 0, 0.58, 1.0)
easingFunction: cubicBezier(0, 0, 0.58, 1.0)
},
{
desc: 'easing function which produces values greater than 1',
easing: 'cubic-bezier(0, 1.5, 1, 1.5)',
easingFunction: cubicBezier(0, 1.5, 1, 1.5)
},
{
desc: 'easing function which produces negative values',
easing: 'cubic-bezier(0, -0.5 ,1, -0.5)',
easingFunction: cubicBezier(0, -0.5, 1, -0.5)
},
];
gEffectEasingTests.forEach(function(options) {
test(function(t) {
var target = createDiv(t);
@ -694,7 +647,7 @@ var gStepTimingFunctionTests = [
{ currentTime: 2500, progress: 0.5 },
]
}
]
];
gStepTimingFunctionTests.forEach(function(options) {
test(function(t) {

View file

@ -70,6 +70,46 @@ test(function(t) {
}, 'Overlapping keyframes between 0 and 1 use the appropriate value on each'
+ ' side of the overlap point');
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ visibility: ['hidden','visible'] },
{ duration: 100 * MS_PER_SEC, fill: 'both' });
anim.currentTime = 0;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress = 0.');
anim.currentTime = 10 * MS_PER_SEC + 1;
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress > 0 due to linear easing.');
anim.finish();
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress = 1.');
}, "Test visibility clamping behavior.");
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ visibility: ['hidden', 'visible'] },
{ duration: 100 * MS_PER_SEC, fill: 'both',
easing: 'cubic-bezier(0.25, -0.6, 0, 0.5)' });
anim.currentTime = 0;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress = 0.');
// Timing function is below zero. So we expected visibility is hidden.
anim.currentTime = 10 * MS_PER_SEC + 1;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress < 0 due to cubic-bezier easing.');
anim.currentTime = 60 * MS_PER_SEC;
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress > 0 due to cubic-bezier easing.');
}, "Test visibility clamping behavior with an easing that has a negative component");
done();
</script>
</body>

View file

@ -0,0 +1,42 @@
var gEffectEasingTests = [
{
desc: 'steps(start) function',
easing: 'steps(2, start)',
easingFunction: stepStart(2)
},
{
desc: 'steps(end) function',
easing: 'steps(2, end)',
easingFunction: stepEnd(2)
},
{
desc: 'linear function',
easing: 'linear', // cubic-bezier(0, 0, 1.0, 1.0)
easingFunction: cubicBezier(0, 0, 1.0, 1.0)
},
{
desc: 'ease function',
easing: 'ease', // cubic-bezier(0.25, 0.1, 0.25, 1.0)
easingFunction: cubicBezier(0.25, 0.1, 0.25, 1.0)
},
{
desc: 'ease-in function',
easing: 'ease-in', // cubic-bezier(0.42, 0, 1.0, 1.0)
easingFunction: cubicBezier(0.42, 0, 1.0, 1.0)
},
{
desc: 'ease-in-out function',
easing: 'ease-in-out', // cubic-bezier(0.42, 0, 0.58, 1.0)
easingFunction: cubicBezier(0.42, 0, 0.58, 1.0)
},
{
desc: 'ease-out function',
easing: 'ease-out', // cubic-bezier(0, 0, 0.58, 1.0)
easingFunction: cubicBezier(0, 0, 0.58, 1.0)
},
{
desc: 'easing function which produces values greater than 1',
easing: 'cubic-bezier(0, 1.5, 1, 1.5)',
easingFunction: cubicBezier(0, 1.5, 1, 1.5)
}
];