gecko-dev/layout/style/test/test_animations_event_order.html
Mantaroh Yoshinaga a7a27655d4 Bug 1366934 - Remove validation of transitioncancel/animationcanel event order tests. r=hiro
The animationcancel and transitioncancel is not absolute order when elapsed time
of siblings cancel event is same. So we will not need to check the order of
cancel events.

MozReview-Commit-ID: 8fiwgcvTlwE

--HG--
extra : rebase_source : 055c4d47554753f79d17eae563dea1ff6636e60b
2017-06-20 14:31:41 +09:00

710 lines
21 KiB
HTML

<!doctype html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1183461
-->
<!--
This test is similar to those in test_animations.html with the exception
that those tests interact with a single element at a time. The tests in this
file are specifically concerned with testing the ordering of events between
elements for which most of the utilities in animation_utils.js are not
suited.
-->
<head>
<meta charset=utf-8>
<title>Test for CSS Animation and Transition event ordering
(Bug 1183461)</title>
<script type="application/javascript"
src="/tests/SimpleTest/SimpleTest.js"></script>
<!-- We still need animation_utils.js for advance_clock -->
<script type="application/javascript" src="animation_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
@keyframes anim { to { margin-left: 100px } }
@keyframes animA { to { margin-left: 100px } }
@keyframes animB { to { margin-left: 100px } }
@keyframes animC { to { margin-left: 100px } }
</style>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1183461">Mozilla Bug
1183461</a>
<div id="display"></div>
<pre id="test">
<script type="application/javascript">
'use strict';
// Take over the refresh driver right from the start.
advance_clock(0);
// Common test scaffolding
var gEventsReceived = [];
var gDisplay = document.getElementById('display');
[ 'animationstart',
'animationiteration',
'animationend',
'animationcancel',
'transitionrun',
'transitionstart',
'transitionend',
'transitioncancel' ]
.forEach(event =>
gDisplay.addEventListener(event,
event => gEventsReceived.push(event)));
function checkEventOrder(...args) {
// Argument format:
// Arguments = ExpectedEvent*, desc
// ExpectedEvent =
// [ target|animationName|transitionProperty, (pseudo,) message ]
var expectedEvents = args.slice(0, -1);
var desc = args[args.length - 1];
var isTestingNameOrProperty = expectedEvents.length &&
typeof expectedEvents[0][0] == 'string';
var formatEvent = (target, nameOrProperty, pseudo, message) =>
isTestingNameOrProperty ?
`${nameOrProperty}${pseudo}:${message}` :
`${target.id}${pseudo}:${message}`;
var actual = gEventsReceived.map(
event => formatEvent(event.target,
event.animationName || event.propertyName,
event.pseudoElement, event.type)
).join(';');
var expected = expectedEvents.map(
event => event.length == 3 ?
formatEvent(event[0], event[0], event[1], event[2]) :
formatEvent(event[0], event[0], '', event[1])
).join(';');
is(actual, expected, desc);
gEventsReceived = [];
}
// 1. TESTS FOR SORTING BY TREE ORDER
// 1a. Test that simultaneous events are sorted by tree order (siblings)
var divs = [ document.createElement('div'),
document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.setAttribute('style', 'animation: anim 10s');
div.setAttribute('id', 'div' + i);
getComputedStyle(div).animationName; // trigger building of animation
});
advance_clock(0);
checkEventOrder([ divs[0], 'animationstart' ],
[ divs[1], 'animationstart' ],
[ divs[2], 'animationstart' ],
'Simultaneous start on siblings');
divs.forEach(div => div.remove());
divs = [];
// 1b. Test that simultaneous events are sorted by tree order (children)
divs = [ document.createElement('div'),
document.createElement('div'),
document.createElement('div') ];
// Create the following arrangement:
//
// display
// / \
// div[0] div[1]
// /
// div[2]
gDisplay.appendChild(divs[0]);
gDisplay.appendChild(divs[1]);
divs[0].appendChild(divs[2]);
divs.forEach((div, i) => {
div.setAttribute('style', 'animation: anim 10s');
div.setAttribute('id', 'div' + i);
getComputedStyle(div).animationName; // trigger building of animation
});
advance_clock(0);
checkEventOrder([ divs[0], 'animationstart' ],
[ divs[2], 'animationstart' ],
[ divs[1], 'animationstart' ],
'Simultaneous start on children');
divs.forEach(div => div.remove());
divs = [];
// 1c. Test that simultaneous events are sorted by tree order (pseudos)
divs = [ document.createElement('div'),
document.createElement('div') ];
// Create the following arrangement:
//
// display
// |
// div[0]
// ::before,
// ::after
// |
// div[1]
gDisplay.appendChild(divs[0]);
divs[0].appendChild(divs[1]);
divs.forEach((div, i) => {
div.setAttribute('style', 'animation: anim 10s');
div.setAttribute('id', 'div' + i);
});
var extraStyle = document.createElement('style');
document.head.appendChild(extraStyle);
var sheet = extraStyle.sheet;
sheet.insertRule('#div0::after { animation: anim 10s }', 0);
sheet.insertRule('#div0::before { animation: anim 10s }', 1);
sheet.insertRule('#div0::after, #div0::before { ' +
' content: " " }', 2);
getComputedStyle(divs[0]).animationName; // build animation
getComputedStyle(divs[1]).animationName; // build animation
advance_clock(0);
checkEventOrder([ divs[0], 'animationstart' ],
[ divs[0], '::before', 'animationstart' ],
[ divs[0], '::after', 'animationstart' ],
[ divs[1], 'animationstart' ],
'Simultaneous start on pseudo-elements');
divs.forEach(div => div.remove());
divs = [];
sheet = undefined;
extraStyle.remove();
extraStyle = undefined;
// 2. TESTS FOR SORTING BY TIME
// 2a. Test that events are sorted by time
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.setAttribute('style', 'animation: anim 10s');
div.setAttribute('id', 'div' + i);
});
divs[0].style.animationDelay = '5s';
advance_clock(0);
advance_clock(20000);
checkEventOrder([ divs[1], 'animationstart' ],
[ divs[0], 'animationstart' ],
[ divs[1], 'animationend' ],
[ divs[0], 'animationend' ],
'Sorting of start and end events by time');
divs.forEach(div => div.remove());
divs = [];
// 2b. Test different events within the one element
var div = document.createElement('div');
gDisplay.appendChild(div);
div.style.animation = 'anim 4s 2, ' + // Repeat at t=4s
'anim 10s 5s, ' + // Start at t=5s
'anim 3s'; // End at t=3s
div.setAttribute('id', 'div');
getComputedStyle(div).animationName; // build animation
advance_clock(0);
advance_clock(5000);
checkEventOrder([ div, 'animationstart' ],
[ div, 'animationstart' ],
[ div, 'animationend' ],
[ div, 'animationiteration' ],
[ div, 'animationstart' ],
'Sorting of different events by time within an element');
div.remove();
div = undefined;
// 2c. Test negative delay is sorted equal to zero delay but before
// positive delay
divs = [ document.createElement('div'),
document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.setAttribute('id', 'div' + i);
});
divs[0].style.animation = 'anim 20s 5s'; // Positive delay, sorts last
divs[1].style.animation = 'anim 20s'; // 0s delay
divs[2].style.animation = 'anim 20s -5s'; // Negative delay, sorts same as
// 0s delay, i.e. use document
// position
advance_clock(0);
advance_clock(5000);
checkEventOrder([ divs[1], 'animationstart' ],
[ divs[2], 'animationstart' ],
[ divs[0], 'animationstart' ],
'Sorting of events including negative delay');
divs.forEach(div => div.remove());
divs = [];
// 3. TESTS FOR SORTING BY animation-name POSITION
// 3a. Test animation-name position
div = document.createElement('div');
gDisplay.appendChild(div);
div.style.animation = 'animA 10s, animB 5s, animC 5s 2';
div.setAttribute('id', 'div');
getComputedStyle(div).animationName; // build animation
advance_clock(0);
checkEventOrder([ 'animA', 'animationstart' ],
[ 'animB', 'animationstart' ],
[ 'animC', 'animationstart' ],
'Sorting of simultaneous animationstart events by ' +
'animation-name');
advance_clock(5000);
checkEventOrder([ 'animB', 'animationend' ],
[ 'animC', 'animationiteration' ],
'Sorting of different types of events by animation-name');
div.remove();
div = undefined;
// 3b. Test time trumps animation-name position
div = document.createElement('div');
gDisplay.appendChild(div);
div.style.animation = 'animA 10s 2s, animB 10s 1s';
div.setAttribute('id', 'div');
advance_clock(0);
advance_clock(3000);
checkEventOrder([ 'animB', 'animationstart' ],
[ 'animA', 'animationstart' ],
'Events are sorted by time first, before animation-position');
div.remove();
div = undefined;
// 4. TESTS FOR TRANSITIONS
// 4a. Test sorting transitions by document position
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.style.transition = 'margin-left 10s';
div.setAttribute('id', 'div' + i);
});
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[0], 'transitionstart' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionend' ],
[ divs[1], 'transitionend' ],
'Simultaneous transitionrun/start/end on siblings');
divs.forEach(div => div.remove());
divs = [];
// 4b. Test sorting transitions by document position (children)
divs = [ document.createElement('div'),
document.createElement('div'),
document.createElement('div') ];
// Create the following arrangement:
//
// display
// / \
// div[0] div[1]
// /
// div[2]
gDisplay.appendChild(divs[0]);
gDisplay.appendChild(divs[1]);
divs[0].appendChild(divs[2]);
divs.forEach((div, i) => {
div.style.marginLeft = '0px';
div.style.transition = 'margin-left 10s';
div.setAttribute('id', 'div' + i);
});
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[0], 'transitionstart' ],
[ divs[2], 'transitionrun' ],
[ divs[2], 'transitionstart' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionend' ],
[ divs[2], 'transitionend' ],
[ divs[1], 'transitionend' ],
'Simultaneous transitionrun/start/end on children');
divs.forEach(div => div.remove());
divs = [];
// 4c. Test sorting transitions by document position (pseudos)
divs = [ document.createElement('div'),
document.createElement('div') ];
// Create the following arrangement:
//
// display
// |
// div[0]
// ::before,
// ::after
// |
// div[1]
gDisplay.appendChild(divs[0]);
divs[0].appendChild(divs[1]);
divs.forEach((div, i) => {
div.setAttribute('id', 'div' + i);
});
extraStyle = document.createElement('style');
document.head.appendChild(extraStyle);
sheet = extraStyle.sheet;
sheet.insertRule('div, #div0::after, #div0::before { ' +
' transition: margin-left 10s; ' +
' margin-left: 0px }', 0);
sheet.insertRule('div.active, #div0.active::after, #div0.active::before { ' +
' margin-left: 100px }', 1);
sheet.insertRule('#div0::after, #div0::before { ' +
' content: " " }', 2);
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.classList.add('active'));
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[0], 'transitionstart' ],
[ divs[0], '::before', 'transitionrun' ],
[ divs[0], '::before', 'transitionstart' ],
[ divs[0], '::after', 'transitionrun' ],
[ divs[0], '::after', 'transitionstart' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionend' ],
[ divs[0], '::before', 'transitionend' ],
[ divs[0], '::after', 'transitionend' ],
[ divs[1], 'transitionend' ],
'Simultaneous transitionrun/start/end on pseudo-elements');
divs.forEach(div => div.remove());
divs = [];
sheet = undefined;
extraStyle.remove();
extraStyle = undefined;
// 4d. Test sorting transitions by time
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.setAttribute('id', 'div' + i);
});
divs[0].style.transition = 'margin-left 10s';
divs[1].style.transition = 'margin-left 5s';
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[0], 'transitionstart' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[1], 'transitionend' ],
[ divs[0], 'transitionend' ],
'Sorting of transitionrun/start/end events by time');
divs.forEach(div => div.remove());
divs = [];
// 4e. Test sorting transitions by time (with delay)
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.setAttribute('id', 'div' + i);
});
divs[0].style.transition = 'margin-left 5s 5s';
divs[1].style.transition = 'margin-left 5s';
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10 * 1000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionstart' ],
[ divs[1], 'transitionend' ],
[ divs[0], 'transitionend' ],
'Sorting of transitionrun/start/end events by time' +
'(including delay)');
divs.forEach(div => div.remove());
divs = [];
// 4f. Test sorting transitions by transition-property
div = document.createElement('div');
gDisplay.appendChild(div);
div.style.opacity = '0';
div.style.marginLeft = '0px';
div.style.transition = 'all 5s';
getComputedStyle(div).marginLeft;
div.style.opacity = '1';
div.style.marginLeft = '100px';
getComputedStyle(div).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ 'margin-left', 'transitionrun' ],
[ 'margin-left', 'transitionstart' ],
[ 'opacity', 'transitionrun' ],
[ 'opacity', 'transitionstart' ],
[ 'margin-left', 'transitionend' ],
[ 'opacity', 'transitionend' ],
'Sorting of transitionrun/start/end events by ' +
'transition-property')
div.remove();
div = undefined;
// 4g. Test document position beats transition-property
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.style.opacity = '0';
div.style.transition = 'all 10s';
div.setAttribute('id', 'div' + i);
});
getComputedStyle(divs[0]).marginLeft;
divs[0].style.opacity = '1';
divs[1].style.marginLeft = '100px';
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[0], 'transitionstart' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionend' ],
[ divs[1], 'transitionend' ],
'Transition events are sorted by document position first, ' +
'before transition-property');
divs.forEach(div => div.remove());
divs = [];
// 4h. Test time beats transition-property
div = document.createElement('div');
gDisplay.appendChild(div);
div.style.opacity = '0';
div.style.marginLeft = '0px';
div.style.transition = 'margin-left 10s, opacity 5s';
getComputedStyle(div).marginLeft;
div.style.opacity = '1';
div.style.marginLeft = '100px';
getComputedStyle(div).marginLeft;
advance_clock(0);
advance_clock(10000);
checkEventOrder([ 'margin-left', 'transitionrun' ],
[ 'margin-left', 'transitionstart' ],
[ 'opacity', 'transitionrun' ],
[ 'opacity', 'transitionstart' ],
[ 'opacity', 'transitionend' ],
[ 'margin-left', 'transitionend' ],
'Transition events are sorted by time first, before ' +
'transition-property');
div.remove();
div = undefined;
// 4i. Test sorting transitions by document position (negative delay)
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.setAttribute('id', 'div' + i);
});
divs[0].style.transition = 'margin-left 10s 5s';
divs[1].style.transition = 'margin-left 10s';
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(15 * 1000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionstart' ],
[ divs[1], 'transitionend' ],
[ divs[0], 'transitionend' ],
'Simultaneous transitionrun/start/end on siblings');
divs.forEach(div => div.remove());
divs = [];
// 4j. Test sorting transitions with cancel
// The order of transitioncancel is based on StyleManager.
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.setAttribute('id', 'div' + i);
});
divs[0].style.transition = 'margin-left 10s 5s';
divs[1].style.transition = 'margin-left 10s';
getComputedStyle(divs[0]).marginLeft;
divs.forEach(div => div.style.marginLeft = '100px');
getComputedStyle(divs[0]).marginLeft;
advance_clock(0);
advance_clock(5 * 1000);
divs.forEach(div => {
div.style.display = 'none';
// The transitioncancel event order is not absolute when firing siblings
// transitioncancel on same elapsed time.
// Force to flush style for the element so that the transition on the element
// iscancelled and corresponding cancel event is queued respectively.
getComputedStyle(div).display;
});
advance_clock(10 * 1000);
checkEventOrder([ divs[0], 'transitionrun' ],
[ divs[1], 'transitionrun' ],
[ divs[1], 'transitionstart' ],
[ divs[0], 'transitionstart' ],
[ divs[0], 'transitioncancel' ],
[ divs[1], 'transitioncancel' ],
'Simultaneous transitionrun/start/cancel on siblings');
divs.forEach(div => div.remove());
divs = [];
// 4k. Test sorting animations with cancel
divs = [ document.createElement('div'),
document.createElement('div') ];
divs.forEach((div, i) => {
gDisplay.appendChild(div);
div.style.marginLeft = '0px';
div.setAttribute('id', 'div' + i);
});
divs[0].style.animation = 'anim 10s 5s';
divs[1].style.animation = 'anim 10s';
getComputedStyle(divs[0]).animation; // flush
advance_clock(0); // divs[1]'s animation start
advance_clock(5 * 1000); // divs[0]'s animation start
divs.forEach(div => {
div.style.display = 'none';
// The animationcancel event order is not absolute when firing siblings
// animationcancel on same elapsed time.
// Force to flush style for the element so that the transition on the element
// iscancelled and corresponding cancel event is queued respectively.
getComputedStyle(div).display;
});
advance_clock(10 * 1000);
checkEventOrder([ divs[1], 'animationstart' ],
[ divs[0], 'animationstart' ],
[ divs[0], 'animationcancel' ],
[ divs[1], 'animationcancel' ],
'Simultaneous animationcancel on siblings');
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
</script>
</body>
</html>