gecko-dev/testing/web-platform/tests/css/css-animations/event-order.tentative.html
Brian Birtles e6ccbab28e Bug 1545707 - Add tests for animation of ::marker pseudo-elements; r=mats
In particular, this tests the composite order of ::marker pseudo-elements and
dispatching cancel events for these elements.

It adds one test that is known to fail in Gecko. This will be fixed in the next
patch in this series.

Differential Revision: https://phabricator.services.mozilla.com/D28175

--HG--
extra : moz-landing-system : lando
2019-04-22 00:54:09 +00:00

253 lines
7.8 KiB
HTML

<!doctype html>
<meta charset=utf-8>
<title>Tests for CSS animation event order</title>
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim {
from { margin-left: 0px; }
to { margin-left: 100px; }
}
@keyframes color-anim {
from { color: red; }
to { color: green; }
}
</style>
<div id="log"></div>
<script type='text/javascript'>
'use strict';
/**
* Asserts that the set of actual and received events match.
* @param actualEvents An array of the received AnimationEvent objects.
* @param expectedEvents A series of array objects representing the expected
* events, each having the form:
* [ event type, target element, [pseudo type], elapsed time ]
*/
const checkEvents = (actualEvents, ...expectedEvents) => {
const actualTypeSummary = actualEvents.map(event => event.type).join(', ');
const expectedTypeSummary = expectedEvents.map(event => event[0]).join(', ');
assert_equals(
actualEvents.length,
expectedEvents.length,
`Number of events received (${actualEvents.length}) \
should match expected number (${expectedEvents.length}) \
(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
);
for (const [index, actualEvent] of actualEvents.entries()) {
const expectedEvent = expectedEvents[index];
const [type, target] = expectedEvent;
const pseudoElement = expectedEvent.length === 4 ? expectedEvent[2] : '';
const elapsedTime = expectedEvent[expectedEvent.length - 1];
assert_equals(
actualEvent.type,
type,
`Event #${index + 1} types should match \
(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
);
assert_equals(
actualEvent.target,
target,
`Event #${index + 1} targets should match`
);
assert_equals(
actualEvent.pseudoElement,
pseudoElement,
`Event #${index + 1} pseudoElements should match`
);
assert_equals(
actualEvent.elapsedTime,
elapsedTime,
`Event #${index + 1} elapsedTimes should match`
);
}
};
const setupAnimation = (t, animationStyle, receiveEvents) => {
const div = addDiv(t, { style: 'animation: ' + animationStyle });
for (const name of ['start', 'iteration', 'end']) {
div['onanimation' + name] = evt => {
receiveEvents.push({
type: evt.type,
target: evt.target,
pseudoElement: evt.pseudoElement,
elapsedTime: evt.elapsedTime,
});
};
}
const watcher = new EventWatcher(t, div, [
'animationstart',
'animationiteration',
'animationend',
]);
const animation = div.getAnimations()[0];
return [animation, watcher, div];
};
promise_test(async t => {
let events = [];
const [animation1, watcher1, div1] =
setupAnimation(t, 'anim 100s 2 paused', events);
const [animation2, watcher2, div2] =
setupAnimation(t, 'anim 100s 2 paused', events);
await Promise.all([ watcher1.wait_for('animationstart'),
watcher2.wait_for('animationstart') ]);
checkEvents(events, ['animationstart', div1, 0],
['animationstart', div2, 0]);
events.length = 0; // Clear received event array
animation1.currentTime = 100 * MS_PER_SEC;
animation2.currentTime = 100 * MS_PER_SEC;
await Promise.all([ watcher1.wait_for('animationiteration'),
watcher2.wait_for('animationiteration') ]);
checkEvents(events, ['animationiteration', div1, 100],
['animationiteration', div2, 100]);
events.length = 0; // Clear received event array
animation1.finish();
animation2.finish();
await Promise.all([ watcher1.wait_for('animationend'),
watcher2.wait_for('animationend') ]);
checkEvents(events, ['animationend', div1, 200],
['animationend', div2, 200]);
}, 'Same events are ordered by elements');
promise_test(async t => {
// Setup a hierarchy as follows:
//
// parent
// |
// (::marker, ::before, ::after)
// |
// child
const parentDiv = addDiv(t, { style: 'animation: anim 100s' });
parentDiv.id = 'parent-div';
addStyle(t, {
'#parent-div::after': "content: ''; animation: anim 100s",
'#parent-div::before': "content: ''; animation: anim 100s",
});
if (CSS.supports('selector(::marker)')) {
parentDiv.style.display = 'list-item';
addStyle(t, {
'#parent-div::marker': "content: ''; animation: color-anim 100s",
});
}
const childDiv = addDiv(t, { style: 'animation: anim 100s' });
parentDiv.append(childDiv);
// Setup event handlers
let events = [];
for (const name of ['start', 'iteration', 'end', 'cancel']) {
parentDiv['onanimation' + name] = evt => {
events.push({
type: evt.type,
target: evt.target,
pseudoElement: evt.pseudoElement,
elapsedTime: evt.elapsedTime,
});
};
}
// Wait a couple of frames for the events to be dispatched
await waitForFrame();
await waitForFrame();
const expectedEvents = [
['animationstart', parentDiv, 0],
['animationstart', parentDiv, '::marker', 0],
['animationstart', parentDiv, '::before', 0],
['animationstart', parentDiv, '::after', 0],
['animationstart', childDiv, 0],
];
if (!CSS.supports('selector(::marker)')) {
expectedEvents.splice(1, 1);
}
checkEvents(events, ...expectedEvents);
}, 'Same events on pseudo-elements follow the prescribed order');
promise_test(async t => {
let events = [];
const [animation1, watcher1, div1] =
setupAnimation(t, 'anim 200s 400s', events);
const [animation2, watcher2, div2] =
setupAnimation(t, 'anim 300s 2', events);
await watcher2.wait_for('animationstart');
animation1.currentTime = 400 * MS_PER_SEC;
animation2.currentTime = 400 * MS_PER_SEC;
events.length = 0; // Clear received event array
await Promise.all([ watcher1.wait_for('animationstart'),
watcher2.wait_for('animationiteration') ]);
checkEvents(events, ['animationiteration', div2, 300],
['animationstart', div1, 0]);
}, 'Start and iteration events are ordered by time');
promise_test(async t => {
let events = [];
const [animation1, watcher1, div1] =
setupAnimation(t, 'anim 150s', events);
const [animation2, watcher2, div2] =
setupAnimation(t, 'anim 100s 2', events);
await Promise.all([ watcher1.wait_for('animationstart'),
watcher2.wait_for('animationstart') ]);
animation1.currentTime = 150 * MS_PER_SEC;
animation2.currentTime = 150 * MS_PER_SEC;
events.length = 0; // Clear received event array
await Promise.all([ watcher1.wait_for('animationend'),
watcher2.wait_for('animationiteration') ]);
checkEvents(events, ['animationiteration', div2, 100],
['animationend', div1, 150]);
}, 'Iteration and end events are ordered by time');
promise_test(async t => {
let events = [];
const [animation1, watcher1, div1] =
setupAnimation(t, 'anim 100s 100s', events);
const [animation2, watcher2, div2] =
setupAnimation(t, 'anim 100s 2', events);
animation1.finish();
animation2.finish();
await Promise.all([ watcher1.wait_for([ 'animationstart',
'animationend' ]),
watcher2.wait_for([ 'animationstart',
'animationend' ]) ]);
checkEvents(events, ['animationstart', div2, 0],
['animationstart', div1, 0],
['animationend', div1, 100],
['animationend', div2, 200]);
}, 'Start and end events are sorted correctly when fired simultaneously');
</script>