summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-scroll-snap/support/common.js
blob: c7800b95f1468e072579b5b9e7834487be6b9688 (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
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 => {
    eventTarget.addEventListener(type, resolve, { once: true });
  });
}

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

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

function waitForScrollStop(eventTarget) {
  const TIMEOUT_IN_MS = 200;

  return new Promise(resolve => {
    let lastScrollEventTime = performance.now();

    const scrollListener = () => {
      lastScrollEventTime = performance.now();
    };
    eventTarget.addEventListener('scroll', scrollListener);

    const tick = () => {
      if (performance.now() - lastScrollEventTime > TIMEOUT_IN_MS) {
        eventTarget.removeEventListener('scroll', scrollListener);
        resolve();
        return;
      }
      requestAnimationFrame(tick); // wait another frame
    }
    requestAnimationFrame(tick);
  });
}

function waitForScrollEnd(eventTarget) {
  if (window.onscrollend !== undefined) {
    return waitForScrollendEventNoTimeout(eventTarget);
  }
  return waitForScrollEvent(eventTarget).then(() => {
    return waitForScrollStop(eventTarget);
  });
}

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();
      }
    });
  });
}