6

I am trying to set up an if/else statement in puppeteer to click on a button if it is present, else click on another button. I am doing something like this:

if (document.querySelector('#buttonToClick') !== null) {
    await page.waitForSelector('#buttonToClick');
    await page.click('#buttonToClick');
  } 

else { 
// first click #otherButton and then click #buttonToClick
    await page.waitForSelector('#otherButton');
    await page.click('#otherButton');

    await page.waitForSelector('#buttonToClick');
    await page.click('#buttonToClick');
  }

For some reason I keep falling into the else block even when I go to my Chrome console and do a document.querySelector('#buttonToClick') !==null on the desired page and it is showing as true

UPDATE: The following code seems to be working for me, but I am not sure why

await page.waitFor(3000);

  if ((await page.$('#buttonToClick')) !== null) {
    await page.click('#buttonToClick');
  } else {
    await page.waitForSelector('#otherButton');
    await page.click('#otherButton');

    await page.waitForSelector('#buttonToClick');
    await page.click('#buttonToClick');
  }`

I thought maybe it might have something to do with the way the DOM loads, so I tried:

await page.waitForNavigation({waitUntil: 'domcontentloaded'})
// await page.waitFor(3000)

  if ((await page.$('#buttonToClick')) !== null) {
    await page.click('#buttonToClick');
  } else {
    await page.waitForSelector('#otherButton');
    await page.click('#otherButton');

    await page.waitForSelector('#buttonToClick');
    await page.click('#buttonToClick');
  }

But that didn't work...it only works with await page.waitFor(30000) before the if statement...any ideas why?

HaagenDaaS
  • 103
  • 1
  • 1
  • 6
  • Maybe the result is ‘undefined’ so the !== null will always be true. Try it without the !==.. can you log document.querySelector(‘#buttonToClick’) and past the result here? – MevlütÖzdemir May 25 '18 at 20:34
  • Can you post more of your `node.js` program? I was under the impression that `document.querySelector` would be part of your webpage and not part of the puppeteer program. – Steve May 25 '18 at 20:43
  • This is the result of running document.querySelector(#choose-file-button) (the button I want to click) in the Chrome console: `` – HaagenDaaS May 25 '18 at 21:03
  • await page.$ never returns null for me! I am trying to check if an element is on the page and it doesn't seem possible – Gerry Feb 21 '19 at 15:37

2 Answers2

8

I don't think that document.querySelector normally exists inside a node.js program. Puppeteer does provide page.$, which is a close analog. It returns a Promise.

Update: With the new information in the question, it sounds like the part of the DOM tree that contains #buttonToClick is constructed after the DOMContentLoaded event. I've had good results using page.waitForNavigation({ waitUntil: 'networkidle0' }), though 'networkidle2' or some other option might be better if you have lingering network connections. See the various options here: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions

I suspect this should work:

await page.waitForNavigation({ waitUntil: 'networkidle0' });
if (await page.$('#buttonToClick') !== null) {
    await page.click('#buttonToClick');
  } else {
    await page.waitForSelector('#otherButton');
    await page.click('#otherButton');
  }
Steve
  • 10,435
  • 15
  • 21
  • So this code you pasted got me on the right track, but it didn't work until I added: `await page.waitFor(3000); if ((await page.$('#buttonToClick')) !== null) { await page.click('#buttonToClick'); } else { await page.waitForSelector('#otherButton'); await page.click('#otherButton'); }` and then it worked...I don't like the idea of waiting for a fixed number of seconds though, feels brittle – HaagenDaaS May 25 '18 at 21:18
  • I've updated my answer-- sounds like your page is adding the button after the `DOMContentLoaded` is fired. – Steve May 26 '18 at 00:53
  • Thanks so much for all your help. I tried adding `await page.waitForNavigation({ waitUntil: 'networkidle0' });` and that didn't work, so then I tried adding `await page.waitForNavigation({ waitUntil: 'networkidle2' });` and that didn't work either :( The only thing that seems to work is the `await page.waitFor(3000)` for some odd reason...even `await page.waitFor(1000)` seems to work as well...I guess I will have to live with that lol – HaagenDaaS May 26 '18 at 07:00
  • You might have some luck with setting `waitUntil` in your `page.goto` call, e.g. `page.goto(url, { waitUntil: 'networkidle0' })`. It sounds like it probably won't work though. For reference: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options – Steve May 26 '18 at 07:09
4

Sounds like what you really want is...

const btn = await Promise.race([
      page.waitForSelector('#buttonToClick'),
      page.waitForSelector('#otherButton')
]);

await btn.click()

i.e. wait for one or the other button. Whichever is found first, click that one. This will work even an SPA or ajax-based page where the buttons aren't there immediately upon load.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Thanks for this, would there be any way to tell which btn ended up getting clicked? Here is how things happen on the site im testing: Is #buttonToClick available? If yes, click it and move on. Else, click #otherButton and then click #buttonToClick. I think your above scenario would work for my "If" case, but in the "Else" case I would need to first click the #otherButton and then click the #buttonToClick, in which case I would know which button actually got clicked after `await btn.click()` if that makes sense? – HaagenDaaS May 25 '18 at 22:12
  • I've been using Puppeteer for all of 10 minutes, so I don't know, but I'm sure there's ample juicy details in the `btn` object that will tell you which one it found. Once you know what it is, you can click or not click or do whatever you need to do. – mpen May 25 '18 at 22:20