These are exciting times for End to End (E2E) testing in the JavaScript world. In the last couple of years, tools such as cypress and Puppeteer have flooded the JavaScript community and gain a fast adoption.
Today I’m writing about Puppeteer. I want to share a pragmatic list of tips and resources that can help you get a fast overall understanding of things to consider when using Puppeteer, and what it has to offer.
»Topics I’ll Cover
»Getting things running
In this section, I discuss the main aspects of running a test with Puppeteer, including some interoperability aspects that we should consider, such as the usage of an underlying testing library/framework such as Jest.
»Running tests in parallel
To launch different browser instances to run your test suite, you can rely on your chosen test runner. For example, with Jest, I leverage the config maxWorkers to define how many browser sessions I allow to run concurrently.
»Be aware of the global timeout value
You want to increase the default global value for a test to timeout. E2E tests might take up several seconds to run. If you’re using Jest, you can configure the timeout value with the property testTimeout, which for Jest 26.0 defaults to 5 seconds.
Here’s an example of my jest.config.js
with the mentioned configurations.
If you’re using (for example) mocha, you can add this.timeout(VALUE_IN_SECONDS);
at the top level of your describe
block.
»Abstracting puppeteer.launch
To bootstrap your test, you have to run puppeteer.lauch. I recommend you to be abstract this call within a wrapper function. Doing so allows you to centralize all your test environment customizations easily. I’m referring to making the following things configurable:
- Allow the client to specify what page the browser should open on.
- Allow the client to decide under what network conditions the test runs. I’ll also cover this in this article.
- Allow the client to specify things like whether the DevTools are open etc.
I like to have my launch function just dealing with the bootstrap configuration aspects of my test environment and launch the application. I try to keep it as slimmer as possible, but sometimes I feel the urge to add more stuff here. There’s a saying:
“Functions should do one thing. They should do it well. They should do it only.”
source: Clean Code by Robert C. Martin
»Throttling Network Connection Speed
You can run your tests under different network speed conditions. Let me share the pattern I use based on this gist that I luckily found.
If you abstract puppeteer.launch, your test could switch between network presets just by doing the following.
network-presets.js
»Loading a Browser Extension
Here’s how you can load a browser extension.
If you want to read through about testing chrome extensions with Puppeteer, I recommend this article: Automate the UI Testing of your chrome extension by Gokul Kathirvel.
»Writing Tests
Apart from the last subsection, what I discuss next, can be easily found in the official documentation. I’m just going to step on those topics that I consider to be essential parts of the Puppeteer API.
»Working with page.evaluate
You’ll need to get used to the detail that when using page.evaluate, you run on the page context,
meaning even if you’re using arrow functions as an argument to page.evaluate
, you can’t refer
to things out of the scope of that function. You need to provide all the data you’ll need as the
third argument of page.evaluate
. Keep this in mind.
»page.waitForSelector & page.waitForFunction
Quickly getting familiar with the APIs page.waitForSelector and page.waitForFunction can reveal itself very productive. If you have a couple of tests to write changes that you’ll need to wait for some condition to be met in the UI before you allowing your test to proceed, are high. Suspend the test flow and wait for the UI is a common practice, not exclusive to Puppeteer. See the below examples for some basic usages.
There’s a decision you need to make. The choice is whereas you should have a higher or lower timeout. I usually try to advocate for lower as possible, because we want to keep our tests fast. Running E2E tests against systems where you need to perform (not mocked) network requests, means that you need to account for network instability, altough usually you’ll run under perfect network conditions, you might want to cut some slack to the timeout value.
»element.select
I like the way it’s possible to select options on a native HTML select element. It works both for single and multiple selections, and it feels natural.
While element.select
it’s convenient, you’ll probably have to approach this
differently for custom select fields built on a div > ul > li
structure with a
hidden input field, for instance select Material UI components.
»Screenshots
For specific test cases, I like to output a collection of screenshots that build a timeline of how my application looks throughout the test. Screenshotting in between your test helps you get an initial pointer to what you should be debugging in a failing test. Here’s my small utility that wraps page.screenshot API.
»Page Reloads
With the page.reload API. You can specify a set of options that allow you to wait for specific underlying browser tasks to idle before proceeding.
In this above example, we reload the page with networkidle0
, which does not allow the test to proceed unless there are no HTTP requests within a half a second period.
»Clearing Text from an Input Field
I was stunned not to find a very out-of-the-box way to clear an input field. A few developers have expressed interest in this feature, but it seems there’s no interest on the other end. I’ve found a way to do it:
It only works on Chrome as it takes advantage of the functionality where three consecutive clicks in a text area/input field select the whole text. After that, you need to trigger a keyboard event to clear the entire field.
»Debugging
I want to highlight some debugging techniques. Especially the slowMo
option.
»Debugging with slowMo
You’ll want to use slowMo to debug individual tests. The option allows you to slow down the interactions (the steps) of your E2E test so that you can see what’s going on, almost like seeing an actual human interacting with your application. I can’t emphasize enough how valuable this is.
In the following GIFs you can see the difference of running without and with the slowMo
option respectively. E2E test on the tweak chrome extension without slowMo. You can’t possibly understand what’s going on.
In these examples I’m using the tweak browser extension to demo the different use cases.
For more awesome tips for debugging, I highly recommend this short article on Debugging Tips from Google.
»Using debugger
I got this one from Google debugging tips. I had the habit of
throwing a sleep
statement to stop my tests for X seconds and inspect the application
to see why the tests were breaking. But now I completely shifted to this.
For more great debugging tips, I highly recommend this short article on Debugging Tips from Google.
»Performance Automation
There’s quite a buzz going on about using Puppeteer to automate web performance
testing. I couldn’t write this article without giving a shout out to Addy Osmani on the
work developed on addyosmani/puppeteer-webperf, which I couldn’t
recommend more. Within the project README.md
you’ll find the most organized
set of examples to tune your performance automation.
»Browser Support
According to the official documentation, you can use Puppeteer with Firefox, with the caveat that you might encounter some issues since this capability is experimental at the time of this writing. You can specify which browser to run via puppeteer.launch
options API that I’ve covered in this section.
What are your favorite bits of Puppeteer? What would you recommend me to learn next?