Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer close javascript alert box

I'm trying to click on a page button on this website but when I enter the site an alert box shows up and I don't know how to close it.

I just started experimenting with Puppeteer, this is the code I'm using this simple code right now:

const ptr = require('puppeteer');

ptr.launch().then(async browser => {

const page = await browser.newPage();

await page.setViewport({ width: 1280, height: 800 });
await page.goto('https://portaleperiti.grupporealemutua.it/PPVET/VetrinaWebPortalePeriti/');

//This is the alert button selector 
await page.click("#BoxAlertBtnOk");

//This is the button on the page i want to click on 
await page.click("input[value='Perito RE / G.F.']");

await page.screenshot({
    path: 'screenshot.png',
    fullPage: true
});

await browser.close();
});

This is the error I get: UnhandledPromiseRejectionWarning: Error: Node is either not visible or not an HTMLElement at ElementHandle._clickablePoint

Any help would be really appreciated, thanks!

like image 492
astronomy-domine Avatar asked Mar 20 '19 10:03

astronomy-domine


2 Answers

There are few things going on that page,

  • The alert box only loads after page is loaded (It has a onload property on body tag). So you should wait until network is idle.
  • Clicking those "Perito" buttons creates a new window/tab due to the window.open() code put into onclick handler.
  • The new tab redirects multiple times and shows a login page if the user is not logged in already.

Solution:

1. Make sure to load the page properly.

Just add { waitUntil: "networkidle0" } to .goto or .waitForNavigation.

await page.goto(
    "https://portaleperiti.grupporealemutua.it/PPVET/VetrinaWebPortalePeriti/",
    { waitUntil: "networkidle0" }
    // <-- Make sure the whole page is completely loaded
);

2. Wait for the element before clicking

Already suggested on other answers, wait for the element using waitFor.

// wait and click the alert button
await page.waitFor("#BoxAlertBtnOk");
await page.click("#BoxAlertBtnOk");

3. Optional, add few seconds before taking screenshot after clicking the button.

// optional, add few seconds before taking this screenshot
// just to make sure it works even on slow machine
await page.waitFor(2000);
await page.screenshot({
  path: "screenshot_before.png",
  fullPage: true
});

4. Use the page.evaluate and document.querySelector to get element

page.click will not handle all kind of clicks. Sometimes there are different events bound to some elements and you have to treat that separately.

// we can click using querySelector and the native
// just page.click does not trigger the onclick handler on this page
await page.evaluate(() =>
    document.querySelector("input[value='Perito RE / G.F.']").click()
);

5. Treat the new tab separately

Together with browser.once('targetcreated'), new Promise, and browser.pages() you can catch the newly created tab and work on it.

Note: Read final code at end of the answer before using this.

// this is the final page after clicking the input on previous page
// https://italy.grupporealemutua.it/FIM/sps/IDPRMA/saml20/login
function newTabCatcher(browser) {
  // we resolve this promise after doing everything we need to do on this page
  // or in error
  return new Promise((resolve, reject) => {
    // set the listener before clicking the button to have proper interaction
    // we listen for only one new tab
    browser.once("targetcreated", async function() {
      console.log("New Tab Created");
      try {
        // get the newly created window
        const tabs = await browser.pages();
        const lastTab = tabs[tabs.length - 1];

        // Wait for navigation to finish as well as specific login form
        await Promise.all([
          lastTab.waitForNavigation({ waitUntil: "networkidle0" }),
          lastTab.waitFor("#div_login")
        ]);

        // browser will switch to this tab just when it takes the screenshot
        await lastTab.screenshot({
          path: "screenshot_newtab.png",
          fullPage: true
        });

        resolve(true);
      } catch (error) {
        reject(error);
      }
    });
  });
}

Final Code:

Just for clarity, here is how I used all code snippets specified above.

const ptr = require("puppeteer");

ptr.launch({ headless: false }).then(async browser => {
  const page = await browser.newPage();

  await page.setViewport({ width: 1280, height: 800 });
  await page.goto(
    "https://portaleperiti.grupporealemutua.it/PPVET/VetrinaWebPortalePeriti/",
    { waitUntil: "networkidle0" }
    // <-- Make sure the whole page is completely loaded
  );

  // wait and click the alert button
  await page.waitFor("#BoxAlertBtnOk");
  await page.click("#BoxAlertBtnOk");

  // optional, add few seconds before taking this screenshot
  // just to make sure it works even on slow machine
  await page.waitFor(2000);
  await page.screenshot({
    path: "screenshot_before.png",
    fullPage: true
  });

  // we can click using querySelector and the native
  // just page.click does not trigger the onclick handler on this page
  await page.evaluate(() =>
    document.querySelector("input[value='Perito RE / G.F.']").click()
  );

  // here we go and process the new tab
  // aka get screenshot, fill form etc
  await newTabCatcher(browser);

  // rest of your code
  // ... 

  await browser.close();
});

Result:

It worked flawlessly!

Note:

Notice how I used new Promise and async await together. This might not be the best practice, but now you have a lead of what to look for when creating a scraper for some old websites.

like image 128
Md. Abu Taher Avatar answered Oct 06 '22 00:10

Md. Abu Taher


If it's relevant to anyone else who facing dialog boxes, the following code solved it for me:

    this.page.on('dialog', async dialog => {
        await dialog.dismiss();
    });
like image 23
Or Assayag Avatar answered Oct 06 '22 01:10

Or Assayag