Introducing Playwright 🎭

Introducing Playwright 🎭

Introducing Playwright, one of the newest and most rapidly growing headless automation libraries out there. Released in January 2020 by Microsoft, Playwright is a Node.js library that advertises performant, reliable and hustle-free browser automation.

The Charming Browser Qualities 🐈

One of the main advantages that you will find on Playwright versus other similar solutions is the range of browsers it can orchestrate. It supports Chromium, Firefox and WebKit based browsers on Linux, Windows and Mac operating systems. Yes you heard that right, you can run a “Safari-like” browser on Linux or Windows; nothing new, just WebKit. An amazing advantage with the speed and reliability of protocol-driven browser libraries paired with a really broad range of browser coverage. How does Playwright achieve that out of the box ?

As mentioned above for Chromium based browsers, in a similar way as Puppeteer does, downloads a version of Chromium/Chrome and uses the Chrome DevTools Protocol to orchestrate the browser instance. For Firefox and WebKit engines, what Playwright does is again downloading the actual browser but extends their debugging protocol capabilities to provide a unified API and features. There is no modification of the actual browsers, so that it is expected to work exactly the same in the testing and the real user’s browser. To get a fill for the “patches” you can probably take a look at the repository under the _browserpatches folder.

Moving on From Puppeteer 💼

If you have used Puppeteer in the past and you were excited about its good parts, Playwright promises that with even more power. Just by taking a glimpse of the API on the Playwright official website you will quickly notice that it kinda looks the same with the Puppeteer API. You are certainly right and it is not by accident. The truth is that the same team that built Puppeteer, has now moved on to Microsoft and continued Playwright from a Puppeteer fork 👀

Without feeling the need to get into company politics or open source dynamics, the Playwright team promises an even better and more testing-friendly API along with significant improvements that target multi-page scenario performance, cloud-native operations and other goodies. All that while keeping the migration scenario from a Puppeteer codebase, an almost “mechanical” and straightforward task.

Let’s jump in then!

The Installation Step

Initially make sure you are in a machine with Node.js >=v10.15.0 installed so we can go with the current Playwright version.

Make a new project folder called playwright-example so we can start cooking 🍳

mkdir playwright-example
cd playwright-example

Now for the setup of our Node.js project.

npm init -y

The pre-setup (funny that we have these stuff 😅) is ready, now for the actual setup:

npm install playwright

Installing Playwright as you can see in your console pulls specific versions of Chromium, Firefox and WebKit. With the additional ~250mb of downloads in a special place in your machine cache, you get the browser support the library is rightfully advertising.

Small Detour 🏝

To ease up the tension that you might have been building with this thought running through your mind, we will take a small detour:

What is this WebKit thing and how can I be confident it behaves like Safari on Linux/Windows ?

First off some foundational knowledge. The ‘commercial’ browsers as you know them like Google Chrome, Mozilla Firefox, Apple Safari and others, are built on top of rendering/browser engines and each vendor adds on top of it a few goodies for its userbase. The most well known engines are Blink, Gecko and WebKit that are used by Chrome/Chromium/Microsoft Edge/Opera, Firefox and Safari respectively. In other words, it is the base for a browser’s main functionalities.

You can run WebKit with Playwright on Windows/Linux and expect similar results with the real Safari browser, as the layouting on the page and the JavaScript execution (handled by JavaScriptCore) are mostly the same. There might be differences in more specialized fields as how rendering works, performance, audio, video and images, but probably will fit your use case.

If you want to know more or keep up with the latest news on Playwright, go ahead and follow Arjun Attam, you won’t be dissappointed.

The Launchpad 🚀

Let’s create our launchpad with the bare essential commands to get up and running with Playwright.

Touch an index.js file with the following contents:

(async function(){
  const browser = await playwright.webkit.launch({ headless: false }); // Non-headless mode to feel comfy
  const context = await browser.newContext(); // So much to say, but another time
  const page = await context.newPage(); // Create a new Page instance which handles most of your needs
  await page.goto("https://playwright.dev"); // Navigate to the Playwright webpage
  await page.waitForTimeout(5000); // Rest your eyes for five seconds
  await browser.close(); // Close the browser
})();

Starting with Something Simple

To get our feet wet, we aim to test the autocomplete search functionality on the official Playwright website. The search component is there for users to search through the topics, documentation and API methods or Playwright. Our goal is to simulate the scenario where a user browses the page and searches for a specific method using this component.

playwright website

Well structured, dynamically updated component inside a Single Page App type website, it seems like a really good deal for a test drive. Our first objective is to build out the steps that a user needs to take to reach the goal of finding the API method she is looking for.

User Expectations 🥂

The user is expected to take the following steps:

  1. Open the page
  2. Try to find the autocomplete search
  3. Type his query for the API method he is looking for
  4. Click the most relevant result on the list
  5. Expect to see the section with the item he selected

Let’s now see how the steps, that we expect the user to take, can be translated to Playwright commands.

/* Somewhere else... */
const Homepage = {
  autocompleteSearchInput: "search-view input",
};
const apiSearchTerm = "context"; // The API method we are looking for

await page.goto("https://playwright.dev", { waitUntil: "networkidle" });
await page.type(Homepage.autocompleteSearchInput, apiSearchTerm);

// Check for 'methods' that have the specific search term 
await page.click(`//search-suggestions/a[contains(@href, 'api.md')]//mark[.='${apiSearchTerm}']`);

// Find the method name title using XPath
const $apiMethod = await page.$("xpath=//header-with-link//h4[contains(.,'context')]");

// Check if this method name section is actually visible on the viewport
const isApiMethodVisible = await $apiMethod.boundingBox();
assert.notEqual(isApiMethodVisible, null);

As you can see right above, the API that is expressing the user interactions down into code is at least in my view pretty intuitive. Similar to Puppeteer, you can expect that most actions the user can take are translated into direct Page instance methods (type, click, dblclick, etc…).

A point that we can stand on a bit is the combination of commands that are used to detect if the API method that we were looking for is actually inside the browser viewport. People with experience in the field know that to assert this fact you would either create your own custom command (doing viewport dimension calculations) or rely on a framework command that has already been implemented for us.

The differentiating factor here is that the command we get directly from Playwright could be considered the most reliable, just from the fact that it is provided by the platform itself.

One or Two Things Missing 🙈

After we all agree that the API is rather intuitive and simple to use, we can go over and mention a couple of things that might seem to be “missing” in making our development experience a tad much better.

1) Filling your code with the async keyword

As you have definitely observed, there is this async keyword you have to sprinkle all around your code, and it feels a bit noisy for me at least. This keyword is required because of the event-driven nature of the browser APIs. The way to code around asynchronous and event-driven platforms in JavaScript is by using Promises to model your operations, and Playwright has done just that.

To make handling of those asynchronous operations a bit less painful, JavaScript has added some new keywords to the language syntax. These keywords are the async & await that you see on our code. Because Playwright’s API needs to use Promises, the best way we can write our code is to use this async/await syntax for most commands.

2) No chaining available yet

Due to some design decisions and the nature of the library, as we have mentioned in the point above, there is currently no support for what we can call method chainning. With this capability our code could become so much more fluent to read and follow through. Picture something like:

await page.$("search-view input").click().type("context").submit();

But at some point, we might get there!

Closing 🧘‍♂️

So this was a glimpse to get you started with your first Playwright script to assert an actual user scenario. Loads of things to mention for each command and the capabilities but we are gonna see them closer in the recipes that will come on The Home of Web Automation.

Especially the Playwright’s BrowserContext as advertised is an abstraction that can unlock much more power and performance using parallelization locally or from even the cloud. Pretty excited to try that!

Playwright might seem new to the scene but on the contrary it has some long history as we mentioned earlier. If you want to pit it against another tool or introduce it as ‘X killer’, sorry but we are not doing that here. The least I can say is that if your application has a considerable slice of WebKit-based browser users, then give Playwright a try, your users will thank you for that 💪

As with every tool though, start with anything that catches your attention, feels comfortable and addresses your actual needs in a simpler way.

Cross posted from The Home of Web Automation

Image by Devanath from Pixabay