Waiting for an event in layout test...

# Ryosuke Niwa (17 hours ago)

I’m gonna give you a game changing function:

function listenForEventOnce(target, name, timeout) { return new Promise((resolve, reject) => { const timer = timeout ? setTimeout(reject, timeout) : null; target.addEventListener(name, () => { if (timer) clearTimeout(timer); resolve(); }, {once: true}); }); }

You can then write a test like this: await listenForEventOnce(document.body, 'load'); // do stuff after load event.

await listenForEventOnce(document.querySelector('input'), 'focus'); await listenForEventOnce(visualViewport, 'scroll', 5000); // After the input element is focused, then the visual viewport scrolled or 5 seconds has passed.

  • R. Niwa
Contact us to advertise here
# Jer Noble (2 hours ago)

On May 30, 2019, at 11:01 PM, Ryosuke Niwa <rniwa at webkit.org> wrote:

I’m gonna give you a game changing function:

function listenForEventOnce(target, name, timeout) { return new Promise((resolve, reject) => { const timer = timeout ? setTimeout(reject, timeout) : null; target.addEventListener(name, () => { if (timer) clearTimeout(timer); resolve(); }, {once: true}); }); }

You can then write a test like this: await listenForEventOnce(document.body, 'load'); // do stuff after load event.

await listenForEventOnce(document.querySelector('input'), 'focus'); await listenForEventOnce(visualViewport, 'scroll', 5000); // After the input element is focused, then the visual viewport scrolled or 5 seconds has passed.

Ryosuke++.

Just FYI, if you’re writing LayoutTests, we’ve got something very similar in LayoutTests/media/video-test.js:

function waitFor(element, type) { return new Promise(resolve => { element.addEventListener(type, event => { consoleWrite(EVENT(${event.type})); resolve(event); }, { once: true }); }); }

And:

function sleepFor(duration) { return new Promise(resolve => { setTimeout(resolve, duration); }); }

And also:

function shouldReject(promise) { return new Promise((resolve, reject) => { promise.then(result => { logResult(Failed, 'Promise resolved incorrectly'); reject(result); }).catch((error) => { logResult(Success, 'Promise rejected correctly'); resolve(error); }); }); }

So you could also do:

await Promise.race(waitFor(document.body, ‘load’), shouldReject(sleepFor(5000)));

Although we’d probably want a new function, “rejectIn(duration)”, and then it’d be:

await shouldResolve(Promise.race(waitFor(document.body, ‘load’), rejectIn(5000)));

But all that said, I agree that wrapping events in Promises makes it very easy to write readable, single async function test cases. A+++, would write test again.

Want more features?

Request early access to our private beta of readable email premium.