Bridge Node and PhantomJS with phridge

2014-06-19 00:00:00 +0100 by Alex R. Young

I use PhantomJS for integration testing and PDF generation, but I find working with its API awkward. That means I often put as much logic as possible into a Node wrapper before handing off the bare minimum to PhantomJS.

Johannes Ewald sent in a module that attempts to improve on this workflow: phridge (GitHub: peerigon / phridge, License: Unlicense, npm: phridge).

For comparison, here is an example of a standard PhantomJS script:

phantom.addCookie('cookie_name', 'cookie_value', 'localhost', function() {
  phantom.createPage(function(page) {
    page.set('customHeaders.Referer', 'http://google.com', function() {
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)',
        function() {
          page.open('http://localhost:9901/cookie', function (status) {
            page.evaluate(function(selector) {
              return document.querySelector(selector).innerText;
            }, function (text) {
              console.log('The element contains the following text: ', text)
            }, 'h1');

With phridge, however, you can do this:

phantom.run('h1', function(selector, resolve) {
  var page;

  // This code runs inside PhantomJS
  phantom.addCookie('cookie_name', 'cookie_value', 'localhost');

  page = webpage.create();
  page.customHeaders = {
    Referer: 'http://google.com'
  page.settings = {
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)'
  page.open('http://www.google.com', function() {
    var text = page.evaluate(function(selector) {
      return document.querySelector(selector).innerText;
    }, selector);

    // Resolve the promise and pass 'text' back to node
}).then(function(text) {
  // Inside node again
  console.log('The element contains the following text: '+ text);

The phantom object has its own API that is specific to phridge. Phridge works by starting a small HTTP server from within PhantomJS to act as an API entry point from Node. It uses a shared secret to reduce the potential for abuse -- this is documented in the readme, under a note on security.

The approach used by phridge is similar to my own homebrew solutions that have evolved over the last year or two, so I'm hoping I can replace my ad-hoc code with phridge to make my PhantomJS dependent projects more maintainable.