<!DOCTYPE html>
<title>Scroll timelines and animation attachment ranges</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range">
<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>
  #scroller {
    overflow-y: scroll;
    width: 200px;
    height: 200px;
    scroll-timeline: --t1;
  }
  .spacer {
    height: 1100px;
  }
  #target {
    height:  100px;
    width: 0;
    font-size: 10px;
    background-color: green;
  }
  @keyframes grow {
    to { width: 200px }
  }
  .anim-1 {
    animation: auto grow linear;
    animation-timeline: --t1;
    animation-range-start:  25%;
    animation-range-end:  75%;
  }
  .anim-2 {
    animation: auto grow linear;
    animation-timeline: --t1;
    animation-range-start: 40px;
    animation-range-end:  700px;
  }
  .anim-3 {
    animation: auto grow linear;
    animation-timeline: --t1;
    animation-range-start:  calc(30% + 40px);
    animation-range-end:  calc(70% - 20px);
  }
  .anim-4 {
    animation: auto grow linear;
    animation-timeline: --t1;
    animation-range-start:  5em;
    animation-range-end:  calc(100% - 5em);
  }

</style>
<div id=scroller>
  <div id=target></div>
  <div class=spacer></div>
</div>
<script>
  // Test via web-animation API.

  async function test_range_waapi(t, options) {
    const timeline = new ScrollTimeline({source: scroller, axis: 'block'});
    const anim =
        target.animate([{ width: "200px" }],
                       {
                         timeline: timeline,
                         rangeStart: options.rangeStart,
                         rangeEnd: options.rangeEnd
                       });
    t.add_cleanup(() => {
      anim.cancel();
    });
    await anim.ready;
    scroller.scrollTop = 600;
    await waitForNextFrame();

    const expectedProgress =
        (600 - options.startOffset) / (options.endOffset - options.startOffset);
    assert_approx_equals(anim.effect.getComputedTiming().progress,
                         expectedProgress, 0.001);
  }

  promise_test(async t => {
    await test_range_waapi(t, {
      rangeStart: "25%",
      rangeEnd: "75%",
      startOffset: 250,
      endOffset: 750
    });
  }, 'Scroll timeline with percentage range [JavaScript API]');

  promise_test(async t => {
    await test_range_waapi(t, {
      rangeStart: "40px",
      rangeEnd: "700px",
      startOffset: 40,
      endOffset: 700
    });
  }, 'Scroll timeline with px range [JavaScript API]');

  promise_test(async t => {
    await test_range_waapi(t, {
      rangeStart: "calc(30% + 40px)",
      rangeEnd: "calc(70% - 20px)",
      startOffset: 340,
      endOffset: 680
    });
  }, 'Scroll timeline with calculated range [JavaScript API]');

promise_test(async t => {
    t.add_cleanup(() => {
      target.style.fontSize = '';
    });
    await test_range_waapi(t, {
      rangeStart: "5em",
      rangeEnd: "calc(100% - 5em)",
      startOffset: 50,
      endOffset: 950
    });
    target.style.fontSize =  '20px';
    await waitForNextFrame();
    const anim = target.getAnimations()[0];
    const expectedProgress = (600 - 100) / (900 - 100);
    assert_approx_equals(anim.effect.getComputedTiming().progress,
                         expectedProgress, 0.001);
  }, 'Scroll timeline with EM range [JavaScript API]');

  // Test via CSS.
  async function test_range_css(t, options) {
    t.add_cleanup(() => {
      target.classList.remove(options.animation);
    });
    target.classList.add(options.animation);
    const anim = target.getAnimations()[0];
    await anim.ready;
    scroller.scrollTop = 600;
    await waitForNextFrame();

    const expectedProgress =
        (600 - options.startOffset) / (options.endOffset - options.startOffset);
    assert_approx_equals(anim.effect.getComputedTiming().progress,
                         expectedProgress, 0.001);
  }

  promise_test(async t => {
    await test_range_css(t, {
      animation: "anim-1",
      startOffset: 250,
      endOffset: 750
    });
  }, 'Scroll timeline with percentage range [CSS]');

  promise_test(async t => {
    await test_range_css(t, {
      animation: "anim-2",
      startOffset: 40,
      endOffset: 700
    });
  }, 'Scroll timeline with px range [CSS]');

  promise_test(async t => {
    await test_range_css(t, {
      animation: "anim-3",
      startOffset: 340,
      endOffset: 680
    });
  }, 'Scroll timeline with calculated range [CSS]');

promise_test(async t => {
    t.add_cleanup(() => {
      target.style.fontSize = '';
    });
    await test_range_css(t, {
      animation: "anim-4",
      startOffset: 50,
      endOffset: 950
    });
    target.style.fontSize =  '20px';
    await waitForNextFrame();
    const anim = target.getAnimations()[0];
    const expectedProgress = (600 - 100) / (900 - 100);
    assert_approx_equals(anim.effect.getComputedTiming().progress,
                         expectedProgress, 0.001);
  }, 'Scroll timeline with EM range [CSS]');
</script>
