Migrating from Puppeteer to Playwright
We recently switched from Puppeteer to Playwright for end-to-end (e2e) testing. There were several reasons for this, the most important of which is that Playwright is far more powerful than Puppeteer. In this blog post, we’ll cover the reasons why we chose Playwright over Puppeteer.
Migration Motives
This migration was motivated by several factors, including:
-
Locators
-
Browser compatibility
-
Debugging knowledge
-
Test runners
-
Asynchronous matchers
In the next sections, we’ll cover each of these in depth.
Locators
Locators are the foundation of Playwright’s auto-waiting and retry functionality, and they’re a type of getter function that can return an element at any time. Locators are far more powerful than ElementHandle
because you don’t have to wait for elements to be visible before performing an action. Instead, a locator waits for an element to become visible before performing an action:
await page.locator('text=Hello').click();
page.locators
accepts a variety of selectors, including text, CSS, and XPath. This significantly outperforms ElementHandle
, which can only accept CSS selectors. Playwright also enabled us to build custom selectors.
Browser Compatibility
Playwright works with Chrome, Firefox, and Safari, whereas Puppeteer only works with Chrome. It supports Firefox, but many APIs have yet to be implemented. Playwright also has experimental Electron support, in turn increasing the number of platforms we can write tests for.
Debugging Knowledge
Playwright includes an excellent set of debugging tools that aid in identifying problems with either the test or the implementation. Some of the most helpful are:
-
Inspector — The inspector built into Playwright assists us in debugging and fixing flaky tests.
-
Trace Viewer — The Playwright Trace Viewer is a graphical user interface (GUI) tool that enables us to navigate through recorded Playwright traces of our tests, in turn allowing us to visually see what’s happening during each action. It saves console logs, network requests, and screenshots for each executed line.
-
Selector Debugging — When debugging, the inspector highlights the screen element where the current action is taking place. This makes it clear whether the selector we’re using is resolving to the correct elements.
Test Runner
Playwright includes its own test runner, which is easy to set up and use. It also has many features, such as parallel execution, retries, and more. Previously, we were using Jest, but because the APIs of Playwright and Jest are similar, migrating the test runner was easier.
Asynchronous Matchers
The expect
library is included with the Playwright test. The library’s async matchers that wait for a condition to be true
add even more value:
const locator = page.locator('text=Hello'); expect(locator).toHaveCSS('display', 'flex');
All the available matchers can be found here.
Migration Strategy
We transitioned our tests from Puppeteer to Playwright gradually. We began by migrating the most problematic tests, and then we moved on to the remaining tests. We also had to switch our test runner from Jest’s to Playwright’s. This meant we had two test setups running in parallel while migrating, which ensured all the tests on the main branch ran as expected.
We were able to remove all the flaky tests as a result of the migration. We agree that even while using Puppeteer, we can write tests that aren’t flaky, but Playwright makes it much easier to write stable tests and debug problems if one fails. This clearly demonstrated to us how developer-friendly Playwright is.
If you find yourself in a situation where you have to choose between the two, I hope this article helps you make a decision.