loading...

Hey look, it's Playwright, like Puppeteer but with more browser (support)

evanhalley profile image Evan ・4 min read

I've written extensively about the many things you can do with Puppeteer. A deal breaker for some workflows, like automated testing + coverage, is that it only officially supports Google Chrome & Chromium browsers (with some experimental Firefox support sprinkled in). If you needed browser diversity in your automated modern workflow, your choices were limited. Recently, your options open up a bit.

The developers who worked to bring Puppeteer to life are the ones behind a very similar framework with official support for Chrome(mium), Webkit, and Firefox browsers on Linux, macOS, and Windows.

Playwright is a Node library to automate the Chromium, WebKit and Firefox browsers with a single API. It enables cross-browser web automation that is ever-green, capable, reliable and fast.
Our primary goal with Playwright is to improve automated UI testing by eliminating flakiness, improving the speed of execution and offering insights into the browser operation.

I want to take the time to step through getting all set up and running in 5 minutes or less, then leverage it's cross-browser-ness.

Install

The following snippet will bootstrap a Nodejs project and download Playwright and it's dependencies.

mkdir hello-playwright
cd hello-playwright
npm init // accept all of the defaults
npm install --save playwright
touch index.js

Take Screenshots

Let's do an easy thing and see what this website, evanhalley.dev looks like in Chromium, Firefox, and Webkit. Open index.js and enter the following:

const playwright = require('playwright');

(async () => {
    for (const browserType of ['chromium', 'firefox', 'webkit']) {
        const browser = await playwright[browserType].launch();
        const context = await browser.newContext();
        const page = await context.newPage('https://evanhalley.dev/');
        await page.screenshot({ path: `evanhalleydev_${browserType}.png` });
        await browser.close();
    }
})();

Run the script with node index.js. After the script finished, you should have three images in the current directory showing you what this website looks like in 3 different browsers.

With Playwright, iterating through the three browser types is pretty easy, 'chromium', 'firefox', 'webkit'. This can be pretty powerful for those looking to quickly generate screenshots of their web app or to validate their UI designs across different browsers.

One difference between Puppeteer and Playwright deals with how new pages are created. In Puppeteer:

const page = await browser.newPage();
const response = await page.goto('https://evanhalley.dev');

Creates a new page in the default context of the browser. What's not immediately apparent is that all pages created this way will share their cookies and cache.

With Playwright, the first thing you need to do is to create a BrowserContext.

Playwright allows creation of "incognito" browser contexts with browser.newContext(). "Incognito" browser contexts don't write any browsing data to disk.

const context = await browser.newContext();
const page = await context.newPage('https://evanhalley.dev/');

There is no way to create a page as part of the default browser context with Playwright, which is important for making sure you start off with a blank slate when running your automated tests.

Execute a Google Search

Here's how I executed a Google Search in my first Puppeteer article "Getting Started with Puppeteer".

let searchBox = await page.$('#tsf > div:nth-child(2) > div > div.RNNXgb > div > div.a4bIc > input');
let searchButton = await page.$('#tsf > div:nth-child(2) > div > div.FPdoLc.VlcLAe > center > input[type=submit]:nth-child(1)');
await searchBox.type('Avengers Endgame reviews NO SPOILERS!');
await searchButton.click();

What about Playwright?

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

(async () => {
    // get a browser, context, and page objects...also start Firefox with a "head" so we can see what's happening
    const browser = await firefox.launch({ headless: false });
    const context = await browser.newContext();
    const page = await context.newPage('https://google.com');

    // execute the search, the CSS selectors have changed since my first Puppeteer article
    const searchBox = await page.$('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input');
    const searchButton = await page.$('#tsf > div:nth-child(2) > div.A8SBwf > div.FPdoLc.tfB0Bf > center > input.gNO89b');
    await searchBox.type('Weather in Greensboro, North Carolina');
    await searchButton.click();

    // wait for the page to finish navigating before taking a screenshot
    await page.waitForNavigation();
    await page.screenshot({ path: 'a-google-search-with-firefox.png' });
    await browser.close();
})();

Emulating an iPhone 11 Pro

Having programmatic access to different browsers means we can emulate certain conditions, like a Webkit browser with the iPhone 11 Pro's device characteristics. Here I want to see how many requests are executed when the New York Times thinks its loading it's homepage on an iPhone versus a desktop browser.

const { webkit, devices } = require('playwright');
const iPhone11 = devices['iPhone 8 Pro'];

(async () => {
    const devicesToUse = [ iPhone11, null ];

    for (let i = 0; i < devicesToUse.length; i++) {
        const browser = await webkit.launch();
        let context; 

        if (devicesToUse[i]) {
            context = await browser.newContext({
                viewport: iPhone11.viewport,
                userAgent: iPhone11.userAgent
            });
        } else {
            context = await browser.newContext();
        }
        const page = await context.newPage();
        await page.setRequestInterception(true);
        let numRequests = 0;
        page.on('request', request => {
            numRequests++;
            request.continue();
        });
        await page.goto('https://nytimes.com');
        console.log(numRequests);
        await browser.close();
    }
})();

Outro

If you've used Puppeteer in the past, this all looks really familiar, and thats a good thing. The API and capabilities are similar, but now you can use other browsers. Playwright is still pre-1.0 so expect a lot of changes going forward.

Playwright is being actively developed as we get to the feature parity across Chromium, Firefox and WebKit. Progress on each browser can be tracked on the Is Playwright Ready? page, which shows currently failing tests per browser.

✌🏿

(originally posted at EvanHalley.dev)

Posted on by:

evanhalley profile

Evan

@evanhalley

I really love solving fun and interesting problems with software. I am a full-time software developer with over a decade of experience doing a bit of everything…Android, web, backend, scripting, etc.

Discussion

pic
Editor guide
 

Nice article! If anyone is looking to try this out in a cloud environment, we've been building a service where you can connect your Playwright scripts to browsers running in our cloud: headlesstesting.com

The advantage is that you can scale your scripts to run multiple sessions simultaneously, without running these browsers on your own infrastructure.

 

Why isn't it enough to just test in Chrome? Are there major differences in how browsers execute code?

 

Great question. The differences matter depending on your particular website or web app.

An example, here is how well browsers support the lazy load attribute for images

caniuse.com/#feat=loading-lazy-attr

(IE and Chrome support, Firefox does not as of 2/17).