Introduction
In the realm of web automation and testing with Playwright, understanding the performance of various locator strategies is key. This article delves into the getByRole
locator's efficiency compared to CSS selectors, offering insights into the technical workings and practical implications of these choices.
I wanted to use Page#getByRole
's underlying CSS selectors in our codebase. But the getByRole
locator was 1.5 times slower compared to the standard CSS selectors, prompting an investigation into the root cause. This performance discrepancy, likely stemming from Playwright’s use of querySelectorAll('*')
and matching elements by the accessible name, raises essential considerations for developers prioritizing speed in their automation scripts.
Deep Dive: How getByRole
Works
The getByRole
function in Playwright is more than just a method to locate web elements; it's a complex mechanism with multiple layers of interaction within the Playwright architecture. Let's demystify this process with an example. Consider this code:
await page.getByRole('button', { name: 'Enter address manually' }).click();
This command sets off a cascade of actions within Playwright:
-
Page#getByRole creates a
Locator
-
Locator#click delegates call to
Frame#click
passing theLocator#_selector
-
Frame#click delegates to
Channel#click
.Frame
inherits_channel
fromChannelOwner
.ChannelOwner#_channel
is a JS Proxy object based on theEventEmitter
-
Client
Frame
dispatches an event to the serverFrame#click
-
FrameSelector#resolveInjectedForSelector injects the
FrameExecutionContext#injectedScript
script to the page, controlled by Playwright. TheInjectedScript#constructor
adds the engine forgetByRole
locator. -
createRoleEngine calls
parseAttributeSelector
andqueryRole
-
queryRole calls
querySelectorAll('*')
and matches the element
Compared to the getByRole
, the locator with the regular CSS selector just traverses DOM and is 1.5 times faster.
Performance
Comparative tests reveal that a regular locator using CSS selectors outperforms getByRole
by 1.5 times. Interestingly, the $.then
method trailed, being 2x slower in our tests.
console.time("getByRole");
for (let i = 0; i < 100; i++) {
const textContent = await page.getByRole('button', { name: 'Enter address manually' }).textContent()
}
console.timeEnd("getByRole");
console.time("locator");
for (let i = 0; i < 100; i++) {
const textContent = await page.locator('.ektjNL').textContent()
}
console.timeEnd("locator");
console.time("$.then");
for (let i = 0; i < 100; i++) {
const textContent = await page.$('.ektjNL').then(e => e.textContent())
}
console.timeEnd("$.then");
// Output:
// getByRole: 677.5ms
// locator: 497.306ms
// $.then: 1.135s
Conclusion
In web automation, understanding Playwright's locators — getByRole
and CSS selectors — is key. getByRole
excels in clarity, while CSS selectors win in speed. Choose wisely: getByRole
for testing accessibility and/or user-facing attributes of elements, CSS selectors for efficiency.