Mypal/dom/animation/test/chrome/test_observers_for_sync_api...

855 lines
31 KiB
HTML

<!DOCTYPE html>
<meta charset=utf-8>
<title>
Test chrome-only MutationObserver animation notifications for sync APIs
</title>
<script type="application/javascript" src="../testharness.js"></script>
<script type="application/javascript" src="../testharnessreport.js"></script>
<script type="application/javascript" src="../testcommon.js"></script>
<div id="log"></div>
<style>
@keyframes anim {
to { transform: translate(100px); }
}
@keyframes anotherAnim {
to { transform: translate(0px); }
}
</style>
<script>
function assert_record_list(actual, expected, desc, index, listName) {
assert_equals(actual.length, expected.length,
`${desc} - record[${index}].${listName} length`);
if (actual.length != expected.length) {
return;
}
for (var i = 0; i < actual.length; i++) {
assert_not_equals(actual.indexOf(expected[i]), -1,
`${desc} - record[${index}].${listName} contains expected Animation`);
}
}
function assert_equals_records(actual, expected, desc) {
assert_equals(actual.length, expected.length, `${desc} - number of records`);
if (actual.length != expected.length) {
return;
}
for (var i = 0; i < actual.length; i++) {
assert_record_list(actual[i].addedAnimations,
expected[i].added, desc, i, "addedAnimations");
assert_record_list(actual[i].changedAnimations,
expected[i].changed, desc, i, "changedAnimations");
assert_record_list(actual[i].removedAnimations,
expected[i].removed, desc, i, "removedAnimations");
}
}
// Create a pseudo element
function createPseudo(test, element, type) {
addStyle(test, { '@keyframes anim': '',
['.pseudo::' + type]: 'animation: anim 10s;' });
element.classList.add('pseudo');
var anims = document.getAnimations();
assert_true(anims.length >= 1);
var anim = anims[anims.length - 1];
assert_equals(anim.effect.target.parentElement, element);
assert_equals(anim.effect.target.type, '::' + type);
anim.cancel();
return anim.effect.target;
}
[ { subtree: false },
{ subtree: true }
].forEach(aOptions => {
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.duration = 100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after duration is changed");
anim.effect.timing.duration = 100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
anim.currentTime = anim.effect.timing.duration * 2;
anim.finish();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation end");
anim.effect.timing.duration = anim.effect.timing.duration * 3;
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation restarted");
anim.effect.timing.duration = "auto";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after duration set \"auto\"");
anim.effect.timing.duration = "auto";
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value \"auto\"");
}, "change_duration_and_currenttime");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.endDelay = 10 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after endDelay is changed");
anim.effect.timing.endDelay = 10 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
anim.currentTime = 109 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after currentTime during endDelay");
anim.effect.timing.endDelay = -110 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[], "records after assigning negative value");
}, "change_enddelay_and_currenttime");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC,
endDelay: -100 * MS_PER_SEC });
assert_equals_records(observer.takeRecords(),
[], "records after animation is added");
}, "zero_end_time");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.iterations = 2;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after iterations is changed");
anim.effect.timing.iterations = 2;
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
anim.effect.timing.iterations = 0;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation end");
anim.effect.timing.iterations = Infinity;
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation restarted");
}, "change_iterations");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.delay = 100;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after delay is changed");
anim.effect.timing.delay = 100;
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
anim.effect.timing.delay = -100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation end");
anim.effect.timing.delay = 0;
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation restarted");
}, "change_delay");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC,
easing: "steps(2, start)" });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.easing = "steps(2, end)";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after easing is changed");
anim.effect.timing.easing = "steps(2, end)";
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
}, "change_easing");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100, delay: -100 });
assert_equals_records(observer.takeRecords(),
[], "records after assigning negative value");
}, "negative_delay_in_constructor");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var effect = new KeyframeEffectReadOnly(null,
{ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
var anim = new Animation(effect, document.timeline);
anim.play();
assert_equals_records(observer.takeRecords(),
[], "no records after animation is added");
}, "create_animation_without_target");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.target = div;
assert_equals_records(observer.takeRecords(),
[], "no records after setting the same target");
anim.effect.target = null;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after setting null");
anim.effect.target = null;
assert_equals_records(observer.takeRecords(),
[], "records after setting redundant null");
}, "set_redundant_animation_target");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect = null;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation is removed");
}, "set_null_animation_effect");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = new Animation();
anim.play();
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
}, "set_effect_on_null_effect_animation");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after replace effects");
}, "replace_effect_targeting_on_the_same_element");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.currentTime = 60 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after animation is changed");
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
50 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after replacing effects");
}, "replace_effect_targeting_on_the_same_element_not_in_effect");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate([ { marginLeft: "0px" },
{ marginLeft: "-20px" },
{ marginLeft: "100px" },
{ marginLeft: "50px" } ],
{ duration: 100 * MS_PER_SEC });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.spacing = "paced(margin-left)";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after animation is changed");
}, "set_spacing");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate([ { marginLeft: "0px" },
{ marginLeft: "-20px" },
{ marginLeft: "100px" },
{ marginLeft: "50px" } ],
{ duration: 100 * MS_PER_SEC,
spacing: "paced(margin-left)" });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.spacing = "paced(animation-duration)";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after setting a non-animatable paced property");
}, "set_spacing_on_a_non-animatable_property");
test(t => {
var div = addDiv(t);
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate([ { marginLeft: "0px" },
{ marginLeft: "-20px" },
{ marginLeft: "100px" },
{ marginLeft: "50px" } ],
{ duration: 100 * MS_PER_SEC,
spacing: "paced(margin-left)" });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.spacing = "paced(margin-left)";
assert_equals_records(observer.takeRecords(),
[], "no record after setting the same spacing");
}, "set_the_same_spacing");
// Test that starting a single animation that is cancelled by calling
// cancel() dispatches an added notification and then a removed
// notification.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s forwards" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
animations[0].cancel();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: animations }],
"records after animation end");
// Re-trigger the animation.
animations[0].play();
// Single MutationRecord for the Animation (re-)addition.
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
}, "single_animation_cancelled_api");
// Test that updating a property on the Animation object dispatches a changed
// notification.
[
{ prop: "playbackRate", val: 0.5 },
{ prop: "startTime", val: 50 * MS_PER_SEC },
{ prop: "currentTime", val: 50 * MS_PER_SEC },
].forEach(function(aChangeTest) {
test(t => {
// We use a forwards fill mode so that even if the change we make causes
// the animation to become finished, it will still be "relevant" so we
// won't mark it as removed.
var div = addDiv(t, { style: "animation: anim 100s forwards" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
// Update the property.
animations[0][aChangeTest.prop] = aChangeTest.val;
// Make a redundant change.
animations[0][aChangeTest.prop] = animations[0][aChangeTest.prop];
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] }],
"records after animation property change");
}, `single_animation_api_change_${aChangeTest.prop}`);
});
// Test that making a redundant change to currentTime while an Animation
// is pause-pending still generates a change MutationRecord since setting
// the currentTime to any value in this state aborts the pending pause.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
animations[0].pause();
// We are now pause-pending. Even if we make a redundant change to the
// currentTime, we should still get a change record because setting the
// currentTime while pause-pending has the effect of cancelling a pause.
animations[0].currentTime = animations[0].currentTime;
// Two MutationRecords for the Animation changes: one for pausing, one
// for aborting the pause.
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] },
{ added: [], changed: animations, removed: [] }],
"records after pausing then seeking");
}, "change_currentTime_while_pause_pending");
// Test that calling finish() on a forwards-filling Animation dispatches
// a changed notification.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s forwards" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
animations[0].finish();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] }],
"records after finish()");
// Redundant finish.
animations[0].finish();
// Ensure no change records.
assert_equals_records(observer.takeRecords(),
[], "records after redundant finish()");
}, "finish_with_forwards_fill");
// Test that calling finish() on an Animation that does not fill forwards,
// dispatches a removal notification.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
animations[0].finish();
// Single MutationRecord for the Animation removal.
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: animations }],
"records after finishing");
}, "finish_without_fill");
// Test that calling finish() on a forwards-filling Animation dispatches
test(t => {
var div = addDiv(t, { style: "animation: anim 100s" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animation = div.getAnimations()[0];
assert_equals_records(observer.takeRecords(),
[{ added: [animation], changed: [], removed: []}],
"records after creation");
animation.id = "new id";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [animation], removed: []}],
"records after id is changed");
animation.id = "new id";
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value with id");
}, "change_id");
// Test that calling reverse() dispatches a changed notification.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s both" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
animations[0].reverse();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] }],
"records after calling reverse()");
}, "reverse");
// Test that calling reverse() does *not* dispatch a changed notification
// when playbackRate == 0.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s both" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals(animations.length, 1,
"getAnimations().length after animation start");
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: [] }],
"records after animation start");
// Seek to the middle and set playbackRate to zero.
animations[0].currentTime = 50 * MS_PER_SEC;
animations[0].playbackRate = 0;
// Two MutationRecords, one for each change.
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] },
{ added: [], changed: animations, removed: [] }],
"records after seeking and setting playbackRate");
animations[0].reverse();
// We should get no notifications.
assert_equals_records(observer.takeRecords(),
[], "records after calling reverse()");
}, "reverse_with_zero_playbackRate");
// Test that attempting to start an animation that should already be finished
// does not send any notifications.
test(t => {
// Start an animation that should already be finished.
var div = addDiv(t, { style: "animation: anim 1s -2s;" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
// The animation should cause no Animations to be created.
var animations = div.getAnimations();
assert_equals(animations.length, 0,
"getAnimations().length after animation start");
// And we should get no notifications.
assert_equals_records(observer.takeRecords(),
[], "records after attempted animation start");
}, "already_finished");
test(t => {
var div = addDiv(t, { style: "animation: anim 100s, anotherAnim 100s" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var animations = div.getAnimations();
assert_equals_records(observer.takeRecords(),
[{ added: animations, changed: [], removed: []}],
"records after creation");
div.style.animation = "anotherAnim 100s, anim 100s";
animations = div.getAnimations();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: []}],
"records after the order is changed");
div.style.animation = "anotherAnim 100s, anim 100s";
assert_equals_records(observer.takeRecords(),
[], "no records after applying the same order");
}, "animtion_order_change");
});
test(t => {
var div = addDiv(t);
var observer = setupSynchronousObserver(t, div, true);
var child = document.createElement("div");
div.appendChild(child);
var anim1 = div.animate({ marginLeft: [ "0px", "50px" ] },
100 * MS_PER_SEC);
var anim2 = child.animate({ marginLeft: [ "0px", "100px" ] },
50 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim1], changed: [], removed: [] },
{ added: [anim2], changed: [], removed: [] }],
"records after animation is added");
// After setting a new effect, we remove the current animation, anim1,
// because it is no longer attached to |div|, and then remove the previous
// animation, anim2. Finally, add back the anim1 which is in effect on
// |child| now. In addition, we sort them by tree order and they are
// batched.
anim1.effect = anim2.effect;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim1] }, // div
{ added: [anim1], changed: [], removed: [anim2] }], // child
"records after animation effects are changed");
}, "set_effect_with_previous_animation");
test(t => {
var div = addDiv(t);
var observer = setupSynchronousObserver(t, document, true);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
var newTarget = document.createElement("div");
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.target = null;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after setting null");
anim.effect.target = div;
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after setting a target");
anim.effect.target = addDiv(t);
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] },
{ added: [anim], changed: [], removed: [] }],
"records after setting a different target");
}, "set_animation_target");
test(t => {
var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, true);
var anim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.timing.duration = 100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after duration is changed");
anim.effect.timing.duration = 100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value");
anim.currentTime = anim.effect.timing.duration * 2;
anim.finish();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation end");
anim.effect.timing.duration = anim.effect.timing.duration * 3;
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation restarted");
anim.effect.timing.duration = "auto";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after duration set \"auto\"");
anim.effect.timing.duration = "auto";
assert_equals_records(observer.takeRecords(),
[], "records after assigning same value \"auto\"");
}, "change_duration_and_currenttime_on_pseudo_elements");
test(t => {
var div = addDiv(t);
var pseudoTarget = createPseudo(t, div, 'before');
var observer = setupSynchronousObserver(t, div, false);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.finish();
pAnim.finish();
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [], removed: [anim] }],
"records after animation is finished");
}, "exclude_animations_targeting_pseudo_elements");
</script>