summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/resources/common.js
blob: 6ceec9118c5a24e4a1e999616ea7288256a846ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Utility functions for scroll snap tests which verify User-Agents' snap point
// selection logic when multiple snap targets are aligned.
// It depends on methods in /resources/testdriver-actions.js and
// /dom/event/scrolling/scroll_support.js so html files using these functions
// should include those files as <script>s.

// This function should be used by scroll snap WPTs wanting to test snap target
// selection when scrolling to multiple aligned targets.
// It assumes scroll-snap-align: start alignment and tries to align to the list
// of snap targets provided, |elements|, which are all expected to be at the
// same offset.
async function scrollToAlignedElementsInAxis(scroller, elements, axis) {
  let target_offset_y = null;
  let target_offset_x = null;
  if (axis == "y") {
    for (const e of elements) {
      if (target_offset_y) {
        assert_equals(e.offsetTop, target_offset_y,
          `${e.id} is at y offset ${target_offset_y}`);
      } else {
        target_offset_y = e.offsetTop;
      }
    }
    assert_equals();
  } else {
    for (const e of elements) {
      if (target_offset_x) {
        assert_equals(e.offsetLeft, target_offset_x,
          `${e.id} is at x offset ${target_offset_x}`);
      } else {
        target_offset_x = e.offsetLeft;
      }
    }
  }
  assert_not_equals(target_offset_x || target_offset_y, null);

  const scrollend_promise = waitForScrollendEventNoTimeout(scroller);
  await new test_driver.Actions().scroll(0, 0,
    (target_offset_x || 0) - scroller.scrollLeft,
    (target_offset_y || 0) - scroller.scrollTop,
    { origin: scroller })
    .send();
  await scrollend_promise;
  if (axis == "y") {
    assert_equals(scroller.scrollTop, target_offset_y, "vertical scroll done");
  } else {
    assert_equals(scroller.scrollLeft,target_offset_x, "horizontal scroll done");
  }
}

// This function verifies the snap target that a scroller picked by triggerring
// a layout change and observing which target is followed. Tests using this
// method should ensure that there is at least 100px of room to scroll in the
// desired axis.
// It assumes scroll-snap-align: start alignment.
function verifySelectedSnapTarget(scroller, expected_snap_target, axis) {
  // Save initial style.
  const initial_left = getComputedStyle(expected_snap_target).left;
  const initial_top = getComputedStyle(expected_snap_target).top;
  if (axis == "y") {
    // Move the expected snap target along the y axis.
    const initial_scroll_top = scroller.scrollTop;
    const target_top = expected_snap_target.offsetTop + 100;
    expected_snap_target.style.top = `${target_top}px`;
    assert_equals(scroller.scrollTop, expected_snap_target.offsetTop,
      `scroller followed ${expected_snap_target.id} after layout change`);
    assert_not_equals(scroller.scrollTop, initial_scroll_top,
      "scroller actually scrolled in y axis");
  } else {
    // Move the expected snap target along the y axis.
    const initial_scroll_left = scroller.scrollLeft;
    const target_left = expected_snap_target.offsetLeft + 100;
    expected_snap_target.style.left = `${target_left}px`;
    assert_equals(scroller.scrollLeft, expected_snap_target.offsetLeft,
      `scroller followed ${expected_snap_target.id} after layout change`);
    assert_not_equals(scroller.scrollLeft, initial_scroll_left,
      "scroller actually scrolled in x axis");
  }
  // Undo style changes.
  expected_snap_target.style.top = initial_top;
  expected_snap_target.style.left = initial_left;
}

// This is a utility function for tests which verify that the correct element
// is snapped to when snapping at the end of a scroll.
async function runScrollSnapSelectionVerificationTest(t, scroller, aligned_elements,
                                                expected_target, axis) {
  // Save initial scroll offset.
  const initial_scroll_left = scroller.scrollLeft;
  const initial_scroll_top = scroller.scrollTop;
  await scrollToAlignedElementsInAxis(scroller, aligned_elements, axis);
  verifySelectedSnapTarget(scroller, expected_target, axis);
  // Restore initial scroll offsets.
  const scrollend_promise = new Promise((resolve) => {
    scroller.addEventListener("scrollend", resolve);
  });
  scroller.scrollTo(initial_scroll_left, initial_scroll_top);
  await scrollend_promise;
}

// This is a utility function for tests verifying that a layout shift does not
// cause a scroller to change its selected snap target.
// It assumes the element to be aligned have scroll-snap-align: start.
// It tries to align the list of snap targets provided, |elements| with the
// current snap target.
function shiftLayoutToAlignElements(elements, target, axis) {
  for (let element of elements) {
    if (axis == "y") {
      element.style.top = `${target.offsetTop}px`;
    } else {
      element.style.left = `${target.offsetLeft}px`;
    }
  }
}

// This is a utility function for tests verifying that a layout shift does not
// cause a scroller to change its selected snap target.
// It assumes scroll-snap-align: start alignment.
async function runLayoutSnapSeletionVerificationTest(t, scroller, elements_to_align,
  expected_target, axis) {
  // Save initial scroll offsets and position.
  const initial_scroll_left = scroller.scrollLeft;
  const initial_scroll_top = scroller.scrollTop;
  let initial_tops = [];
  for (const element of elements_to_align) {
    initial_tops.push(getComputedStyle(element).top);
  }

  shiftLayoutToAlignElements(elements_to_align, expected_target, axis);
  verifySelectedSnapTarget(scroller, expected_target, axis);

  // Restore initial scroll offset and position states.
  let num_elements = initial_tops.length;
  for (let i = 0; i < num_elements; i++) {
    elements_to_align[i].style.top = initial_tops[i];
  }
  // Restore initial scroll offsets.
  const scrollend_promise = new Promise((resolve) => {
    scroller.addEventListener("scrollend", resolve);
  });
  scroller.scrollTo(initial_scroll_left, initial_scroll_top);
  await scrollend_promise;
}

function focusAndAssert(element, preventScroll=false) {
  element.focus({preventScroll: preventScroll});
  assert_equals(document.activeElement, element);
}