mirror of
https://github.com/zebrajr/ladybird.git
synced 2026-01-15 12:15:15 +00:00
Tests: Import some more animation tests
This commit is contained in:
committed by
Alexander Kalenik
parent
3a7fcde341
commit
16d1498bb0
@@ -0,0 +1,15 @@
|
||||
Harness status: OK
|
||||
|
||||
Found 9 tests
|
||||
|
||||
2 Pass
|
||||
7 Fail
|
||||
Fail play() overrides animation-play-state
|
||||
Pass play() does NOT override the animation-play-state if there was an error
|
||||
Fail pause() overrides animation-play-state
|
||||
Fail reverse() overrides animation-play-state when it starts playing the animation
|
||||
Fail reverse() does NOT override animation-play-state if the animation is already running
|
||||
Fail Setting the startTime to null overrides animation-play-state if the animation is already running
|
||||
Fail Setting the startTime to non-null overrides animation-play-state if the animation is paused
|
||||
Pass Setting the startTime to non-null does NOT override the animation-play-state if the animation is already running
|
||||
Fail Setting the current time completes a pending pause
|
||||
@@ -0,0 +1,18 @@
|
||||
Harness status: OK
|
||||
|
||||
Found 12 tests
|
||||
|
||||
11 Pass
|
||||
1 Fail
|
||||
Pass Playing a running animation leaves the current time unchanged
|
||||
Pass Playing a finished animation seeks back to the start
|
||||
Pass Playing a finished and reversed animation seeks to end
|
||||
Pass Playing a pause-pending but previously finished animation seeks back to to the start
|
||||
Pass Playing a finished animation clears the start time
|
||||
Pass The ready promise should be replaced if the animation is not already pending
|
||||
Pass A pending ready promise should be resolved and not replaced when the animation enters the running state
|
||||
Pass Resuming an animation from paused calculates start time from hold time
|
||||
Pass If a pause operation is interrupted, the ready promise is reused
|
||||
Fail A pending playback rate is used when determining auto-rewind behavior
|
||||
Pass Playing a canceled animation sets the start time
|
||||
Pass Playing a canceled animation backwards sets the start time
|
||||
@@ -0,0 +1,14 @@
|
||||
Harness status: OK
|
||||
|
||||
Found 8 tests
|
||||
|
||||
4 Pass
|
||||
4 Fail
|
||||
Pass Fires cancel event before requestAnimationFrame
|
||||
Pass Fires finish event before requestAnimationFrame
|
||||
Fail Sorts finish events by composite order
|
||||
Fail Sorts cancel events by composite order
|
||||
Pass Queues a cancel event in transitionstart event callback
|
||||
Fail Sorts events for the same transition
|
||||
Pass Playback events with the same timeline retain the order in which they arequeued
|
||||
Fail All timelines are updated before running microtasks
|
||||
@@ -0,0 +1,259 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Pausing a CSSAnimation</title>
|
||||
<link rel="help"
|
||||
href="https://drafts.csswg.org/css-animations-2/#animation-play-state">
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="support/testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
0% { margin-left: 0px }
|
||||
100% { margin-left: 10000px }
|
||||
}
|
||||
</style>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.play();
|
||||
|
||||
await animation.ready;
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Play state is running after calling play()'
|
||||
);
|
||||
|
||||
// Flip the animation-play-state back and forth to check it has no effect
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Should still be running even after flipping the animation-play-state'
|
||||
);
|
||||
}, 'play() overrides animation-play-state');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s infinite paused';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.playbackRate = -1;
|
||||
animation.currentTime = -1;
|
||||
|
||||
assert_throws_dom('InvalidStateError', () => {
|
||||
animation.play();
|
||||
}, 'Trying to play a reversed infinite animation should throw');
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Animation should still be paused'
|
||||
);
|
||||
|
||||
animation.playbackRate = 1;
|
||||
div.style.animationPlayState = 'running';
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Changing the animation-play-state should play the animation'
|
||||
);
|
||||
}, 'play() does NOT override the animation-play-state if there was an error');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.pause();
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
await animation.ready;
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_equals(animation.playState, 'paused', 'playState is paused ');
|
||||
|
||||
// Flip the animation-play-state back and forth to check it has no effect
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Should still be paused even after flipping the animation-play-state'
|
||||
);
|
||||
}, 'pause() overrides animation-play-state');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s paused';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.reverse();
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Play state is running after calling reverse()'
|
||||
);
|
||||
|
||||
// Flip the animation-play-state back and forth to check it has no effect
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Should still be running even after flipping the animation-play-state'
|
||||
);
|
||||
}, 'reverse() overrides animation-play-state when it starts playing the'
|
||||
+ ' animation');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.reverse();
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Play state is running after calling reverse()'
|
||||
);
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Should be paused after changing the animation-play-state'
|
||||
);
|
||||
}, 'reverse() does NOT override animation-play-state if the animation is'
|
||||
+ ' already running');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.startTime = null;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Play state is paused after setting the start time to null'
|
||||
);
|
||||
|
||||
// Flip the animation-play-state back and forth to check it has no effect
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Should still be paused even after flipping the animation-play-state'
|
||||
);
|
||||
}, 'Setting the startTime to null overrides animation-play-state if the'
|
||||
+ ' animation is already running');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s paused';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.startTime = document.timeline.currentTime;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Play state is running after setting the start time to non-null'
|
||||
);
|
||||
|
||||
// Flip the animation-play-state back and forth to check it has no effect
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'running',
|
||||
'Should still be running even after flipping the animation-play-state'
|
||||
);
|
||||
}, 'Setting the startTime to non-null overrides animation-play-state if the'
|
||||
+ ' animation is paused');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.startTime = document.timeline.currentTime;
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(
|
||||
animation.playState,
|
||||
'paused',
|
||||
'Should be paused after changing the animation-play-state'
|
||||
);
|
||||
}, 'Setting the startTime to non-null does NOT override the'
|
||||
+ ' animation-play-state if the animation is already running');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||
const animation = div.getAnimations()[0];
|
||||
let readyPromiseRun = false;
|
||||
|
||||
await animation.ready;
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_true(animation.pending && animation.playState === 'paused',
|
||||
'Animation is pause-pending');
|
||||
|
||||
// Set current time
|
||||
animation.currentTime = 5 * MS_PER_SEC;
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'Animation is paused immediately after setting currentTime');
|
||||
assert_equals(animation.startTime, null,
|
||||
'Animation startTime is unresolved immediately after ' +
|
||||
'setting currentTime');
|
||||
assert_equals(animation.currentTime, 5 * MS_PER_SEC,
|
||||
'Animation currentTime does not change when forcing a ' +
|
||||
'pause operation to complete');
|
||||
|
||||
// The ready promise should now be resolved. If it's not then test will
|
||||
// probably time out before anything else happens that causes it to resolve.
|
||||
await animation.ready;
|
||||
}, 'Setting the current time completes a pending pause');
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Playing an animation</title>
|
||||
<link rel="help"
|
||||
href="https://drafts.csswg.org/web-animations/#playing-an-animation-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(t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.currentTime = 1 * MS_PER_SEC;
|
||||
assert_time_equals_literal(animation.currentTime, 1 * MS_PER_SEC);
|
||||
animation.play();
|
||||
assert_time_equals_literal(animation.currentTime, 1 * MS_PER_SEC);
|
||||
}, 'Playing a running animation leaves the current time unchanged');
|
||||
|
||||
test(t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.finish();
|
||||
assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC);
|
||||
animation.play();
|
||||
assert_time_equals_literal(animation.currentTime, 0);
|
||||
}, 'Playing a finished animation seeks back to the start');
|
||||
|
||||
test(t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.playbackRate = -1;
|
||||
animation.currentTime = 0;
|
||||
assert_time_equals_literal(animation.currentTime, 0);
|
||||
animation.play();
|
||||
assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC);
|
||||
}, 'Playing a finished and reversed animation seeks to end');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.finish();
|
||||
|
||||
// Initiate a pause then abort it
|
||||
animation.pause();
|
||||
animation.play();
|
||||
|
||||
// Wait to return to running state
|
||||
await animation.ready;
|
||||
|
||||
assert_true(animation.currentTime < 100 * 1000,
|
||||
'After aborting a pause when finished, the current time should'
|
||||
+ ' jump back to the start of the animation');
|
||||
}, 'Playing a pause-pending but previously finished animation seeks back to'
|
||||
+ ' to the start');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.finish();
|
||||
await animation.ready;
|
||||
|
||||
animation.play();
|
||||
assert_equals(animation.startTime, null, 'start time is unresolved');
|
||||
}, 'Playing a finished animation clears the start time');
|
||||
|
||||
test(t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.cancel();
|
||||
const promise = animation.ready;
|
||||
animation.play();
|
||||
assert_not_equals(animation.ready, promise);
|
||||
}, 'The ready promise should be replaced if the animation is not already'
|
||||
+ ' pending');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
const promise = animation.ready;
|
||||
const promiseResult = await promise;
|
||||
assert_equals(promiseResult, animation);
|
||||
assert_equals(animation.ready, promise);
|
||||
}, 'A pending ready promise should be resolved and not replaced when the'
|
||||
+ ' animation enters the running state');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.currentTime = 50 * MS_PER_SEC;
|
||||
await animation.ready;
|
||||
|
||||
animation.pause();
|
||||
await animation.ready;
|
||||
|
||||
const holdTime = animation.currentTime;
|
||||
|
||||
animation.play();
|
||||
await animation.ready;
|
||||
|
||||
assert_less_than_equal(
|
||||
animation.startTime,
|
||||
animation.timeline.currentTime - holdTime + TIME_PRECISION
|
||||
);
|
||||
}, 'Resuming an animation from paused calculates start time from hold time');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
await animation.ready;
|
||||
|
||||
// Go to pause-pending state
|
||||
animation.pause();
|
||||
assert_true(animation.pending, 'Animation is pending');
|
||||
const pauseReadyPromise = animation.ready;
|
||||
|
||||
// Now play again immediately (abort the pause)
|
||||
animation.play();
|
||||
assert_true(animation.pending, 'Animation is still pending');
|
||||
assert_equals(animation.ready, pauseReadyPromise,
|
||||
'The pause Promise is re-used when playing while waiting'
|
||||
+ ' to pause');
|
||||
|
||||
// Sanity check: Animation proceeds to running state
|
||||
await animation.ready;
|
||||
assert_true(!animation.pending && animation.playState === 'running',
|
||||
'Animation is running after aborting a pause');
|
||||
}, 'If a pause operation is interrupted, the ready promise is reused');
|
||||
|
||||
promise_test(async t => {
|
||||
// Seek animation beyond target end
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.currentTime = -100 * MS_PER_SEC;
|
||||
await animation.ready;
|
||||
|
||||
// Set pending playback rate to the opposite direction
|
||||
animation.updatePlaybackRate(-1);
|
||||
assert_true(animation.pending);
|
||||
assert_equals(animation.playbackRate, 1);
|
||||
|
||||
// When we play, we should seek to the target end, NOT to zero (which
|
||||
// is where we would seek to if we used the playbackRate of 1.
|
||||
animation.play();
|
||||
assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC);
|
||||
}, 'A pending playback rate is used when determining auto-rewind behavior');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.cancel();
|
||||
assert_equals(animation.startTime, null,
|
||||
'Start time should be unresolved');
|
||||
|
||||
const playTime = animation.timeline.currentTime;
|
||||
animation.play();
|
||||
assert_true(animation.pending, 'Animation should be play-pending');
|
||||
|
||||
await animation.ready;
|
||||
|
||||
assert_false(animation.pending, 'animation should no longer be pending');
|
||||
assert_time_greater_than_equal(animation.startTime, playTime,
|
||||
'The start time of the playing animation should be set');
|
||||
}, 'Playing a canceled animation sets the start time');
|
||||
|
||||
promise_test(async t => {
|
||||
const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
|
||||
animation.playbackRate = -1;
|
||||
animation.cancel();
|
||||
assert_equals(animation.startTime, null,
|
||||
'Start time should be unresolved');
|
||||
|
||||
const playTime = animation.timeline.currentTime;
|
||||
animation.play();
|
||||
assert_true(animation.pending, 'Animation should be play-pending');
|
||||
|
||||
await animation.ready;
|
||||
|
||||
assert_false(animation.pending, 'Animation should no longer be pending');
|
||||
assert_time_greater_than_equal(animation.startTime, playTime + 100 * MS_PER_SEC,
|
||||
'The start time of the playing animation should be set');
|
||||
}, 'Playing a canceled animation backwards sets the start time');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,257 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Update animations and send events</title>
|
||||
<meta name="timeout" content="long">
|
||||
<link rel="help" href="https://drafts.csswg.org/web-animations/#update-animations-and-send-events">
|
||||
<script src="../../../resources/testharness.js"></script>
|
||||
<script src="../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
const animation = div.animate(null, 100 * MS_PER_SEC);
|
||||
|
||||
// The ready promise should be resolved as part of micro-task checkpoint
|
||||
// after updating the current time of all timeslines in the procedure to
|
||||
// "update animations and send events".
|
||||
await animation.ready;
|
||||
|
||||
let rAFReceived = false;
|
||||
requestAnimationFrame(() => rAFReceived = true);
|
||||
|
||||
const eventWatcher = new EventWatcher(t, animation, 'cancel');
|
||||
animation.cancel();
|
||||
|
||||
await eventWatcher.wait_for('cancel');
|
||||
|
||||
assert_false(rAFReceived,
|
||||
'cancel event should be fired before requestAnimationFrame');
|
||||
}, 'Fires cancel event before requestAnimationFrame');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
const animation = div.animate(null, 100 * MS_PER_SEC);
|
||||
|
||||
// Like the above test, the ready promise should be resolved micro-task
|
||||
// checkpoint after updating the current time of all timeslines in the
|
||||
// procedure to "update animations and send events".
|
||||
await animation.ready;
|
||||
|
||||
let rAFReceived = false;
|
||||
requestAnimationFrame(() => rAFReceived = true);
|
||||
|
||||
const eventWatcher = new EventWatcher(t, animation, 'finish');
|
||||
animation.finish();
|
||||
|
||||
await eventWatcher.wait_for('finish');
|
||||
|
||||
assert_false(rAFReceived,
|
||||
'finish event should be fired before requestAnimationFrame');
|
||||
}, 'Fires finish event before requestAnimationFrame');
|
||||
|
||||
function animationType(anim) {
|
||||
if (anim instanceof CSSAnimation) {
|
||||
return 'CSSAnimation';
|
||||
} else if (anim instanceof CSSTransition) {
|
||||
return 'CSSTransition';
|
||||
} else {
|
||||
return 'ScriptAnimation';
|
||||
}
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
createStyle(t, { '@keyframes anim': '' });
|
||||
const div = createDiv(t);
|
||||
|
||||
getComputedStyle(div).marginLeft;
|
||||
div.style = 'animation: anim 100s; ' +
|
||||
'transition: margin-left 100s; ' +
|
||||
'margin-left: 100px;';
|
||||
div.animate(null, 100 * MS_PER_SEC);
|
||||
const animations = div.getAnimations();
|
||||
|
||||
let receivedEvents = [];
|
||||
animations.forEach(anim => {
|
||||
anim.onfinish = event => {
|
||||
receivedEvents.push({
|
||||
type: animationType(anim) + ':' + event.type,
|
||||
timeStamp: event.timeStamp
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
await Promise.all(animations.map(anim => anim.ready));
|
||||
|
||||
// Setting current time to the time just before the effect end.
|
||||
animations.forEach(anim => anim.currentTime = 100 * MS_PER_SEC - 1);
|
||||
|
||||
await waitForNextFrame();
|
||||
|
||||
assert_array_equals(receivedEvents.map(event => event.type),
|
||||
[ 'CSSTransition:finish', 'CSSAnimation:finish',
|
||||
'ScriptAnimation:finish' ],
|
||||
'finish events for various animation type should be sorted by composite ' +
|
||||
'order');
|
||||
}, 'Sorts finish events by composite order');
|
||||
|
||||
promise_test(async t => {
|
||||
createStyle(t, { '@keyframes anim': '' });
|
||||
const div = createDiv(t);
|
||||
|
||||
let receivedEvents = [];
|
||||
function receiveEvent(type, timeStamp) {
|
||||
receivedEvents.push({ type, timeStamp });
|
||||
}
|
||||
|
||||
div.onanimationcancel = event => receiveEvent(event.type, event.timeStamp);
|
||||
div.ontransitioncancel = event => receiveEvent(event.type, event.timeStamp);
|
||||
|
||||
getComputedStyle(div).marginLeft;
|
||||
div.style = 'animation: anim 100s; ' +
|
||||
'transition: margin-left 100s; ' +
|
||||
'margin-left: 100px;';
|
||||
div.animate(null, 100 * MS_PER_SEC);
|
||||
const animations = div.getAnimations();
|
||||
|
||||
animations.forEach(anim => {
|
||||
anim.oncancel = event => {
|
||||
receiveEvent(animationType(anim) + ':' + event.type, event.timeStamp);
|
||||
};
|
||||
});
|
||||
|
||||
await Promise.all(animations.map(anim => anim.ready));
|
||||
|
||||
const timeInAnimationReady = document.timeline.currentTime;
|
||||
|
||||
// Call cancel() in reverse composite order. I.e. canceling for script
|
||||
// animation happen first, then for CSS animation and CSS transition.
|
||||
// 'cancel' events for these animations should be sorted by composite
|
||||
// order.
|
||||
animations.reverse().forEach(anim => anim.cancel());
|
||||
|
||||
// requestAnimationFrame callback which is actually the _same_ frame since we
|
||||
// are currently operating in the `ready` callbac of the animations which
|
||||
// happens as part of the "Update animations and send events" procedure
|
||||
// _before_ we run animation frame callbacks.
|
||||
await waitForAnimationFrames(1);
|
||||
|
||||
assert_times_equal(timeInAnimationReady, document.timeline.currentTime,
|
||||
'A rAF callback should happen in the same frame');
|
||||
|
||||
assert_array_equals(receivedEvents.map(event => event.type),
|
||||
// This ordering needs more clarification in the spec, but the intention is
|
||||
// that the cancel playback event fires before the equivalent CSS cancel
|
||||
// event in each case.
|
||||
[ 'CSSTransition:cancel', 'CSSAnimation:cancel', 'ScriptAnimation:cancel',
|
||||
'transitioncancel', 'animationcancel' ],
|
||||
'cancel events should be sorted by composite order');
|
||||
}, 'Sorts cancel events by composite order');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
getComputedStyle(div).marginLeft;
|
||||
div.style = 'transition: margin-left 100s; margin-left: 100px;';
|
||||
const anim = div.getAnimations()[0];
|
||||
|
||||
let receivedEvents = [];
|
||||
anim.oncancel = event => receivedEvents.push(event);
|
||||
|
||||
const eventWatcher = new EventWatcher(t, div, 'transitionstart');
|
||||
await eventWatcher.wait_for('transitionstart');
|
||||
|
||||
const timeInEventCallback = document.timeline.currentTime;
|
||||
|
||||
// Calling cancel() queues a cancel event
|
||||
anim.cancel();
|
||||
|
||||
await waitForAnimationFrames(1);
|
||||
assert_times_equal(timeInEventCallback, document.timeline.currentTime,
|
||||
'A rAF callback should happen in the same frame');
|
||||
|
||||
assert_array_equals(receivedEvents, [],
|
||||
'The queued cancel event shouldn\'t be dispatched in the same frame');
|
||||
|
||||
await waitForAnimationFrames(1);
|
||||
assert_array_equals(receivedEvents.map(event => event.type), ['cancel'],
|
||||
'The cancel event should be dispatched in a later frame');
|
||||
}, 'Queues a cancel event in transitionstart event callback');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
getComputedStyle(div).marginLeft;
|
||||
div.style = 'transition: margin-left 100s; margin-left: 100px;';
|
||||
const anim = div.getAnimations()[0];
|
||||
|
||||
let receivedEvents = [];
|
||||
anim.oncancel = event => receivedEvents.push(event);
|
||||
div.ontransitioncancel = event => receivedEvents.push(event);
|
||||
|
||||
await anim.ready;
|
||||
|
||||
anim.cancel();
|
||||
|
||||
await waitForAnimationFrames(1);
|
||||
|
||||
assert_array_equals(receivedEvents.map(event => event.type),
|
||||
[ 'cancel', 'transitioncancel' ],
|
||||
'Playback and CSS events for the same transition should be sorted by ' +
|
||||
'schedule event time and composite order');
|
||||
}, 'Sorts events for the same transition');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
const anim = div.animate(null, 100 * MS_PER_SEC);
|
||||
|
||||
let receivedEvents = [];
|
||||
anim.oncancel = event => receivedEvents.push(event);
|
||||
anim.onfinish = event => receivedEvents.push(event);
|
||||
|
||||
await anim.ready;
|
||||
|
||||
anim.finish();
|
||||
anim.cancel();
|
||||
|
||||
await waitForAnimationFrames(1);
|
||||
|
||||
assert_array_equals(receivedEvents.map(event => event.type),
|
||||
[ 'finish', 'cancel' ],
|
||||
'Calling finish() synchronously queues a finish event when updating the ' +
|
||||
'finish state so it should appear before the cancel event');
|
||||
}, 'Playback events with the same timeline retain the order in which they are' +
|
||||
'queued');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
|
||||
// Create two animations with separate timelines
|
||||
|
||||
const timelineA = document.timeline;
|
||||
const animA = div.animate(null, 100 * MS_PER_SEC);
|
||||
|
||||
const timelineB = new DocumentTimeline();
|
||||
const animB = new Animation(
|
||||
new KeyframeEffect(div, null, 100 * MS_PER_SEC),
|
||||
timelineB
|
||||
);
|
||||
animB.play();
|
||||
|
||||
animA.currentTime = 99.9 * MS_PER_SEC;
|
||||
animB.currentTime = 99.9 * MS_PER_SEC;
|
||||
|
||||
// When the next tick happens both animations should be updated, and we will
|
||||
// notice that they are now finished. As a result their finished promise
|
||||
// callbacks should be queued. All of that should happen before we run the
|
||||
// next microtask checkpoint and actually run the promise callbacks and
|
||||
// hence the calls to cancel should not stop the existing callbacks from
|
||||
// being run.
|
||||
|
||||
animA.finished.then(() => { animB.cancel() });
|
||||
animB.finished.then(() => { animA.cancel() });
|
||||
|
||||
await Promise.all([animA.finished, animB.finished]);
|
||||
}, 'All timelines are updated before running microtasks');
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user