DEV Community

Dennis Whalen for Leading EDJE

Posted on • Edited on

1

E2E Testing - Pulling Strings with Puppeteer

On a recent QA automation assignment my team needed to quickly build and deploy some basic UI smoke tests for an enterprise web application. After some discussion we decided to go with Puppeteer. This is my first exposure to Puppeteer and I want to share a little of what I've learned so far.

So what is Puppeteer? Puppeteer is an open source Node library that provides a high-level API which allows an automation developer to drive the browser via the Dev Tool Protocol.

The first step to exploring the features of Puppeteer is to get it installed, so let's get started!

Puppeteer setup

 npm i puppeteer
Enter fullscreen mode Exit fullscreen mode

And there you go! Once you successfully install puppeteer you have also downloaded the version of Chromium that is guaranteed to work with the installed Puppeteer APIs.

If you don't want the overhead of that download and want to test with an existing install of Chrome, you can install puppeteer-core instead. Just be sure the browser version you plan to connect to is compatible with the version of Puppeteer you're installing, which is found in the Puppeteer package.json file.

Taking a screenshot

We're now ready to to create our first test, and we'll start with something basic. For this test we'll open the browser, navigate to the Leading EDJE home page, save a screenshot of the page, and close the browser.

Create a new folder for your tests, and then create new file named screenshot.js:

const puppeteer = require('puppeteer');

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.setViewport({ width: 1680, height: 1050 })
 await page.goto('http://leadingedje.com', {waitUntil: 'networkidle2'});
 await page.screenshot({path: 'le-screenshot.png'});
 await page.pdf({path: 'le-screenshot.pdf'});

 await browser.close();
})();
Enter fullscreen mode Exit fullscreen mode

If you're familiar with other UI automation frameworks, this all probably looks familiar. We open the browser, override the default resolution of 800x600, navigate to the page, capture the screenshot, then close the browser. We are also taking a screenshot in both PNG and PDF format, with just 2 lines of code.

That's the code, so now let's run it!

node screenshot.js 
Enter fullscreen mode Exit fullscreen mode

If this runs successfully, you should see no errors on the command line, and new files created named le-screenshot.png and le-screenshot.pdf. Open the PDF file and notice the full page is captured.

What you won't see is the browser opening. That's because by default Puppeteer run headless, which is necessary when running as an automated CI process. If you want to see the browser in action, simply set the headless option when launching the browser:

const browser = await puppeteer.launch({headless: false});
Enter fullscreen mode Exit fullscreen mode

Google search automation

Let's create another test and name it google.js:

const puppeteer = require('puppeteer');
const { expect } = require('chai');

// puppeteer options
const opts = {
 headless: false,
 slowMo: 100,
 timeout: 10000
};

(async () => {
 const browser = await puppeteer.launch(opts);
 const page = await browser.newPage();
 await page.setViewport({ width: 1680, height: 1050 })
 await page.goto('https://www.google.com', {waitUntil: 'networkidle2'});
 await console.log('search page loaded');

 const searchTextbox = await page.waitFor('input[name=q]');

 await searchTextbox.type('meeseek');
 await page.keyboard.press('Enter');

 const [response] = await Promise.all([
 page.waitForNavigation(),
 page.once('load', () => console.log('meeseek results page loaded'))
 ]);

 expect(await page.title()).to.contain('Google Search');

 await page.screenshot({path: 'meeseek.png'});

 await browser.close();
})();
Enter fullscreen mode Exit fullscreen mode

With this test we are navigating to google.com, performing a search, waiting for the results, and validating the results page title.

In addition, we are slowing the test by 100ms for each operation by using the sloMo option when launching the browser. This can be useful if you have a fast running test and want to be sure to see all the browser interactions.

We've also set the timeout to 10000ms. Any test that test longer than 10 seconds will fail.

Performance Tracing

For our last example we're going to step away from basic UI automation and use Puppeteer to capture performance trace information.

The Performance tab in Chrome dev tools allows you to records critical browser performance metrics as you navigate through your website. With these metrics you can troubleshoot performance issues by analyzing what Chrome is doing under the hood to render your site.

We are going to modify our Google example a bit to automatically capture a trace file during the automated test. From there we can load that trace file into Chrome dev tools and see what's really happening during our test.

Create a new file names trace.js:

const puppeteer = require('puppeteer');

// puppeteer options
const opts = {
 headless: false
};

(async () => {
 const browser = await puppeteer.launch(opts);
 const page = await browser.newPage();
 await page.setViewport({ width: 1680, height: 1050 })

 await page.tracing.start({path: 'trace.json',screenshots:true});

 for (i = 0; i < 10; i++) { 
 await page.goto('https://www.google.com', {waitUntil: 'networkidle2'});

 await console.log('search page loaded');
 const searchTextbox = await page.$('input[type=text]');
 await searchTextbox.type('meeseek box');
 await page.keyboard.press('Enter');

 await Promise.all([
 page.once('load', () => console.log('meeseek results page loaded'))
 ]);

 await page.screenshot({path: 'meeseek.png'});
 }

 await page.tracing.stop();

 await browser.close();
})();
Enter fullscreen mode Exit fullscreen mode

For this test we are looping through our Google search 10 times, but more importantly we are starting a trace prior to the automation with the line:

await page.tracing.start({path: 'trace.json',screenshots:true});&nbsp;
Enter fullscreen mode Exit fullscreen mode

With this line of code we'll be creating a trace.json file of the entire automated session, including screen prints. From there we can load that file into Chrome dev tools and manually troubleshoot, or automate further by parsing the trace file programmatically and proactively identifying performance issues.

Here's what the trace file looks like when I manually load it into Chrome:

Alt Text

Conclusion

Although Puppeteer provides functionality similar to Selenium, it is not meant as a replacement. Selenium provides a single common API for performing browser automation across all major browsers. Puppeteer targets only Chrome and Chromium, and it's strengths include a broader set of services, and an event-driven architecture that allows for less test flakiness and failures.

Feel free to take a look at my github project that contains all of these examples. Give Puppeteer a test drive and make Chrome dance!


Smart EDJE Image

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)

Billboard image

Try REST API Generation for Snowflake

DevOps for Private APIs. Automate the building, securing, and documenting of internal/private REST APIs with built-in enterprise security on bare-metal, VMs, or containers.

  • Auto-generated live APIs mapped from Snowflake database schema
  • Interactive Swagger API documentation
  • Scripting engine to customize your API
  • Built-in role-based access control

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay