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.)
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.