chromium/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/arrow-key-scroll-snaps-visual-viewport.html

<!DOCTYPE html>
<style>
body, html {
  width: 100%;
  height: 100%;
  margin: 0px;
}

#root-scroller::-webkit-scrollbar {
  width: 0px;
  height: 0px;
}

#root-scroller {
  width: 100%;
  height: 100%;
  overflow: scroll;
  position: absolute;
  left: 0;
  top: 0;
  background-color: red;
  scroll-snap-type: y mandatory;
}

.spacer {
  width: 100%;
  height: 100%;
}

#initial-snap-area {
  width: 200px;
  height: 50%;
  background-color: blue;
  scroll-snap-align: start;
}

#snap-area {
  width: 200px;
  height: 50%;
  background-color: blue;
  scroll-snap-align: start;
}
</style>

<script src="../../../resources/gesture-util.js"></script>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>

<div id="root-scroller">
  <div id="initial-snap-area"></div>
  <div class="spacer" style="background-color: PaleGreen"></div>
  <div class="spacer" style="background-color: PaleGreen"></div>
  <div id="snap-area"></div>
</div>

<script>
if (window.internals) {
  internals.runtimeFlags.implicitRootScrollerEnabled = true;
  // Jump directly to the snap position. Otherwise, we need to wait until X ms
  // goes by without a change to the scroll position, which leads to either slow
  // running tests or flakes when the test machines get bogged down.
  internals.settings.setScrollAnimatorEnabled(false);
}

const root_scroller = document.getElementById("root-scroller");
const snap_area = document.getElementById("snap-area");
const initial_area = document.getElementById("initial-snap-area");

async function arrowDown() {
  // Click on the middle of the viewport.
  const initial_scroll_position = {
    x: visualViewport.width / 2,
    y: visualViewport.height / 2
  }
  await mouseClickOn(initial_scroll_position.x, initial_scroll_position.y);
  await new Promise((resolve, reject) => {
    if (window.eventSender) {
      eventSender.keyDown("ArrowDown");
      resolve();
    }
    else {
      reject('This test requires window.eventSender');
    }
  });
}

// Tests that the visual viewport is affected when scrolling the global root
// scroller with an arrow key. The snap area is located at the bottom of the layout
// viewport, so the layout viewport cannot align with the snap area.
// However, when it becomes the global root scroller we should be scrolling the
// visual viewport too to align with the snap area.
promise_test(async function () {
  const scale_factor = 2;
  internals.setPageScaleFactor(scale_factor);
  internals.setVisualViewportOffset(0, 0);

  assert_equals(visualViewport.scale, 2);
  assert_equals(visualViewport.offsetTop, 0);

  await waitForCompositorCommit();

  assert_equals(window.internals.effectiveRootScroller(document),
    root_scroller,
    "#root-scroller must be the effective root scroller");
  // Should be snapped to the closer snap area (0,0) on the initial layout.
  assert_equals(visualViewport.offsetTop + root_scroller.scrollTop,
      initial_area.offsetTop);

  const scrollWatcher = waitForScrollEvent(root_scroller);
  await arrowDown();
  await scrollWatcher;

  // The offset of the visual viewport and the layout viewport combined should
  // be at the snap point.
  assert_equals(visualViewport.offsetTop + root_scroller.scrollTop,
    snap_area.offsetTop, "Visual viewport offset combined with the scroller's \
scroll offset should add to the snap area's position.");

}, "Snapping the root scroller after arrow key scrolling should affect the \
visual viewport offset.");
</script>