Playwright: Launching Cross-Browser Automation to the Stars

Alexander Andryashin
Jul 16 · 6 min read
Image for post
Image for post

Hey,

Hope you and your family are doing well.

Several months have passed since our last article about efficient browser automation. If you missed some of them — the entire list can be found here. Today we are going to dive into an attractive Playwright browser automation world.

What is Playwright?

Web application architecture has evolved a lot during the last years. Now client-side part of a web application (frontend) can be as complex as the backend. From dozens of large static HTML pages with Javascript inclusions the world now moved to a single HTML page with thousands of lines of complex Javascript logic. Such fundamental change in web applications certainly requires a lot of changes in testing. It would be great to have full control over a running single-page application code with an ability to catch any page lifecycle events, log messages and exceptions being thrown. We certainly would like to inspect and optionally mock all network requests between client and server side. Sometimes we would need to manipulate and mock page layout including DOM tree and CSS styles. Unfortunately such important features are simply not available in Selenium and that is the first reason to create new generation of browser automation tools.

The second reason is that in the majority of cases the only thing that matters when opening a web page in browser is the rendering engine used in this browser. If we don’t need to test in old-school browsers like Internet Explorer and Opera 12.16, all other browsers are nowadays using one of three popular engines:

  • Blink — used in Chromium-based browsers like Google Chrome, modern Opera and Microsoft Edge
  • Gecko — used in Firefox and other Mozilla projects
  • Webkit — nowadays used by Safari browser for desktop and mobile platforms

So being able to run automated tests in these 3 browser engines we cover browser automation in the majority of modern browsers. These considerations (more powerful API and limited set of supported browser engines) gave life to the new generation of browser automation tools like Puppeteer and its successor — Playwright.

So what is Playwright nowadays? Basically it is a Node.js library able to launch Blink, Gecko and WebKit engines, giving everything Selenium can do and additionally providing all important features not available in Selenium and described above. A typical Playwright test working with Firefox is very similar to Selenium one and looks like the following:

const { firefox } = require('playwright');

(async () => {
var browser = await firefox.launch();
const page = await browser.newPage();
await page.goto('http://internet.yandex.ru/');
await page.screenshot({ path: `screenshot.png` });
await browser.close();
})();

To use another browser, e.g. Chromium you just need to replace firefox with chromium:

var browser = await chromium.launch();

Execute your Playwright tests in parallel

Image for post
Image for post

To understand how to run Playwright tests in parallel we need to dive a bit into its internals. Both Selenium and Playwright are sending all browser automation commands and receiving responses in JSON format. Selenium for historical reasons is doing this using separate HTTP requests for every command like launching the browser, opening the page, taking screenshots and so forth. Sending a new HTTP request for every command requires to send and receive a lot of redundant information such as HTTP headers. Such approach tends to be slower than using one permanent communication channel for all commands which is alive while browser is running. Knowing this Playwright is using the second approach and communicates with browser using one long-living web-socket connection. In the code above this web-socket connection is being established inside launch() method implementation which also automatically starts browser process for you. If you wish to start Playwright test working with remote browser, you have to explicitly configure this test to connect to remote web-socket endpoint as follows:

var browser = await chromium.connect({ wsEndpoint: 'ws://remote-host/some-endpoint' });

Every time you run this line of code — a completely new browser with clean cache will be started in remote Kubernetes cluster in seconds. Now let’s apply this knowledge to Moon. In Selenium world a standard port to execute tests remotely is 4444, so having Moon running on some host moon.example.com a working connection string will be:

var browser = await chromium.connect({ wsEndpoint: 'ws://moon.example.com:4444/playwright/chromium' });

In addition to host and port this connection URL contains playwright/chromium which tells Moon browser type to be launched. If you need to change browser type and enable additional features like video recording - you just add more parameters to the same URL as follows:

var browser = await firefox.connect({ wsEndpoint: 'ws://moon.example.com:4444/playwright/firefox?headless=false&enableVideo=true&screenResolution=1280x1024' });

To run multiple browsers in parallel — you just have to enable running tests in parallel in your test framework. This is how running tests in parallel looks like in action:

What about debugging my tests?

  • First of all we quickly install a small Kubernetes cluster on workstation. The best way to do this is using minikube — a one-command Kubernetes installation tool. Simply get this tool as shown in the docs and run one command to start Kubernetes:
$ minikube start
  • Now let’s deploy Moon for local tests development. To do this we clone and apply ready-to-use YAML manifests where Moon automated installation instructions are stored.
$ git clone https://github.com/aerokube/moon-deploy.git
$ cd moon-deploy
$ minikube kubectl -- create -f moon-local.yaml # This command will actually start Moon
$ minikube kubectl -- patch svc moon -n moon --patch "{\"spec\":{\"externalIPs\":[\"$(minikube ip)\"]}}" # This is needed for network connectivity
  • Now let’s get Playwright connection IP:
$ minikube ip
192.168.99.100

Having this IP— simply use it in your tests as follows:

var browser = await chromium.connect({ wsEndpoint: 'ws://192.168.99.100:4444/playwright/chromium' });
  • For debugging your tests you can optionally use Moon UI allowing to interact with running browser sessions. To open UI simply use this address and open in browser:
http://192.168.99.100:8080

Here is a video demonstrating how it looks like in real life:

Conclusion

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store