<!DOCTYPE html>
<link rel="help" href="https://www.w3.org/TR/css-scroll-snap-1/#snap-overflow" />
<title></title>
<meta name="assert" content="Test passes if snap is to the nearest edge">
<style>
  #scroller {
    scroll-snap-type: block mandatory;
    overflow-y: scroll;
    height:  400px;
    width: 400px
  }
  #space {
    width: 200px;
    height: 4000px;
  }
  .box {
    scroll-snap-align: start;
    background: #ccccff;
    margin-bottom:  10px;
    width: 300px;
    height: 500px;
    position:  relative;
  }
  .header {
    top: 0;
    position:  absolute;
  }
  .footer {
    bottom:  0;
    position:  absolute;
  }
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="../support/common.js"></script>

<div id="scroller" tabindex="0">
  <div id="target" class="box">
    <div class="header">Header 1</div>
    <div class="footer">Footer 1</div>
  </div>
  <div id="next" class="box">
    <div class="header">Header 2</div>
    <div class="footer">Footer 2</div>
  </div>
  <div id="space"></div>
</div>

<script>
// If all of the following conditions are met:
//   1. the snap area is larger than the snapport along the scroll axis, and
//   2. the distance between the previous and subsequent snap positions along
//      the axis is greater then the snapport size.
//
// Then any scroll position in which the snap area covers the snapport is
// valid snap position. This rule facilitates scrolling around in oversized
// elements.
//
// These test covers edge cases with snap-areas that overflow the snapport.
// It should be possible to scroll to the end of an oversized snap-area.

const scroller = document.getElementById("scroller");
const target = document.getElementById("target");
const next = document.getElementById("next");
const scrollTop = () => {
  return scroller.scrollTop;
};
const cleanup = () => {
  target.style.height = '500px';
};

promise_test(async t => {
  t.add_cleanup(cleanup);
  scroller.scrollTo(0, 0);
  assert_equals(scroller.scrollTop, 0, "verify test pre-condition");

  // Ensure we can freely scroll in an oversized element.
  const scrollPromise = waitForScrollEvent(scroller);
  await keyPress(scroller, "ArrowDown");
  await scrollPromise;
  await waitForAnimationEnd(scrollTop);
  assert_greater_than(scroller.scrollTop, 0,
                      'Arrowkey scroll moved scroll position');
  assert_less_than_equal(scroller.scrollTop, target.clientHeight,
                         'Scroll within snap-area overflow');

  // Resize the element so it is oversized by less than the line scroll amount.
  // The next keyboard-triggered scroll should stop at the end of the snap-area.
  // Otherwise it is not possible to scroll to the last line of the snap-area
  // via keyboard.
  const scrollAmount = scroller.scrollTop;
  target.style.height = `${scroller.clientHeight + scrollAmount - 1}px`;
  assert_equals(scroller.scrollTop, 0, "Verify snap on relayout");
  await keyPress(scroller, "ArrowDown");
  await waitForAnimationEnd(scrollTop);
  assert_equals(scroller.scrollTop,
                target.clientHeight - scroller.clientHeight,
                'End boundary of snap-area is valid snap target');

  // Must not get stuck at a snap position. Since already at the end of the
  // snap area, we should advance to the next.
  await keyPress(scroller, "ArrowDown");
  await waitForAnimationEnd(scrollTop);
  assert_equals(scroller.scrollTop,
                next.clientTop,
                'Advance to next snap-area');

}, "Keyboard scrolling with vertical snap-area overflow");

promise_test(async t => {
  scroller.scrollTo(0, 0);
  assert_equals(scroller.scrollTop, 0, "verify test pre-condition");

  // Ensure we can freely scroll in an oversized element.
  const scrollPromise = waitForScrollEvent(scroller);
  await new test_driver.Actions()
       .scroll(50, 50, 0, 50, {origin: scroller})
       .send();
  await scrollPromise;
  await waitForAnimationEnd(scrollTop);
  assert_equals(scroller.scrollTop, 50,
                      'Wheel-scroll moved scroll position');

  // Target position for wheel scroll overshoots the boundary of the snap-area.
  // Ensure that we stop at the boundary.
  const scrollAmount =
      target.clientHeight - scroller.clientHeight - scroller.scrollTop + 1;

  await new test_driver.Actions()
       .scroll(50, 50, 0, scrollAmount, {origin: scroller})
       .send();
  await waitForAnimationEnd(scrollTop);
  assert_equals(scroller.scrollTop, 100,
                      'End boundary of snap-area is valid snap target');

  // Must not get stuck at a snap position. Since already at the end of the
  // snap area, we should advance to the next.
  await new test_driver.Actions()
       .scroll(50, 50, 0, 50, {origin: scroller})
       .send();
  await waitForAnimationEnd(scrollTop);
  assert_equals(scroller.scrollTop,
                next.clientTop,
                'Advance to next snap-area');

}, "Mouse-wheel scrolling with vertical snap-area overflow");
</script>
