Many times, you unknowingly carry the legacy problems to your new solutions. Specifically, QAs transitioning from Selenium to Playwright, they tend to do this mistake. They don’t utilise the features offered by Playwright at fullest and soon start experiencing the same problems as faced with Selenium. Missing out the important features of these new tools keeps you outdated and probably hindering you to get the best possible outcome from automated tests.
In last few years, I have worked with several people, all of them had prior experience with Selenium and now transitioned to Playwright. Playwright has definitely a learning curve, but people generally focus on boilerplate code and structure. They tend to ignore the important details like locators suggested by Playwright or the waits. It is not the tool (Selenium) limiting them but rather lack of awareness of the Playwright’s best practices. For such folks, I am going to highlight a couple of examples where they should be leveraging Playwright’s capabilities to get most out of it.
Locators
The first and most important poor practice I notice when people transition to Playwright from Selenium is their choice of locators.
To begin with, people are very comfortable in writing a locator like this in Selenium.
("//div[@id='main']//span[contains(text(),'Checkout')]")
However, Playwright offers a better approach:
getByRole('button', { name: 'Checkout' })
Though this is a small example, but the benefits are significant in Playwright way of using it. XPath depends heavily on the DOM structure, if the <span>
changes to a <div>
, the locator breaks. Additionally, the elements in the XPath must appear in the same order within the HTML, meaning any structural change could cause test failures.
Playwright’s locators are simpler, more readable, and more resilient. Instead of relying on the DOM structure, Playwright understands the role of the HTML element and its associated text. This means the locator remains valid even if the button is moved to a different <div>
or <span>
.
Let’s take another example, where we iterate loop for findElements in Selenium to find the element of interest.
List<WebElement> products = driver.findElements(By.className("some-name"));
for (WebElement product : products) {
if (product.getText().contains("Some String")) {
product.findElement(By.className(".someClassName")).click();
break;
}
}
There is nothing wrong in above locator strategy. However, Playwright does not encourage such loops as there are short and convenient alternative is available in Playwright to achieve same. Playwright offers functions like filter, nth, first, last to work with complex DOM structure. It removes the boilerplate code of doing same. Additionally, readability gets better with such piece of code.
await page.locator('.some-name')
.filter({ hasText: 'Some String' })
.getByRole('button', { name: 'Button Text' })
.click();
What you miss
Readability, maintainability and less prone to failures are few advantages that you will miss out if you are still using legacy ways of locating elements. I would like to underscore another important aspect of Playwright that is, its implicit check of accessibility in certain locators. For example, getByRole in Playwright has certain accessibility benefits which you get without any extra effort by using appropriate locators. I would recommend readers to read in detail about locators offered by Playwright before transitioning to Playwright.
Waits
Selenium, being a unidirectional tool (except for the latest version 4), lacks the intelligence to understand the application’s behavior in real-time within the browser. Writing tests in Selenium requires explicit waits to ensure elements are ready before interaction. The absence of waits often makes the tests non-working. Hence, QAs started using Playwright after Selenium, generally write explicit waits in Playwright as well.
This mindset is not necessary when using Playwright. Playwright has understanding of the application’s behavior, particularly in Chromium-based browsers, due to its use of the Chrome DevTools Protocol (CDP) behind the scenes. It intelligently tracks the DOM state and automatically waits for elements to become actionable, reducing the need for explicit waits. This built-in auto-wait mechanism ensures more reliable and stable tests without additional effort from the tester. Additionally, the explicit waits in Playwright can be replaced with waitForLoadState
.
I must say, auto-waits in Playwright are a great help. They are especially efficient for enterprise applications where large volumes of test data are involved, and the application frequently displays loading popups before rendering the complete UI.
Such applications can be challenging to automate using Selenium WebDriver because these loading popups or spinners prevent interactions until the data is fully loaded. Selenium lacks built-in mechanisms to handle such scenarios efficiently. Playwright automatically waits for the UI to stabilize before proceeding with interactions. This significantly reduces flakiness, eliminates the need for manual wait handling, and makes test execution more reliable and efficient.
Few important reads before you use Playwright
- Auto-waiting | Playwright
- Avoiding hard waits in Playwright and Puppeteer – DEV Community
- Best Practices | Playwright
- Page Object Model [Question] · Issue #1604 · microsoft/playwright
Thank you for reading this post, don't forget to subscribe!