1async function waitForScrollendEvent(test, target, timeoutMs = 500) { 2 return new Promise((resolve, reject) => { 3 const timeoutCallback = test.step_timeout(() => { 4 reject(`No Scrollend event received for target ${target}`); 5 }, timeoutMs); 6 target.addEventListener('scrollend', (evt) => { 7 clearTimeout(timeoutCallback); 8 resolve(evt); 9 }, { once: true }); 10 }); 11} 12 13const MAX_FRAME = 700; 14const MAX_UNCHANGED_FRAMES = 20; 15 16// Returns a promise that resolves when the given condition is met or rejects 17// after MAX_FRAME animation frames. 18// TODO(crbug.com/1400399): deprecate. We should not use frame based waits in 19// WPT as frame rates may vary greatly in different testing environments. 20function waitFor(condition, error_message = 'Reaches the maximum frames.') { 21 return new Promise((resolve, reject) => { 22 function tick(frames) { 23 // We requestAnimationFrame either for MAX_FRAM frames or until condition 24 // is met. 25 if (frames >= MAX_FRAME) 26 reject(error_message); 27 else if (condition()) 28 resolve(); 29 else 30 requestAnimationFrame(tick.bind(this, frames + 1)); 31 } 32 tick(0); 33 }); 34} 35 36// TODO(crbug.com/1400446): Test driver should defer sending events until the 37// browser is ready. Also the term compositor-commit is misleading as not all 38// user-agents use a compositor process. 39function waitForCompositorCommit() { 40 return new Promise((resolve) => { 41 // rAF twice. 42 window.requestAnimationFrame(() => { 43 window.requestAnimationFrame(resolve); 44 }); 45 }); 46} 47 48// TODO(crbug.com/1400399): Deprecate as frame rates may vary greatly in 49// different test environments. 50function waitForAnimationEnd(getValue) { 51 var last_changed_frame = 0; 52 var last_position = getValue(); 53 return new Promise((resolve, reject) => { 54 function tick(frames) { 55 // We requestAnimationFrame either for MAX_FRAME or until 56 // MAX_UNCHANGED_FRAMES with no change have been observed. 57 if (frames >= MAX_FRAME || frames - last_changed_frame > MAX_UNCHANGED_FRAMES) { 58 resolve(); 59 } else { 60 current_value = getValue(); 61 if (last_position != current_value) { 62 last_changed_frame = frames; 63 last_position = current_value; 64 } 65 requestAnimationFrame(tick.bind(this, frames + 1)); 66 } 67 } 68 tick(0); 69 }) 70} 71 72// Scrolls in target according to move_path with pauses in between 73function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_in_ms = 100) { 74 const test_driver_actions = new test_driver.Actions() 75 .addPointer("pointer1", "touch") 76 .pointerMove(0, 0, {origin: target}) 77 .pointerDown(); 78 79 const substeps = 5; 80 let x = 0; 81 let y = 0; 82 // Do each move in 5 steps 83 for(let move of move_path) { 84 let step_x = (move.x - x) / substeps; 85 let step_y = (move.y - y) / substeps; 86 for(let step = 0; step < substeps; step++) { 87 x += step_x; 88 y += step_y; 89 test_driver_actions.pointerMove(x, y, {origin: target}); 90 } 91 test_driver_actions.pause(pause_time_in_ms); 92 } 93 94 return test_driver_actions.pointerUp().send(); 95} 96 97function touchScrollInTarget(pixels_to_scroll, target, direction, pause_time_in_ms = 100) { 98 var x_delta = 0; 99 var y_delta = 0; 100 const num_movs = 5; 101 if (direction == "down") { 102 y_delta = -1 * pixels_to_scroll / num_movs; 103 } else if (direction == "up") { 104 y_delta = pixels_to_scroll / num_movs; 105 } else if (direction == "right") { 106 x_delta = -1 * pixels_to_scroll / num_movs; 107 } else if (direction == "left") { 108 x_delta = pixels_to_scroll / num_movs; 109 } else { 110 throw("scroll direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'"); 111 } 112 return new test_driver.Actions() 113 .addPointer("pointer1", "touch") 114 .pointerMove(0, 0, {origin: target}) 115 .pointerDown() 116 .pointerMove(x_delta, y_delta, {origin: target}) 117 .pointerMove(2 * x_delta, 2 * y_delta, {origin: target}) 118 .pointerMove(3 * x_delta, 3 * y_delta, {origin: target}) 119 .pointerMove(4 * x_delta, 4 * y_delta, {origin: target}) 120 .pointerMove(5 * x_delta, 5 * y_delta, {origin: target}) 121 .pause(pause_time_in_ms) 122 .pointerUp() 123 .send(); 124} 125 126// Trigger fling by doing pointerUp right after pointerMoves. 127function touchFlingInTarget(pixels_to_scroll, target, direction) { 128 touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */); 129} 130 131function mouseActionsInTarget(target, origin, delta, pause_time_in_ms = 100) { 132 return new test_driver.Actions() 133 .addPointer("pointer1", "mouse") 134 .pointerMove(origin.x, origin.y, { origin: target }) 135 .pointerDown() 136 .pointerMove(origin.x + delta.x, origin.y + delta.y, { origin: target }) 137 .pointerMove(origin.x + delta.x * 2, origin.y + delta.y * 2, { origin: target }) 138 .pause(pause_time_in_ms) 139 .pointerUp() 140 .send(); 141} 142 143// Returns a promise that resolves when the given condition holds for 10 144// animation frames or rejects if the condition changes to false within 10 145// animation frames. 146// TODO(crbug.com/1400399): Deprecate as frame rates may very greatly in 147// different test environments. 148function conditionHolds(condition, error_message = 'Condition is not true anymore.') { 149 const MAX_FRAME = 10; 150 return new Promise((resolve, reject) => { 151 function tick(frames) { 152 // We requestAnimationFrame either for 10 frames or until condition is 153 // violated. 154 if (frames >= MAX_FRAME) 155 resolve(); 156 else if (!condition()) 157 reject(error_message); 158 else 159 requestAnimationFrame(tick.bind(this, frames + 1)); 160 } 161 tick(0); 162 }); 163} 164