Source code

Revision control

Copy as Markdown

Other Tools

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