What I want to do :
There is an input box and when you start typing an email id like abc@example.com
, it will show you a recommendation list of person names that can have that associated email id you are trying to type. You can click on any person name in this list that has the associated email you typed and the corresponding person name will be entered in the input box like a pill(the way you see when you add tags to a question in StackOverflow).
Again when you click inside this input box in any empty area then you can type another email like def@example.com
and list will again appear and you can click on the name that matches the email id you typed and it will be added to the input as well. And so on...
Input box will look something like below after above steps - (sorry for oversimplification)
ABC, DEF |(cursor)
Below is the code that I have written for above steps -
Code Section A :
inviteeDetails = [
{
name: "ABC",
email: "abc@example.com"
},
{
name: "DEF",
email: "def@example.com"
},
{
name: "GHI",
email: "ghi@example.com"
}]
async function addInvitees(inviteeDetails) {
inviteeDetails.forEach(async (invitee) => {
// type the email id
await page.getByTestId("parent").getByRole("textbox").fill(invitee.email);
// click on the name from the recommendation list that matches the email id entered
await page.getByTestId("result-list-item").nth(0).click();
});
}
await addInvitees(inviteeDetails);
Suppose I have to add 3 email addresses like the way above and only once these are added(i.e filling + clicking) in the input box, I have to do some verification for which I need to get the names from each of the pills. I am getting the text from each of the added three pills using allInnerTexts()
as below. The text in each pill denotes the name of the person belonging to the email.
const allNames = await page.getByTestId("invitee-details").getByTestId("invitee-name").allInnerTexts();
console.log("ALL NAMES", allNames);
What is the problem that I am facing : allNames
array is empty when the code in Code Section A is executed and
works fine when I execute all the code in Code Section A one by one WITHOUT using the loop and allNames
array is NOT empty and has values ["ABC", "DEF", "EFG"] which is expected.
What I have debugged in playwright inspector and console logs :
In the UI I can observe that this line await page.getByTestId("parent").getByRole("textbox").fill(invitee.email);
executes three times. I could see all three email ids typed in the input box.
It then goes to the line const allNames = await page.getByTestId("invitee-details").getByTestId("invitee-name").allInnerTexts();
and executes it
Then it executes console.log("ALL NAMES", allNames);
(this prints 0 in console log obviously because await page.getByTestId("result-list-item").nth(0).click();
is NOT executed and is executed after this statement)
And finally executes await page.getByTestId("result-list-item").nth(0).click();
three times.
I am trying to figure out if the statement await addInvitees(inviteeDetails);
has an await. Then it should execute completely before moving on the statement const allNames = await page.getByTestId("invitee-details").getByTestId("invitee-name").allInnerTexts();
Where am I wrong? What can I do to solve this problem. Thank you in advance! :)
You cannot use forEach loop over async code.
Use for...of loop instead:
With modern for … of loop await
will work as expected:
async function addInvitees(inviteeDetails) {
for (const invitee of inviteeDetails) {
await page.getByTestId("parent").getByRole("textbox").fill(invitee.email);
// click on the name from the recommendation list that matches the email id entered
await page.getByTestId("result-list-item").nth(0).click();
}
}
Reference: Using async/await with a forEach loop