Headless Chrome started shipping with Google Chrome from version 59 onwards. It brings all modern web platform features provided by Chromium and the Blink rendering engine to the command line. Today, Headless Chrome is one of the most preferred options for interacting with the browsers through the command line or code for automated testing, web-scraping SPAs, performance tracking and visual analysis.

Later in the post, I also cover how Heimdall Bot uses Headless Chrome, Puppeteer and Flock API to send our internal Service Performance Monitoring (SPM) Kibana dashboard screenshots every morning judiciously to the dev team.

Headless Chrome

Working with Headless Chrome is simple. Here are quick and simple examples from their project page:

Print to PDF:

$ chrome --headless --disable-gpu --print-to-pdf https://www.deltax.com/

Taking a screenshot:

chrome --headless --disable-gpu --screenshot --window-size=1280,800 https://www.deltax.com

This would take a screenshot after the page is loaded with a viewport of 1280x800 and save it in a file screenshot.png in the local directory.

Here are some use-cases for Headless Chrome:

  • Generate screenshots and PDFs of pages.
  • Crawl an SPA and generate pre-rendered content (i.e. “SSR”).
  • Automate form submission, UI testing, keyboard input, etc.
  • Create an up-to-date, automated testing environment.
  • Capture a timeline trace of your site to help diagnose performance issues.

Headless Chrome provides varied levels of APIs to interact with it programmatically namely Puppeteer and CRI (Chrome Remote Interface) Library.

Puppeteer

Puppeteer is a Node library developed by the Chrome team. It provides a high-level API to control headless Chrome and also interact with the DevTools protocol. In short, Puppeteer makes Headless Chrome accessible to millions of developers to build their own applications.

Here is a simple example of taking a screenshot:

const puppeteer = require('puppeteer');

let date_string = new Date(Date.now()).toLocaleDateString();

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // set viewport
  await page.setViewport({width: 1280, height: 800});
  
  await page.goto('http://www.deltax.com', {
    // wait until page is loaded
    waitUntil: 'networkidle0'
  });
  
  // take screenshot of the full page (by default it's only of the viewport)
  await page.screenshot({path: 'snapshot.png', fullPage: true});

  await browser.close();
})();

Heimdall Bot

Heimdall is our ELK-based Service Performance Monitoring service. Although, for the last few months we have been on top of our dashboards every single day; it would be amazing to have snapshots of those dashboards available on email and Flock which we use as our primary internal collaboration tool. Heimdall Bot simply does this in style.

Here are the moving parts:

1. Node.js Puppeteer script which takes the screenshot
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // set viewport
  await page.setViewport({width: 1280, height: 800});
  
  await page.goto('http://link-to-kibana-dashboard?embed=true', {
    // wait until page is loaded
    waitUntil: 'networkidle0'
  });

  // take screenshot of the full page (by default it's only of the viewport)
  await page.screenshot({path: 'snapshot.png', fullPage: true});

  await browser.close();
})();
2. CRON job which acts as a glue to publish the screenshot to S3 and sends a message on Flock

Here is how the cron config looks like:

# Take the screenshot
0 5 * * * /home/ubuntu/.nvm/versions/node/v7.6.0/bin/node /home/ubuntu/kibana/snapshot.js >> /home/ubuntu/kibana/snapshot.log 2>&1
# Push to S3
10 5 * * * s3cmd put /home/ubuntu/kibana/snapshot.png s3://public-s3-bucket --acl-public
# Post Flock message
11 5 * * * /home/ubuntu/kibana/snapshot.sh

Post Flock message - snapshot.sh

curl -X POST https://api.flock.com/hooks/sendMessage/POST-URL -H "Content-Type: application/json" -d  "{\"text\": \"I am Heimdall, all-seeing and all-hearing guardian sentry of DeltaX! Take a few mins to see what I'm seeing in the last 24 hours :)\",\"attachments\": [{\"views\": {\"image\": {\"original\": { \"src\": \"http://public-s3-bucket/snapshot.png?rnd=$RANDOM\", \"width\": 1280, \"height\": 910 }}}}]}"

Here is how the message looks like:

Heimdall Bot - message

The complete code is available on our internal Bitbucket project page. It would be interesting to add support for labels in Heimdall Bot so that we can push relevant snapshots to the respective teams.

Headless Chrome has opened up a plethora of possibilities. What’s possible on the browser now is also possible through code.