mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-11 05:39:41 +02:00
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
253 lines
7.8 KiB
HTML
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>
|