Sponsored Link
Looking for a unified way to  viewmonitor, and  debug Playwright Test Automation runs?
Try the Playwright Dashboard by today. Use the coupon code PWSOL10 for a 12 month 10% discount.

Is it possible in Playwright to conditionally click an element depending on which one is visible?

A question was recently asked in the Playwright Slack Channel asking if it was possible to click on whichever element was visible out of 2. There was an answer that was provided that allowed me to dive in and learn more about promises in JavaScript.

The solution used the Promise.race() method which accepts an Array of Promises, and whichever promise returns soonest gets fulfilled.

The Promise.race() method is one of the promise concurrency methods. It's useful when you want the first async task to complete, but do not care about its eventual state (i.e. it can either succeed or fail).

This is great for us in that our test won't fail as long as one of the locators is present that we are interacting with. To prove out and test this I found the the-internet.herokuapp.com had a page with disapearing elements. The Portfolio element is always returned while the Gallery element is only sometimes returned upon page refresh.

The below code snippet has comments explaining what is happening, and some console.log comments that can be used for debugging.  What is really nice with this implementation is you can just add as many locators as you want to the array, if the system under test has multiple states that need to be accounted for.

test("Click one of the elements in the array using promise.race", async ({
  page,
}) => {
  await page.goto("https://the-internet.herokuapp.com/disappearing_elements");

  // Builds a promise that can then be passed into the Array of promises
  const waitForLocator = (locator: Locator): Promise<Locator> => {
    return locator.waitFor().then(() => locator);
  };

  let returnedLocator = await Promise.race(
    // Array promises/locators
    [
      waitForLocator(page.getByRole("link", { name: "Gallery" })),
      waitForLocator(page.getByRole("link", { name: "Portfolio" })),
    ]
  );

  // console.log(await returnedLocator.innerText());
  await returnedLocator.click();

  // console.log(page.url());
  await expect(page).toHaveURL(/.*gallery|.*portfolio/);
});

After going through this exercise, I started to think through if there is an easier more straightforward way to handle this conditional, and started to whip up a solution with if  statements. After a few minutes I had a pretty good working solution that was easier to grasp. In the code below we are making a check to see if the locator.isVisible() if true it proceeds, if false it moves onto the next block. I would tend to use this solution as it gets the job done, and it's much easier to follow.

test("Click one of the elements that is visible out of two", async ({
  page,
}) => {
  await page.goto("https://the-internet.herokuapp.com/disappearing_elements");

  const gallery = page.getByRole("link", { name: "Gallery" });
  const portfolio = page.getByRole("link", { name: "Portfolio" });

  if (await gallery.isVisible()) {
    await gallery.click();
  } else if (await portfolio.isVisible()) {
    await portfolio.click();
  }

  await expect(page).toHaveURL(/.*gallery|.*portfolio/);
});

Thanks for reading! If you found this helpful, reach out and let me know on LinkedIn or consider buying me a cup of coffee. If you want more content delivered to you in your inbox subscribe below.