Search code examples
javascriptseleniumes6-promise

Why does awaiting Selenium getAttribute still return a promise?


I am using Selenium for JavaScript and trying to create an array containing a form's input values. I know that getAttribute (as well as most other methods) return a Promise. But, why are the values in the values array Promises even though I called await?

Then(/^The Author form should be empty$/, () => {
  return driver.findElements(By.css('.author-form input')).then( (inputs) => {
    expect(inputs.length).toBe(3)    
    let values = inputs.map(async (input) => await input.getAttribute('value'))
    console.log("The values")
    console.log(values)
    values.forEach((value) => {
      expect(value).toEqual('')
    })
  })
})

When I run the code, I get the following output:

The values
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]

Doesn't calling await on getAttribute produce a value?

(Yes, I know the code looks a little strange. I was trying to figure out how to call expect inside a forEach loop without getting an UnhandledPromiseRejectionWarning -- but that is a question for another post.)


Solution

  • To explain why this is happening, let me provide another example:

    let values = inputs.map(async (input) => {
        await input.getAttribute('value');
        console.log('After await');
    });
    

    Without async await, this becomes:

    let values = inputs.map((input) => {
        return input.getAttribute('value').then((result) => {
            console.log('After await');
        });
    });
    

    You would expect in this case that values is a list of promises, which it is.

    The problem you are running into is yes, you are awaiting the result. However an async function ALWAYS returns a promise. map does nothing to resolve that promise. The await inside the function passed to map doesn't actually do anything in this case. Instead, you need to do this:

    const value_promises = inputs.map((input) => {
        return input.getAttribute('value');
    });
    const values = await Promise.all(value_promises);
    

    Promise.all is designed for just this case, where you have a list of Promises and you want to get their values.