How to wait for a button to be enabled and click with puppeteer?
Asked Answered
H

5

7

I have a form with two fields username and password. Once the username is entered the next button is enabled, once I click on it, it shows a password field and once that's entered, it enables the next button again. How do I wait for the button to be enabled in between form updates?

I tried the below approaches, one is commented and the other is not. Both don't work for me.


(async () => {
    const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('http://localhost:9000/start#!');

await page.type('#login-form-un-field', 'xxxx')
// await page.waitForTarget('#default-next-btn:not([disabled])')
await page.$eval('#default-next-btn:not([disabled])', elem => elem.click());
// const btnNext = await page.$('#default-next-btn');
// btnNext.click();
await page.type('login-form-passcode', '1234');
await page.click('#default-next-btn');
await browser.close();
})();```

Thanks for the help in advance.

Edit: the button is always present on the page. It is just disabled while form entries are being validated.

Hallo answered 23/1, 2020 at 15:58 Comment(0)
I
4

you can use await page.waitForSelector(selector) => docs

your code became:

(async () => {

    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.goto('http://localhost:9000/start#!');

    await page.type('#login-form-un-field', 'xxxx')

    await page.waitForSelector('YOUR_SELECTOR_1')
    await page.click('YOUR_SELECTOR_1')

    await page.type('login-form-passcode', '1234');

    await page.waitForSelector('YOUR_SELECTOR_2')
    await page.click('YOUR_SELECTOR_2')

    await browser.close();
})();

Inerney answered 23/1, 2020 at 16:26 Comment(0)
J
30

To wait for a button (or input) to no longer be disabled, i.e. <button id="id" disabled> use :not([disabled]) in the selector string passed to waitForSelector.

Example:

page.waitForSelector('button#id:not([disabled])');
Janis answered 6/10, 2020 at 15:53 Comment(0)
I
4

you can use await page.waitForSelector(selector) => docs

your code became:

(async () => {

    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.goto('http://localhost:9000/start#!');

    await page.type('#login-form-un-field', 'xxxx')

    await page.waitForSelector('YOUR_SELECTOR_1')
    await page.click('YOUR_SELECTOR_1')

    await page.type('login-form-passcode', '1234');

    await page.waitForSelector('YOUR_SELECTOR_2')
    await page.click('YOUR_SELECTOR_2')

    await browser.close();
})();

Inerney answered 23/1, 2020 at 16:26 Comment(0)
R
1

I had this problem and I couldn't find any solution. So I'm checking for the button to be enabled repeatedly

// Using Typescript but you can remove the Types to make it JS :)

function runRepeatedly(callback: () => Promise<boolean>, options?: TOptions) {
  return new Promise((resolve, reject) => {
    let totalTime = 0;
    const interval = options?.interval || 2000;
    const maxLimit = options?.maxLimit || 50000;
    const id = setInterval(async () => {
      const isDisabled = await callback();
      totalTime += interval;
      if (!isDisabled) {
        clearInterval(id);
        resolve();
      }
      if (totalTime === maxLimit) {
        clearInterval(id);
        reject("Max time reached");
      }
    }, interval);
  });
}

Here's how you use it

try {
    await runRepeatedly(async function () {
      const response = (await page.evaluate(
        `document.querySelector("button").hasAttribute("disabled")`,
      )) as boolean;
      return response;
    });
} catch (err) {
  // Handle the error here 
  console.error("Error", err);
}

Hope it helps 😊

Retina answered 5/12, 2020 at 4:37 Comment(0)
C
1

I have done it by querying the class list object which returns all the classes including 'disabled'

await page.waitForSelector('#buttonId') 
const isDiabled = await page.evaluate(() =>
      document
        .querySelector('#buttonId')
        .classList.contains('disabled')
    )

Once you will have the isDisabled value as false you can proceed to the next steps. This solution is helpful when a tester just wants to create a test to check if the object shown on the screen is disabled or not.

Colter answered 11/3, 2021 at 2:37 Comment(0)
C
0

The correct way is to use await page.waitFor(selector). For more info check the docs here

// First you wait for button to appear on screen
await page.waitFor('#default-next-btn');

// Then you click it
await page.click('#default-next-btn');
Counterespionage answered 23/1, 2020 at 16:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.