mirror of
https://github.com/servo/servo.git
synced 2025-08-12 00:45:33 +01:00
Update web-platform-tests to revision e426a6933a05bf144eba06a1d4c47ba876a4e2d1
This commit is contained in:
parent
415b26e4f1
commit
5e5eccabf8
495 changed files with 14920 additions and 784 deletions
|
@ -0,0 +1,389 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Animation.commitStyles</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-commitstyles">
|
||||
<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';
|
||||
|
||||
function assert_numeric_style_equals(opacity, expected, description) {
|
||||
return assert_approx_equals(
|
||||
parseFloat(opacity),
|
||||
expected,
|
||||
0.0001,
|
||||
description
|
||||
);
|
||||
}
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.opacity = '0.1';
|
||||
|
||||
const animation = div.animate(
|
||||
{ opacity: 0.2 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
|
||||
animation.commitStyles();
|
||||
|
||||
// Cancel the animation so we can inspect the underlying style
|
||||
animation.cancel();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.2);
|
||||
}, 'Commits styles');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
div.style.opacity = '0.1';
|
||||
|
||||
const animA = div.animate(
|
||||
{ opacity: 0.2 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animB = div.animate(
|
||||
{ opacity: 0.3 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
await animA.finished;
|
||||
|
||||
animB.cancel();
|
||||
|
||||
animA.commitStyles();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.2);
|
||||
}, 'Commits styles for an animation that has been removed');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.margin = '10px';
|
||||
|
||||
const animation = div.animate(
|
||||
{ margin: '20px' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
|
||||
animation.commitStyles();
|
||||
|
||||
animation.cancel();
|
||||
|
||||
assert_equals(div.style.marginLeft, '20px');
|
||||
}, 'Commits shorthand styles');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.marginLeft = '10px';
|
||||
|
||||
const animation = div.animate(
|
||||
{ marginInlineStart: '20px' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
|
||||
animation.commitStyles();
|
||||
|
||||
animation.cancel();
|
||||
|
||||
assert_equals(div.style.marginLeft, '20px');
|
||||
}, 'Commits logical properties');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.marginLeft = '10px';
|
||||
|
||||
const animation = div.animate({ opacity: [0.2, 0.7] }, 1000);
|
||||
animation.currentTime = 500;
|
||||
animation.commitStyles();
|
||||
animation.cancel();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.45);
|
||||
}, 'Commits values calculated mid-interval');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.setProperty('--target', '0.5');
|
||||
|
||||
const animation = div.animate(
|
||||
{ opacity: 'var(--target)' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
animation.commitStyles();
|
||||
animation.cancel();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.5);
|
||||
|
||||
// Changes to the variable should have no effect
|
||||
div.style.setProperty('--target', '1');
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.5);
|
||||
}, 'Commits variables as their computed values');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
div.style.fontSize = '10px';
|
||||
|
||||
const animation = div.animate(
|
||||
{ width: '10em' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
animation.commitStyles();
|
||||
animation.cancel();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).width, 100);
|
||||
|
||||
// Changes to the font-size should have no effect
|
||||
div.style.fontSize = '20px';
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).width, 100);
|
||||
}, 'Commits em units as pixel values');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
div.style.opacity = '0.1';
|
||||
|
||||
const animA = div.animate(
|
||||
{ opacity: '0.2' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animB = div.animate(
|
||||
{ opacity: '0.2', composite: 'add' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animC = div.animate(
|
||||
{ opacity: '0.3', composite: 'add' },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
animA.persist();
|
||||
animB.persist();
|
||||
|
||||
await animB.finished;
|
||||
|
||||
// The values above have been chosen such that various error conditions
|
||||
// produce results that all differ from the desired result:
|
||||
//
|
||||
// Expected result:
|
||||
//
|
||||
// animA + animB = 0.4
|
||||
//
|
||||
// Likely error results:
|
||||
//
|
||||
// <underlying> = 0.1
|
||||
// (Commit didn't work at all)
|
||||
//
|
||||
// animB = 0.2
|
||||
// (Didn't add at all when resolving)
|
||||
//
|
||||
// <underlying> + animB = 0.3
|
||||
// (Added to the underlying value instead of lower-priority animations when
|
||||
// resolving)
|
||||
//
|
||||
// <underlying> + animA + animB = 0.5
|
||||
// (Didn't respect the composite mode of lower-priority animations)
|
||||
//
|
||||
// animA + animB + animC = 0.7
|
||||
// (Resolved the whole stack, not just up to the target effect)
|
||||
//
|
||||
|
||||
animB.commitStyles();
|
||||
|
||||
animA.cancel();
|
||||
animB.cancel();
|
||||
animC.cancel();
|
||||
|
||||
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.4);
|
||||
}, 'Commits the intermediate value of an animation in the middle of stack');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
div.style.opacity = '0.1';
|
||||
|
||||
// Setup animation
|
||||
const animation = div.animate(
|
||||
{ opacity: 0.2 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
|
||||
// Setup observer
|
||||
const mutationRecords = [];
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutationRecords.push(...mutations);
|
||||
});
|
||||
observer.observe(div, { attributes: true, attributeOldValue: true });
|
||||
|
||||
animation.commitStyles();
|
||||
|
||||
// Wait for mutation records to be dispatched
|
||||
await Promise.resolve();
|
||||
|
||||
assert_equals(mutationRecords.length, 1, 'Should have one mutation record');
|
||||
|
||||
const mutation = mutationRecords[0];
|
||||
assert_equals(mutation.type, 'attributes');
|
||||
assert_equals(mutation.oldValue, 'opacity: 0.1;');
|
||||
|
||||
observer.disconnect();
|
||||
}, 'Triggers mutation observers when updating style');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
div.style.opacity = '0.2';
|
||||
|
||||
// Setup animation
|
||||
const animation = div.animate(
|
||||
{ opacity: 0.2 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
animation.finish();
|
||||
|
||||
// Setup observer
|
||||
const mutationRecords = [];
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutationRecords.push(...mutations);
|
||||
});
|
||||
observer.observe(div, { attributes: true });
|
||||
|
||||
animation.commitStyles();
|
||||
|
||||
// Wait for mutation records to be dispatched
|
||||
await Promise.resolve();
|
||||
|
||||
assert_equals(mutationRecords.length, 0, 'Should have no mutation records');
|
||||
|
||||
observer.disconnect();
|
||||
}, 'Does NOT trigger mutation observers when the change to style is redundant');
|
||||
|
||||
test(t => {
|
||||
const pseudo = getPseudoElement(t, 'before');
|
||||
const animation = pseudo.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
assert_throws('NoModificationAllowedError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, 'Throws if the target element is a pseudo element');
|
||||
|
||||
test(t => {
|
||||
const animation = createDiv(t).animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
const nonStyleElement
|
||||
= document.createElementNS('http://example.org/test', 'test');
|
||||
document.body.appendChild(nonStyleElement);
|
||||
animation.effect.target = nonStyleElement;
|
||||
|
||||
assert_throws('NoModificationAllowedError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
|
||||
nonStyleElement.remove();
|
||||
}, 'Throws if the target element is not something with a style attribute');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
const animation = div.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
div.style.display = 'none';
|
||||
|
||||
assert_throws('InvalidStateError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, 'Throws if the target effect is display:none');
|
||||
|
||||
test(t => {
|
||||
const container = createDiv(t);
|
||||
const div = createDiv(t);
|
||||
container.append(div);
|
||||
|
||||
const animation = div.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
container.style.display = 'none';
|
||||
|
||||
assert_throws('InvalidStateError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, "Throws if the target effect's ancestor is display:none");
|
||||
|
||||
test(t => {
|
||||
const container = createDiv(t);
|
||||
const div = createDiv(t);
|
||||
container.append(div);
|
||||
|
||||
const animation = div.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
container.style.display = 'contents';
|
||||
|
||||
// Should NOT throw
|
||||
animation.commitStyles();
|
||||
}, 'Treats display:contents as rendered');
|
||||
|
||||
test(t => {
|
||||
const container = createDiv(t);
|
||||
const div = createDiv(t);
|
||||
container.append(div);
|
||||
|
||||
const animation = div.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
div.style.display = 'contents';
|
||||
container.style.display = 'none';
|
||||
|
||||
assert_throws('InvalidStateError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, 'Treats display:contents in a display:none subtree as not rendered');
|
||||
|
||||
test(t => {
|
||||
const div = createDiv(t);
|
||||
const animation = div.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
div.remove();
|
||||
|
||||
assert_throws('InvalidStateError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, 'Throws if the target effect is disconnected');
|
||||
|
||||
test(t => {
|
||||
const pseudo = getPseudoElement(t, 'before');
|
||||
const animation = pseudo.animate(
|
||||
{ opacity: 0 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
|
||||
pseudo.element.remove();
|
||||
|
||||
assert_throws('NoModificationAllowedError', () => {
|
||||
animation.commitStyles();
|
||||
});
|
||||
}, 'Checks the pseudo element condition before the not rendered condition');
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Animation.persist</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-persist">
|
||||
<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';
|
||||
|
||||
async_test(t => {
|
||||
const div = createDiv(t);
|
||||
|
||||
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
|
||||
animA.onremove = t.step_func_done(() => {
|
||||
assert_equals(animA.replaceState, 'removed');
|
||||
animA.persist();
|
||||
assert_equals(animA.replaceState, 'persisted');
|
||||
});
|
||||
}, 'Allows an animation to be persisted after being removed');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
|
||||
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
|
||||
|
||||
animA.persist();
|
||||
|
||||
await animA.finished;
|
||||
|
||||
assert_equals(animA.replaceState, 'persisted');
|
||||
}, 'Allows an animation to be persisted before being removed');
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -11,8 +11,8 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
// Test that each property defined in the Animation interface does not produce
|
||||
// style change events.
|
||||
// Test that each property defined in the Animation interface behaves as
|
||||
// expected with regards to whether or not it produces style change events.
|
||||
//
|
||||
// There are two types of tests:
|
||||
//
|
||||
|
@ -29,8 +29,9 @@
|
|||
// (b) An object with the following format:
|
||||
//
|
||||
// {
|
||||
// setup: elem => { /* return Animation */ }
|
||||
// test: animation => { /* play |animation| */ }
|
||||
// setup: elem => { /* return Animation */ },
|
||||
// test: animation => { /* play |animation| */ },
|
||||
// shouldFlush: boolean /* optional, defaults to false */
|
||||
// }
|
||||
//
|
||||
// If the latter form is used, the setup function should return an Animation
|
||||
|
@ -56,15 +57,17 @@
|
|||
// animation, but simply needs to get/set the property under test.
|
||||
|
||||
const PlayAnimationTest = testFuncOrObj => {
|
||||
let test, setup;
|
||||
let test, setup, shouldFlush;
|
||||
|
||||
if (typeof testFuncOrObj === 'function') {
|
||||
test = testFuncOrObj;
|
||||
shouldFlush = false;
|
||||
} else {
|
||||
test = testFuncOrObj.test;
|
||||
if (typeof testFuncOrObj.setup === 'function') {
|
||||
setup = testFuncOrObj.setup;
|
||||
}
|
||||
shouldFlush = !!testFuncOrObj.shouldFlush;
|
||||
}
|
||||
|
||||
if (!setup) {
|
||||
|
@ -74,11 +77,11 @@ const PlayAnimationTest = testFuncOrObj => {
|
|||
);
|
||||
}
|
||||
|
||||
return { test, setup };
|
||||
return { test, setup, shouldFlush };
|
||||
};
|
||||
|
||||
const UsePropertyTest = testFuncOrObj => {
|
||||
const { setup, test } = PlayAnimationTest(testFuncOrObj);
|
||||
const { setup, test, shouldFlush } = PlayAnimationTest(testFuncOrObj);
|
||||
|
||||
let coveringAnimation;
|
||||
return {
|
||||
|
@ -93,6 +96,7 @@ const UsePropertyTest = testFuncOrObj => {
|
|||
test(animation);
|
||||
coveringAnimation.play();
|
||||
},
|
||||
shouldFlush,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -160,6 +164,7 @@ const tests = {
|
|||
}),
|
||||
playState: UsePropertyTest(animation => animation.playState),
|
||||
pending: UsePropertyTest(animation => animation.pending),
|
||||
replaceState: UsePropertyTest(animation => animation.replaceState),
|
||||
ready: UsePropertyTest(animation => animation.ready),
|
||||
finished: UsePropertyTest(animation => {
|
||||
// Get the finished Promise
|
||||
|
@ -172,6 +177,13 @@ const tests = {
|
|||
// Set the onfinish menber
|
||||
animation.onfinish = () => {};
|
||||
}),
|
||||
onremove: UsePropertyTest(animation => {
|
||||
// Get the onremove member
|
||||
animation.onremove;
|
||||
|
||||
// Set the onremove menber
|
||||
animation.onremove = () => {};
|
||||
}),
|
||||
oncancel: UsePropertyTest(animation => {
|
||||
// Get the oncancel member
|
||||
animation.oncancel;
|
||||
|
@ -225,6 +237,51 @@ const tests = {
|
|||
animation.reverse();
|
||||
},
|
||||
}),
|
||||
persist: PlayAnimationTest({
|
||||
setup: async elem => {
|
||||
// Create an animation whose replaceState is 'removed'.
|
||||
const animA = elem.animate(
|
||||
{ opacity: 1 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animB = elem.animate(
|
||||
{ opacity: 1 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
await animA.finished;
|
||||
animB.cancel();
|
||||
|
||||
return animA;
|
||||
},
|
||||
test: animation => {
|
||||
animation.persist();
|
||||
},
|
||||
}),
|
||||
commitStyles: PlayAnimationTest({
|
||||
setup: async elem => {
|
||||
// Create an animation whose replaceState is 'removed'.
|
||||
const animA = elem.animate(
|
||||
// It's important to use opacity of '1' here otherwise we'll create a
|
||||
// transition due to updating the specified style whereas the transition
|
||||
// we want to detect is the one from flushing due to calling
|
||||
// commitStyles.
|
||||
{ opacity: 1 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
const animB = elem.animate(
|
||||
{ opacity: 1 },
|
||||
{ duration: 1, fill: 'forwards' }
|
||||
);
|
||||
await animA.finished;
|
||||
animB.cancel();
|
||||
|
||||
return animA;
|
||||
},
|
||||
test: animation => {
|
||||
animation.commitStyles();
|
||||
},
|
||||
shouldFlush: true,
|
||||
}),
|
||||
get ['Animation constructor']() {
|
||||
let originalElem;
|
||||
return UsePropertyTest({
|
||||
|
@ -266,7 +323,7 @@ test(() => {
|
|||
for (const key of properties) {
|
||||
promise_test(async t => {
|
||||
assert_own_property(tests, key, `Should have a test for '${key}' property`);
|
||||
const { setup, test } = tests[key];
|
||||
const { setup, test, shouldFlush } = tests[key];
|
||||
|
||||
// Setup target element
|
||||
const div = createDiv(t);
|
||||
|
@ -276,7 +333,7 @@ for (const key of properties) {
|
|||
});
|
||||
|
||||
// Setup animation
|
||||
const animation = setup(div);
|
||||
const animation = await setup(div);
|
||||
|
||||
// Setup transition start point
|
||||
div.style.transition = 'opacity 100s';
|
||||
|
@ -291,17 +348,24 @@ for (const key of properties) {
|
|||
// If the test function produced a style change event it will have triggered
|
||||
// a transition.
|
||||
|
||||
// Wait for the animation to start and then for at least one animation
|
||||
// frame to give the transitionrun event a chance to be dispatched.
|
||||
// Wait for the animation to start and then for at least two animation
|
||||
// frames to give the transitionrun event a chance to be dispatched.
|
||||
assert_true(
|
||||
typeof animation.ready !== 'undefined',
|
||||
'Should have a valid animation to wait on'
|
||||
);
|
||||
await animation.ready;
|
||||
await waitForAnimationFrames(1);
|
||||
await waitForAnimationFrames(2);
|
||||
|
||||
assert_false(gotTransition, 'A transition should NOT have been triggered');
|
||||
}, `Animation.${key} does NOT trigger a style change event`);
|
||||
if (shouldFlush) {
|
||||
assert_true(gotTransition, 'A transition should have been triggered');
|
||||
} else {
|
||||
assert_false(
|
||||
gotTransition,
|
||||
'A transition should NOT have been triggered'
|
||||
);
|
||||
}
|
||||
}, `Animation.${key} produces expected style change events`);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue