Pseudo-elements are one of the stranger constructs supported in the HTML and CSS specs. Pseudo-elements don’t just
modify the styling of an existing element, but can also add content to the page. But unlike a normal element,
pseudo-elements are not considered a distinct node within the Document Object Model (DOM) and thus cannot be accessed
through the querySelector
or querySelectorAll
.
Because of these pecularities, pseudo-elements can be quite difficult to test in an automated way, lack native support in many testing libraries including Microsoft’s popular Playwright testing framework. In this article we’ll show you how you can workaround these limitations to test pseudo-elements in Playwright.
Syntax and uses
Pseudo-elements are represented by a double colon (::) followed by the name of the pseudo-element:
|
|
For example, you can use the ::first-letter
pseudo-element to style the first letter in an element.
|
|
Other popular pseudo-elements include:
::before
- Adding content before the selected element.::after
- Adding content after the selected element.::first-line
- Styling the first line of an element.::placeholder
- Styling the placeholder of an input element.
Note that pseudo-elements look very similar to pseudo-classes, such as foo:hover
, but are quite different.
Pseudo-classes define the styling of an element in a certain state, such as in the state of hovering, whereas
pseudo-elements add content or styling to the default state of an element.
To make things even more confusing, Playwright has a similarly-named construct called pseudo-selectors..
Pseudo-selectors, such as :has()
and :text()
, are methods for accessing elements on the page in ways that are not
supported in the CSS spec. In other words, with pseudo-selectors Playwright has extended the CSS spec to offer
additional convenience methods for referencing elements in the DOM.
Even though Playwright has extended the CSS spec to provide these convenience methods, it does not include a native way to access pseudo-elements. We’ll show you the workaround for accessing pseudo-elements in Playwright, but first let’s cover how to get your development environment up and running.
Playwright support for pseudo elements
Here’s a simple HTML example with a single pseudo-element defined:
|
|
If you view this example in your web browser, you’ll see that the <p>
element will the content ‘Yay. ’ prepended to
the string ‘Hello World’:
Pseudo-elements let you use CSS to render elements that don’t exist in the DOM. Hence the name “Pseudo”. Playwright does not support accessing these pseudo-elements with pure selectors.
Install Playwright using pip:
|
|
|
|
After running this code, you’ll observe that Playwright will only return the contents of the original HTML element.
Accessing pseudo elements in Playwright
Playwright provides a page.evaluate()
method that executes javascript code. The window.getComputedStyle()
function
gets all the styles related to an element or pseudo-element.
|
|
Using JavaScript, you can extract the value of the pseudo element’s content.
|
|
The getComputedStyle()
function accepts two arguments:
- The element for which the computed style should be returned.
- A string that specifies the pseudo-element to match. For real elements, this is omitted (or null).
|
|
The snippet outputs the content of the pseudo-element.
Pseudo elements and radio buttons
::before
is a common pseudo-element for styling checkboxes and radio buttons. Consider the simple HTML page below:
|
|
Using CSS pseudo-elements, we can change the colour of a radio item, both before and after it has been checked.
|
|
CSS Credit: Stackoverflow
While this is a very simple example, this same approach is used by many UI libraries to provide custom-styled radios and checkboxes that still preserve some native behaviors like keyboard support.
The page.click()
method in Playwright is used for clicking on selected elements. In some cases, the default click
method will work as expected and will click on the input that contains a pseudo-element:
|
|
The snippet outputs:
In this example, Playwright correctly clicks on the element and extracts the expected background color of #ffa500
(rgb(255, 165, 0)
). If you find that clicks are not working correctly on elements that contain pseudo-elements, the
likely culprit is that the click needs to be modified with an offset so that it clicks on the pseudo-element which lies
outside the boundaries of the actual element. To do this, you’ll need to define the options argument in page.click
and
pass in the position
property. So for example if you need to click 10 pixels to the left of the top-left corner of the
main element, and 5 pixels above the top-left corner, you can pass in a value of { position: { x: -10, y: -5 }}
.
If we wanted to test the default state of the element, we could instead use the following code:
|
|
The new snippet outputs:
rgb(209, 211, 209
is equivalent to #d1d3d1
, the radio button’s original background colour in an unchecked state.
Conclusion
In summary, pseudo-elements lets you add fake elements to the DOM using CSS. These pseudo-elements cannot be selected
using ordinary Playwright selectors. However, via the use of the native window.getComputedStyle()
method, and
Playwright’s ability to evaluate Javascript code, we can fetch and test pseudo-elements in Playwright.
Reflect: An alternative to Playwright with native support for pseudo-elements
Reflect is a no-code testing tool that can interact with pseudo-elements like in the example above.
Creating a test in Reflect is easy: the tool records your actions as you use your site and automatically translates those actions into a repeatable test that you can run any time.
Get automated test coverage for your web app today — try it for free.