diff options
Diffstat (limited to 'dom/animation/test/css-transitions/file_animation-starttime.html')
-rw-r--r-- | dom/animation/test/css-transitions/file_animation-starttime.html | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/dom/animation/test/css-transitions/file_animation-starttime.html b/dom/animation/test/css-transitions/file_animation-starttime.html new file mode 100644 index 000000000..a156ba0a0 --- /dev/null +++ b/dom/animation/test/css-transitions/file_animation-starttime.html @@ -0,0 +1,284 @@ +<!doctype html> +<html> + <head> + <meta charset=utf-8> + <title>Tests for the effect of setting a CSS transition's + Animation.startTime</title> + <style> + +.animated-div { + margin-left: 100px; + transition: margin-left 1000s linear 1000s; +} + + </style> + <script src="../testcommon.js"></script> + </head> + <body> + <script type="text/javascript"> + +'use strict'; + +// TODO: Once the computedTiming property is implemented, add checks to the +// checker helpers to ensure that computedTiming's properties are updated as +// expected. +// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055 + + +const ANIM_DELAY_MS = 1000000; // 1000s +const ANIM_DUR_MS = 1000000; // 1000s + +/** + * These helpers get the value that the startTime needs to be set to, to put an + * animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the + * middle of various phases or points through the active duration. + */ +function startTimeForBeforePhase(timeline) { + return timeline.currentTime - ANIM_DELAY_MS / 2; +} +function startTimeForActivePhase(timeline) { + return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2; +} +function startTimeForAfterPhase(timeline) { + return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2; +} +function startTimeForStartOfActiveInterval(timeline) { + return timeline.currentTime - ANIM_DELAY_MS; +} +function startTimeForFiftyPercentThroughActiveInterval(timeline) { + return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5; +} +function startTimeForEndOfActiveInterval(timeline) { + return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS; +} + + +// Expected computed 'margin-left' values at points during the active interval: +// When we assert_between_inclusive using these values we could in theory cause +// intermittent failure due to very long delays between paints, but since the +// active duration is 1000s long, a delay would need to be around 100s to cause +// that. If that's happening then there are likely other issues that should be +// fixed, so a failure to make us look into that seems like a good thing. +const INITIAL_POSITION = 100; +const TEN_PCT_POSITION = 110; +const FIFTY_PCT_POSITION = 150; +const END_POSITION = 200; + +// The terms used for the naming of the following helper functions refer to +// terms used in the Web Animations specification for specific phases of an +// animation. The terms can be found here: +// +// https://w3c.github.io/web-animations/#animation-effect-phases-and-states +// +// Note the distinction between the "animation start time" which occurs before +// the start delay and the start of the active interval which occurs after it. + +// Called when the ready Promise's callbacks should happen +function checkStateOnReadyPromiseResolved(animation) +{ + assert_less_than_equal(animation.startTime, animation.timeline.currentTime, + 'Animation.startTime should be less than the timeline\'s ' + + 'currentTime on the first paint tick after animation creation'); + + assert_equals(animation.playState, 'running', + 'Animation.playState should be "running" on the first paint ' + + 'tick after animation creation'); + + var div = animation.effect.target; + var marginLeft = parseFloat(getComputedStyle(div).marginLeft); + assert_equals(marginLeft, INITIAL_POSITION, + 'the computed value of margin-left should be unaffected ' + + 'by an animation with a delay on ready Promise resolve'); +} + +// Called when startTime is set to the time the active interval starts. +function checkStateAtActiveIntervalStartTime(animation) +{ + // We don't test animation.startTime since our caller just set it. + + assert_equals(animation.playState, 'running', + 'Animation.playState should be "running" at the start of ' + + 'the active interval'); + + var div = animation.effect.target; + var marginLeft = parseFloat(getComputedStyle(div).marginLeft); + assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION, + 'the computed value of margin-left should be close to the value at the ' + + 'beginning of the animation'); +} + +function checkStateAtFiftyPctOfActiveInterval(animation) +{ + // We don't test animation.startTime since our caller just set it. + + var div = animation.effect.target; + var marginLeft = parseFloat(getComputedStyle(div).marginLeft); + assert_equals(marginLeft, FIFTY_PCT_POSITION, + 'the computed value of margin-left should be half way through the ' + + 'animation at the midpoint of the active interval'); +} + +// Called when startTime is set to the time the active interval ends. +function checkStateAtActiveIntervalEndTime(animation) +{ + // We don't test animation.startTime since our caller just set it. + + assert_equals(animation.playState, 'finished', + 'Animation.playState should be "finished" at the end of ' + + 'the active interval'); + + var div = animation.effect.target; + var marginLeft = parseFloat(getComputedStyle(div).marginLeft); + assert_equals(marginLeft, END_POSITION, + 'the computed value of margin-left should be the final transitioned-to ' + + 'value at the end of the active duration'); +} + +test(function(t) +{ + var div = addDiv(t, {'class': 'animated-div'}); + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + assert_equals(animation.startTime, null, 'startTime is unresolved'); +}, 'startTime of a newly created transition is unresolved'); + + +test(function(t) +{ + var div = addDiv(t, {'class': 'animated-div'}); + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + var currentTime = animation.timeline.currentTime; + animation.startTime = currentTime; + assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error + 'Check setting of startTime actually works'); +}, 'Sanity test to check round-tripping assigning to new animation\'s ' + + 'startTime'); + + +async_test(function(t) { + var div = addDiv(t, {'class': 'animated-div'}); + var eventWatcher = new EventWatcher(t, div, 'transitionend'); + + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + + animation.ready.then(t.step_func(function() { + checkStateOnReadyPromiseResolved(animation); + + animation.startTime = startTimeForStartOfActiveInterval(animation.timeline); + checkStateAtActiveIntervalStartTime(animation); + + animation.startTime = + startTimeForFiftyPercentThroughActiveInterval(animation.timeline); + checkStateAtFiftyPctOfActiveInterval(animation); + + animation.startTime = startTimeForEndOfActiveInterval(animation.timeline); + return eventWatcher.wait_for('transitionend'); + })).then(t.step_func(function() { + checkStateAtActiveIntervalEndTime(animation); + })).catch(t.step_func(function(reason) { + assert_unreached(reason); + })).then(function() { + t.done(); + }); +}, 'Skipping forward through animation'); + + +test(function(t) { + var div = addDiv(t, {'class': 'animated-div'}); + var eventWatcher = new EventWatcher(t, div, 'transitionend'); + + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + + // Unlike in the case of CSS animations, we cannot skip to the end and skip + // backwards since when we reach the end the transition effect is removed and + // changes to the Animation object no longer affect the element. For + // this reason we only skip forwards as far as the 90% through point. + + animation.startTime = + startTimeForFiftyPercentThroughActiveInterval(animation.timeline); + checkStateAtFiftyPctOfActiveInterval(animation); + + animation.startTime = startTimeForStartOfActiveInterval(animation.timeline); + + // Despite going backwards from being in the active interval to being before + // it, we now expect an 'animationend' event because the animation should go + // from being active to inactive. + // + // Calling checkStateAtActiveIntervalStartTime will check computed style, + // causing computed style to be updated and the 'transitionend' event to + // be dispatched synchronously. We need to call waitForEvent first + // otherwise eventWatcher will assert that the event was unexpected. + eventWatcher.wait_for('transitionend').then(function() { + t.done(); + }); + checkStateAtActiveIntervalStartTime(animation); +}, 'Skipping backwards through transition'); + + +async_test(function(t) { + var div = addDiv(t, {'class': 'animated-div'}); + + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + + var storedCurrentTime; + + animation.ready.then(t.step_func(function() { + storedCurrentTime = animation.currentTime; + animation.startTime = null; + return animation.ready; + })).catch(t.step_func(function(reason) { + assert_unreached(reason); + })).then(t.step_func(function() { + assert_equals(animation.currentTime, storedCurrentTime, + 'Test that hold time is correct'); + t.done(); + })); +}, 'Setting startTime to null'); + + +async_test(function(t) { + var div = addDiv(t, {'class': 'animated-div'}); + + flushComputedStyle(div); + div.style.marginLeft = '200px'; // initiate transition + + var animation = div.getAnimations()[0]; + + animation.ready.then(t.step_func(function() { + var savedStartTime = animation.startTime; + + assert_not_equals(animation.startTime, null, + 'Animation.startTime not null on ready Promise resolve'); + + animation.pause(); + return animation.ready; + })).then(t.step_func(function() { + assert_equals(animation.startTime, null, + 'Animation.startTime is null after paused'); + assert_equals(animation.playState, 'paused', + 'Animation.playState is "paused" after pause() call'); + })).catch(t.step_func(function(reason) { + assert_unreached(reason); + })).then(function() { + t.done(); + }); +}, 'Animation.startTime after paused'); + +done(); + </script> + </body> +</html> |