summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-scroll-snap/support/common.js
blob: b380fe80c88ebf4146c8048c3ce6ae4ea33a0d4b (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
const KEY_CODE_MAP = {
  'ArrowLeft':  '\uE012',
  'ArrowUp':    '\uE013',
  'ArrowRight': '\uE014',
  'ArrowDown':  '\uE015',
  'PageUp':     '\uE00E',
  'PageDown':   '\uE00F',
  'End':        '\uE010',
  'Home':       '\uE011',
  'Space':      ' ',
};

// Send key event to the target element using test driver. Supports human
// friendly key names for common keyboard scroll operations e.g., arrow keys,
// page keys, etc.
async function keyPress(target, key) {
  let code = key;
  if (KEY_CODE_MAP.hasOwnProperty(key))
    code = KEY_CODE_MAP[key];

  // First move pointer on target and click to ensure it receives the key.
  return test_driver.send_keys(target, code);
}

// Use rAF to wait for the value of the getter function passed to not change for
// at least 15 frames or timeout after 1 second.
//
// Example usage:
//    await waitForAnimationEnd(() => scroller.scrollTop);
function waitForAnimationEnd(getValue) {
  const TIMEOUT = 1000; // milliseconds
  const MAX_UNCHANGED_FRAMES = 15;

  const start_time = performance.now();
  let last_changed_frame = 0;
  let last_value = getValue();

  return new Promise((resolve, reject) => {
    function tick(frames, time) {
    // We requestAnimationFrame either for TIMEOUT milliseconds or until
    // MAX_UNCHANGED_FRAMES with no change have been observed.
      if (time - start_time > TIMEOUT ||
          frames - last_changed_frame >= MAX_UNCHANGED_FRAMES) {
        resolve(time);
      } else {
        current_value = getValue();
        if (last_value != current_value) {
          last_changed_frame = frames;
          last_value = current_value;
        }
        requestAnimationFrame(tick.bind(this, frames + 1));
      }
    }
    tick(0, start_time);
  });
}


function waitForEvent(eventTarget, type) {
  return new Promise((resolve, reject) => {
    const eventListener = (evt) => {
      eventTarget.removeEventListener('scroll', eventListener);
      resolve(evt);
    };
    eventTarget.addEventListener(type, eventListener);
  });
}

function waitForScrollEvent(eventTarget) {
  return waitForEvent(eventTarget, 'scroll');
}

function waitForWheelEvent(eventTarget) {
  return waitForEvent(eventTarget, 'wheel');
}

// TODO: Update tests to replace call to this method with calls to
// waitForScrollTo, since this method does not test that scrolling has in fact
// stopped.
function waitForScrollEnd(eventTarget, getValue, targetValue) {
  return waitForScrollTo(eventTarget, getValue, targetValue);
}

function waitForScrollTo(eventTarget, getValue, targetValue) {
  return new Promise((resolve, reject) => {
    const scrollListener = (evt) => {
      if (getValue() == targetValue) {
        eventTarget.removeEventListener('scroll', scrollListener);
        resolve(evt);
      }
    };
    if (getValue() == targetValue)
      resolve();
    else
      eventTarget.addEventListener('scroll', scrollListener);
  });
}

function waitForNextFrame() {
  return new Promise(resolve => {
    const start = performance.now();
    requestAnimationFrame(frameTime => {
      if (frameTime < start) {
        requestAnimationFrame(resolve);
      } else {
        resolve();
      }
    });
  });
}