Behat Mink: 08 - phantomjs

PhantomJS

  •  

 

behat-phantomjs.yml

 

Example

default:
  suites:
    default:
      contexts:
      - FeatureContext
      - Drupal\DrupalExtension\Context\DrupalContext
      - Drupal\DrupalExtension\Context\MinkContext
      - Drupal\DrupalExtension\Context\MessageContext
      - Drupal\DrupalExtension\Context\DrushContext
#    TestSuiteA:
#      contexts:
#      - FeatureContext
  extensions:
    Behat\MinkExtension:
      base_url: http://unityconstruct.org/uc/
      

      #goutte: ~
      # goutte/curl will take precedence when not commented
      #   so it is commented when using phantomjs
      
      selenium2:
        # webdriver port/socket
        wd_host: "http://localhost:8643/wd/hub"
          
        # aparently has no effect when using phantomjs
        #browser: phantomjs

    Drupal\DrupalExtension:
      blackbox: ~
      # Use region_map: to narrow scope of selector search
      region_map:
        footer: ".footer-menu"
        copyright: ".copyright"
        main_menu: "#main-menu"

 

 

 

Working Out Valid PhantomJS behat.yml options

# default: profile to be used by default & can be overriden
#           with similar identifiers such as 'phantomjs:suites:default:contexts'
default:

  # suites: default: suite setting will be used but..
  #                   default:suites:TestSuiteA
  #                   default:suites:TestSuiteB
  #                   can be specified for unique settings
  #                   for each test suite
  #         Contexts seem to anchor to the specified "bootstrap:" folder
  #          and include subfolders
  #         Seems that
  #            Drupal\DrupalExtension\Context\DrupalContext
  #           maps the vendors folder:
  #             /vendor/Drupal/-somestuff-/src/DrupalExtension/Context/DrupalContext  
  suites:
    default:
      contexts:
      - FeatureContext
      - Drupal\DrupalExtension\Context\DrupalContext
      - Drupal\DrupalExtension\Context\MinkContext
      - Drupal\DrupalExtension\Context\MessageContext
      - Drupal\DrupalExtension\Context\DrushContext
#    TestSuiteA:
#      contexts:
#      - FeatureContext
#    TestSuiteB:
#      contexts:
#      - FeatureContext      


# extensions: identifier allows for importing & extending
#              the behat framework with 'use' 'extends' & 'implements'
#              the namespaces can be disorientating to work with
#              but the php class files should have something like:
#                    "namespace = Drupal"
#
#              check the /vendors folder for Drupal, Behat, etc namespaces
#              the namespace reference STARTS at the '/src' folder for each one
#              
#              still working this out 2018.1012
  extensions:
    Behat\MinkExtension:
      base_url: http://unityconstruct.org/uc/
      #base_url: 'http://saucelabs.com/'
      
      
      # goutte: if enabled, goutte will take over from phantomjs
      #goutte: ~

      #
      # FAIL: trying to see if goutte can pass to selenium2
      # goutte: selenium2
      #

      # PHANTOMJS seleinium2: NOTES ----------------
      # selenium2: ~ not sure what the ~ specifies
      #              currently using it if no other
      #              entry exists
      #selenium2: ~
      selenium2:
        # PHANTOMJS wd_host: NOTES ----------------
        #  wd_host: specifies the webdriver socket for behat to use
        #           seems like 8643 is the 'one & only' value,
        #           but is probably also set by config file somewhere
        wd_host: "http://localhost:8643/wd/hub"  
        
        # PHANTOMJS browser: NOTES ----------------
        #  browser: seems irrelevant with phantomjs running and
        #  wd_host: set to the correct port 8643
        #  and apparently "/wd/hub" endpoint for webdriver api calls
        #   ^^^^^ NOT CONFIRMED
        #       browser: chrome seemed irrelevant for phantomjs
        #       browser: chrome
        # ------------------------------------
        #browser: phantomjs

    Drupal\DrupalExtension:
      # blackbox - not sure what this specifies
      #
      blackbox: ~

      # Use region_map: to narrow scope of selector search
      region_map:
        footer: ".footer-menu"
        copyright: ".copyright"
        main_menu: "#main-menu"

# -----------------------------------------------------
# default behat.yml settings for goutte(curl) driver
#
#
# -----------------------------------------------------
#default:
#  suites:
#    default:
#      contexts:
#      - FeatureContext
#      - Drupal\DrupalExtension\Context\DrupalContext
#      - Drupal\DrupalExtension\Context\MinkContext
#      - Drupal\DrupalExtension\Context\MessageContext
#      - Drupal\DrupalExtension\Context\DrushContext
#  extensions:
#    Behat\MinkExtension:
#      goutte: ~
#      selenium2: ~
#      base_url: http://unityconstruct.org/uc/
#    Drupal\DrupalExtension:
#      blackbox: ~
#      region_map:
#        footer: ".footer-menu"
#        copyright: ".copyright"
#        main_menu: "#main-menu"


# -------------------------------------------------------------------------        
#phantomjs:
#    original from
#    https://shashikantjagtap.net/running-behat-scenarios-with-pahntomjs/
# -------------------------------------------------------------------------
#
#
#  context:
#      class:  'FeatureContext'
#  extensions:
#    Behat\MinkExtension\Extension:
#      base_url: 'http://saucelabs.com/'
#      goutte: ~
#      selenium2:
#        #wd_host: specifies the webdriver socket for behat to use
#        #           seems like 8643 is the 'one & only' value,
#        #           but is probably also set by config file somewhere
#        wd_host: "http://localhost:8643/wd/hub"
# -------------------------------------------------------------------------

 


phantomjs scripting

 

 

 

PhantomJS Core Concepts

Since PhantomJS is not usable when it comes to surfing the web, it has a whole set of features that developers love and use for many purposes.

  • Screen capture
  • Page automation
  • Network monitoring
  • Testing
  • And more...

Screen Capture

PhantomJS can be used to take screenshots of websites, those screenshots can be rendered in different formats also. Let's spin up a basic javascript script that takes screenshots of a website.

var webpage = require('webpage').create();

webpage.open('https://scotch.io/', function() {
    webpage.render('scotch.png');
    phantom.exit();
});

Running this snippet from a web-browser won't work, we need to load this script using PhantomJS. So we save this snippet in a file screenshot.js, can be anything you want to name it. Then from the command line, run.

phantomjs screenshot.js

Give it a few seconds to run and you should see a file in the same path as screenshot.js named scotch.png, open it and you should see a full page screenshot of scotch.io.

Page Automation

Because we can use PhantomJS to load and manipulate a web page, it is perfect for carrying out page automation. This helps developers run a bunch of tests without ever having to open a web browser.

Although this may not seem important, this allows us to automate any sort of interactions with a web page without having to open a browser (an operation that will save you a tremendous amount of time).

var webpage = require('webpage').create();

// open scotch.io
webpage.open('https://scotch.io', function(status) {
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var title = webpage.evaluate(function() {
            return document.title;
        });

     // log the title
        console.log(title === 'Scotch | Developers bringing fire to the people.');
    }

    phantom.exit();
});

In the evaluate() method, that's where we write the javascript we want to run on the loaded page. We can save the snippet above in a file and run phantomjs <filename>.js.

 

Network Monitoring

Because PhantomJS permits the inspection of network traffic, it is suitable to build various analysis on the network behavior and performance.

We can hook into PhantomJS during a request-response cycle and collect data about the website. This data can be reformatted and allows us to check the performance of a web page.

var page = require('webpage').create();

// hook into initial request
page.onResourceRequested = function(request) {
    console.log('Request ' + JSON.stringify(request, undefined, 4));
};

// hook to response
page.onResourceReceived = function(response) {
    console.log('Receive ' + JSON.stringify(response, undefined, 4));
};

page.open(url);

We can use tools like confess.js (a PhantomJS script) and YSlow for a more in-depth network analysis. This is useful in the sense that we can detect regression in the performance of our website before pushing the code.

Testing

This is an important aspect of software development, but developers rarely talk about. PhantomJS has been made popular as a tool for running unit tests. It can run a lot of tests and show the user the result in the command line. Testing tools like Mocha, Casper, just to mention but a few are good examples of testing with PhantomJS as these tools are based on it.

PhantomJS in the Wild

PhantomJS is used by many companies, you may have used the product and wondered how it was built. For example, Media Queries (source of inspiration for responsive websites) takes a link, checks if the website is responsive, and shows a preview of the website using different screen sizes. The site was made possible thanks to PhantomJS.

A Spanish Life uses PhantomJS to create ads based on user content. Check Out more examples of PhantomJS.

Twitter uses PhantomJS to run QUnit tests on their website.

Conclusion

This article is just an introduction to PhantomJS, in follow-up articles, we will build a screenshot taking application with PhantomJS etc. Stay tuned.