summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html')
-rw-r--r--testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html271
1 files changed, 271 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html b/testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html
new file mode 100644
index 0000000000..744639f663
--- /dev/null
+++ b/testing/web-platform/tests/scroll-animations/css/scroll-timeline-dynamic.tentative.html
@@ -0,0 +1,271 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
+<style>
+ main {
+ scroll-timeline-attachment: defer;
+ scroll-timeline-name: timeline;
+ }
+
+ .scroller {
+ scroll-timeline-attachment: ancestor;
+ }
+
+ main > div {
+ overflow: hidden;
+ width: 100px;
+ height: 100px;
+ }
+ main > div > div {
+ height: 200px;
+ }
+ @keyframes expand {
+ from { width: 100px; }
+ to { width: 200px; }
+ }
+ #element {
+ width: 0px;
+ height: 20px;
+ animation-name: expand;
+ /* Some of the tests in this file assume animations attached to the
+ DocumentTimeline are "stopped" without actually being paused.
+ Using 600s + steps(10, end) achieves this for one minute.*/
+ animation-duration: 600s;
+ animation-timing-function: steps(10, end);
+ }
+</style>
+<main id=main>
+ <div id=scroller1 class=scroller>
+ <div></div>
+ </div>
+ <div id=scroller2 class=scroller>
+ <div></div>
+ </div>
+ <div id=container></div>
+</main>
+<script>
+ // Force layout of scrollers.
+ scroller1.offsetTop;
+ scroller2.offsetTop;
+
+ // Note the steps(10, end) timing function and height:100px. (10px scroll
+ // resolution).
+ scroller1.scrollTop = 20;
+ scroller2.scrollTop = 40;
+
+ function insertElement() {
+ let element = document.createElement('div');
+ element.id = 'element';
+ container.append(element);
+ return element;
+ }
+
+ // Runs a test with dynamically added/removed elements or CSS rules.
+ // Each test is instantiated twice: once for the initial style resolve where
+ // the DOM change was effectuated, and once after scrolling.
+ function dynamic_rule_test(func, description) {
+ // assert_width is an async function which verifies that the computed value
+ // of 'width' is as expected.
+ const instantiate = (assert_width, description) => {
+ promise_test(async (t) => {
+ try {
+ await func(t, assert_width);
+ } finally {
+ while (container.firstChild)
+ container.firstChild.remove();
+ main.style = '';
+ scroller1.style = '';
+ scroller2.style = '';
+ }
+ }, description);
+ };
+
+ // Verify that the computed style is as expected after a full frame update
+ // following the rule change took place.
+ instantiate(async (element, expected) => {
+ await waitForCSSScrollTimelineStyle();
+ assert_equals(getComputedStyle(element).width, expected);
+ }, description + ' [immediate]');
+
+ // Verify that the computed style after scrolling a bit.
+ instantiate(async (element, expected) => {
+ scroller1.scrollTop = scroller1.scrollTop + 10;
+ scroller2.scrollTop = scroller2.scrollTop + 10;
+ await waitForNextFrame();
+ scroller1.scrollTop = scroller1.scrollTop - 10;
+ scroller2.scrollTop = scroller2.scrollTop - 10;
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(element).width, expected);
+ }, description + ' [scroll]');
+ }
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ // This element initially has a DocumentTimeline.
+ await assert_width(element, '100px');
+
+ // Switch to scroll timeline.
+ scroller1.style.scrollTimelineName = 'timeline';
+ element.style.animationTimeline = 'timeline';
+ await assert_width(element, '120px');
+
+ // Switching from ScrollTimeline -> DocumentTimeline should preserve
+ // current time.
+ scroller1.style = '';
+ element.style = '';
+ await assert_width(element, '120px');
+ }, 'Switching between document and scroll timelines');
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ // Flush style and create the animation with play pending.
+ getComputedStyle(element).animation;
+
+ let anim = element.getAnimations()[0];
+ assert_true(anim.pending, "The animation is in play pending");
+
+ // Switch to scroll timeline for a pending animation.
+ scroller1.style.scrollTimelineName = 'timeline';
+ element.style.animationTimeline = 'timeline';
+
+ await anim.ready;
+ assert_false(anim.pending, "The animation is not pending");
+
+ await assert_width(element, '120px');
+ }, 'Switching pending animation from document to scroll timelines');
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ // Note: #scroller1 is at 20%, and #scroller2 is at 40%.
+ scroller1.style.scrollTimelineName = 'timeline1';
+ scroller2.style.scrollTimelineName = 'timeline2';
+ main.style.scrollTimelineName = "timeline1, timeline2";
+
+ await assert_width(element, '100px');
+
+ element.style.animationTimeline = 'timeline1';
+ await assert_width(element, '120px');
+
+ element.style.animationTimeline = 'timeline2';
+ await assert_width(element, '140px');
+
+ element.style.animationTimeline = 'timeline1';
+ await assert_width(element, '120px');
+
+ // Switching from ScrollTimeline -> DocumentTimeline should preserve
+ // current time.
+ element.style.animationTimeline = '';
+ await assert_width(element, '120px');
+
+ }, 'Changing computed value of animation-timeline changes effective timeline');
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ scroller1.style.scrollTimelineName = 'timeline';
+
+ // DocumentTimeline applies by default.
+ await assert_width(element, '100px');
+
+ // Wait for the animation to be ready so that we a start time and no hold
+ // time.
+ await element.getAnimations()[0].ready;
+
+ // DocumentTimeline -> none
+ element.style.animationTimeline = 'none';
+ await assert_width(element, '0px');
+
+ // none -> DocumentTimeline
+ element.style.animationTimeline = '';
+ await assert_width(element, '100px');
+
+ // DocumentTimeline -> ScrollTimeline
+ element.style.animationTimeline = 'timeline';
+ await assert_width(element, '120px');
+
+ // ScrollTimeline -> none
+ element.style.animationTimeline = 'none';
+ await assert_width(element, '120px');
+
+ // none -> ScrollTimeline
+ element.style.animationTimeline = 'timeline';
+ await assert_width(element, '120px');
+ }, 'Changing to/from animation-timeline:none');
+
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ element.style.animationDirection = 'reverse';
+ element.style.animationTimeline = 'timeline';
+
+ // Inactive animation-timeline. Animation is inactive.
+ await assert_width(element, '0px');
+
+ // Note: #scroller1 is at 20%.
+ scroller1.style.scrollTimelineName = 'timeline';
+ await assert_width(element, '180px');
+
+ // Note: #scroller2 is at 40%.
+ scroller1.style.scrollTimelineName = '';
+ scroller2.style.scrollTimelineName = 'timeline';
+ await assert_width(element, '160px');
+
+ element.style.animationDirection = '';
+ await assert_width(element, '140px');
+ }, 'Reverse animation direction');
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+ element.style.animationTimeline = 'timeline';
+
+ // Inactive animation-timeline. Animation effect is inactive.
+ await assert_width(element, '0px');
+
+ // Note: #scroller1 is at 20%.
+ scroller1.style.scrollTimelineName = 'timeline';
+ await assert_width(element, '120px');
+
+ element.style.animationPlayState = 'paused';
+
+ // We should still be at the same position after pausing.
+ await assert_width(element, '120px');
+
+ // Note: #scroller2 is at 40%.
+ scroller1.style.scrollTimelineName = '';
+ scroller2.style.scrollTimelineName = 'timeline';
+
+ // Should be at the same position until we unpause.
+ await assert_width(element, '120px');
+
+ // Unpausing should synchronize to the scroll position.
+ element.style.animationPlayState = '';
+ await assert_width(element, '140px');
+ }, 'Change to timeline attachment while paused');
+
+ dynamic_rule_test(async (t, assert_width) => {
+ let element = insertElement();
+
+ // Note: #scroller1 is at 20%.
+ scroller1.style.scrollTimelineName = 'timeline';
+
+ await assert_width(element, '100px');
+
+ element.style.animationTimeline = 'timeline';
+ element.style.animationPlayState = 'paused';
+
+ // Pausing should happen before the timeline is modified. (Tentative).
+ // https://github.com/w3c/csswg-drafts/issues/5653
+ await assert_width(element, '100px');
+
+ element.style.animationPlayState = 'running';
+ await assert_width(element, '120px');
+ }, 'Switching timelines and pausing at the same time');
+</script>