Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer: How to listen to object events

Is it possible to listen to events dispatched by in-page objects? Let's say I have this code in the page I go to:

var event = new CustomEvent('status', { detail: 'ok' });
window.addEventListener('status', function(e) {
  console.log('status: ', e.detail);
});
setInterval(window.dispatchEvent, 1000, event);

I'd like to be able to listen to events dispatched by the window object (or any other JS object for that matter). How can I do this in Puppeteer?

like image 473
Vic Avatar asked Nov 04 '17 04:11

Vic


People also ask

How do you capture the event object in an event listener?

With the addEventListener() method you can specify the propagation type by using the "useCapture" parameter: addEventListener(event, function, useCapture); The default value is false, which will use the bubbling propagation, when the value is set to true, the event uses the capturing propagation.

How do you listen to all events on an element?

You can listen for events on any element in the DOM. JavaScript has an addEventListener() function that you can call on any element on a web page. The addEventListener() function is a method of the EventTarget interface. All objects that support events implement this interface.

What is Puppeteer js?

Puppeteer is a Node. js library developed by Google that lets you control headless Chrome through the DevTools Protocol. It is a tool for automating testing in your application using headless Chrome or Chromebit devices, without requiring any browser extensions like Selenium Webdriver or PhantomJS.


3 Answers

First, you have to expose a function that can be called from within the page. Second, you listen for the event and call the exposed function and pass on the event data.

// Expose a handler to the page
await page.exposeFunction('onCustomEvent', ({ type, detail }) => {
    console.log(`Event fired: ${type}, detail: ${detail}`);
});

// listen for events of type 'status' and
// pass 'type' and 'detail' attributes to our exposed function
await page.evaluateOnNewDocument(() => {
    window.addEventListener('status', ({ type, detail }) => {
        window.onCustomEvent({ type, detail });
    });
});

await page.goto(/* ... */);

If there is an event status fired in the page now (like the code you have given), you would see it being logged in the console of your Node.js application.

As posted in the comments, a more complex example can be found in the puppeteer examples.

like image 65
Thomas Dondorf Avatar answered Oct 13 '22 03:10

Thomas Dondorf


If you want to actually await the custom event, you can do it this way.

const page = await browser.newPage();

/**
  * Attach an event listener to page to capture a custom event on page load/navigation.
  * @param {string} type Event name.
  * @return {!Promise}
  */
function addListener(type) {
  return page.evaluateOnNewDocument(type => {
    // here we are in the browser context
    document.addEventListener(type, e => {
      window.onCustomEvent({ type, detail: e.detail });
    });
  }, type);
}

const evt = await new Promise(async resolve => {
  // Define a window.onCustomEvent function on the page.
  await page.exposeFunction('onCustomEvent', e => {
    // here we are in the node context
    resolve(e); // resolve the outer Promise here so we can await it outside
  });
  await addListener('status'); // setup listener for "status" custom event on page load
  await page.goto('http://example.com');  // N.B! Do not use { waitUntil: 'networkidle0' } as that may cause a race condition
});

console.log(`${evt.type} fired`, evt.detail || '');

Built upon the example at https://github.com/puppeteer/puppeteer/blob/main/examples/custom-event.js

like image 6
mflodin Avatar answered Oct 13 '22 04:10

mflodin


A simpler method await waitForEvent('event-name', 10)

/**
 * Wait for the browser to fire an event (including custom events)
 * @param {string} eventName - Event name
 * @param {integer} seconds - number of seconds to wait.
 * @returns {Promise} resolves when event fires or timeout is reached
 */
async function waitForEvent(eventName, seconds) {

    seconds = seconds || 30;

    // use race to implement a timeout
    return Promise.race([

        // add event listener and wait for event to fire before returning
        page.evaluate(function(eventName) {
            return new Promise(function(resolve, reject) {
                document.addEventListener(eventName, function(e) {
                    resolve(); // resolves when the event fires
                });
            });
        }, eventName),

        // if the event does not fire within n seconds, exit
        page.waitForTimeout(seconds * 1000)
    ]);
}

Source

like image 4
John Doherty Avatar answered Oct 13 '22 02:10

John Doherty