Getting browser automation scripts to do exactly what you want can feel like a never-ending battle between you and your code. I still remember vividly when I was up late one night years ago writing a Selenium script for a change I was making. I was banging my head against the table as I was running out of Selenium incantations to cast on my test to make it work.
Fast forward to today, I’m writing up this guide on a few tricks I wish I knew at the time, to quickly craft reliable E2E tests, instead of crying over error logs and wrangling clueless automation scripts.
A recorder is a tool that can record the browser actions you take, and turn those actions into a fully-working automation script. So you can start coding out your test, by simply stepping through your user flow as a user would.
I know there are people who might be skeptical, but hear me out. Just as automation frameworks have dramatically improved over time, test recorders have gotten some serious love over time as well.
I find test recorders pretty useful for a few reasons:
Ultimately, using a test recorder can give you a head start, save you from doing the boring parts of testing, and let you focus on the harder parts of perfecting a test automation script.
Here’s a few recorders that can help you get started:
As dynamic CSS classes and elements are the norm today, it can be difficult to try to pick selectors that are robust to a rapidly changing code base, or even a rapidly changing web page due to async logic.
Especially if your project uses something like Tailwinds, styled-components, or even a JS framework like React or Vue that use async logic to render elements, you might be struggling to figure out how to target the right element reliably.
The best way to combat these issues is to explicitly introduce a stable selector to elements you’re looking to test, and nothing beats stability like introducing test IDs to your application.
The concept is very simple: for elements you need to interact with, append a data-test-id=”my-element”
attribute to the HTML element. In your automation scripts, you can easily target the element with
await page.click('[data-test-id="my-element"]');
Now you’ll never need to worry about your selectors breaking the next time your team decides to change button colors or re-builds the application with a new minified class name.
However, this requires adding new attributes to elements you need to target. Depending on how open the application owners are to adding these new attributes, it may be difficult to rely on them. If test IDs aren’t possible, I’d fall back to the next best solution.
Luckily with accessibility becoming a higher priority for web applications, more and more critical elements that need to be interacted with might already have a machine-friendly label attached to it.
Usually you’ll see attributes such as aria-label
, alt
, or title
for elements you want to interact with. Those attributes tend to be more stable than CSS classes and can serve as a good stop-gap measure until you’re able to implement test IDs for the elements you need to test.
A script that utilizes these attributes might look like
await page.click('[alt="Main Logo"]');
If you’re testing an application that might not have all the accessibility selectors built out yet, and haven’t had time to implement test IDs, the last solution you can look towards is targeting elements by text content.
At first glance, it might sound like an incredibly fragile proposition. Indeed it can be for certain elements, but for others, it may be the best stable solution available. Can you remember the last time your team updated the “Sign In” button text on your application?
For elements with non-dynamic text content, usually buttons or input placeholders, text content can be a fairly reliable way to target elements.
Luckily in Playwright, it’s incredibly easy to target elements by text like so:
page.click('text=Sign In');
In Puppeteer, you’ll need to dip into XPaths to target elements by text:
await page.waitForXPath('//*[contains(., "Sign In")]');
const [element] = await page.$x('//*[contains(., "Sign In")]');
await element.click();
These best practices of stable selectors mentioned here and more are already built-in to DeploySentinel Recorder’s selector picking logic. So you don’t have to hunt for a specific test ID or accessibility selector.
If you’re banging your head against a test script trying to figure out why it isn’t working, whipping out the debugging modes is probably the fastest way to find out why your script isn’t doing what you want.
With Playwright, it’s incredibly easy to append PWDEBUG=1
in front of your script to pull up Playwright Inspector, where it’ll be able to step through everything Playwright is doing in great detail during the test. If there’s a step you have issues with, you can add await page.pause()
to pause the test run so you can inspect the page at that point in time.
If you’re executing the script in a remote environment, you can take advantage of Playwright’s Trace Viewer which records detailed logs and DOM snapshots before and after every action.
If you’re using DeploySentinel to run your test - Playwright traces are captured by default and viewable at any time to debug test runs.
In general, you can also enable headed mode with slow-motion enabled to visually see what your script is doing. Both Playwright and Puppeteer support this with just two extra lines of code. See the docs for Playwright and Puppeteer here.
The DeploySentinel Recorder will always have these two options commented out but inserted as part of every script generated to make it easy to debug locally.
Lastly, if there’s an issue that requires you to look at network requests or browser logs, you can have Playwright and Puppeteer open the Chrome dev tools panel on browser launch so all logs and network requests are captured from the start automatically for you. See the Playwright docs here or the dev tools section of Puppeteer’s debugging docs.
I hope this list of tips helps you out in creating testing scripts for Puppeteer or Playwright.
If you’re looking to run tests easily and affordably - you can try DeploySentinel for free, with many of the above best practices included for you with no extra effort.
First Published here