Software Development

JavaScript multi module project – Continuous Integration

JavaScript multi module project

Few days ago, I wrote blog post about JavaScript multi module project with Grunt. This approach allows you to split application into various modules. But at the same time it allows you to create one deployment out of these modules. It was inspired by Maven multi module concept (Maven is build system from Java world).

But configuration of the project is just a half of the puzzle. Testing is must for me. And tests have to be executed. Execution must be automatic. So I needed to figure out Continuous Integration story for this project configuration.
 

Project parameters to consider

Let me quickly summarize base attributes of project configuration:

  • Two Github repositories/sub-projects
  • One main project called primediser
    • builds sub-projects
    • gathers deployment
    • runs Protractor end-to-end tests against deployment
    • gathers code coverage stats for client side code

Choosing CI server

First of all I had to pick CI server. Obviously it has to support chaining of builds (one build would be able to kick off another build). I have Java background and experience with Jenkins. So it would be natural choice. I bet that Jenkins can handle it quite easily. But I wanted to try workflow closer to majority of JavaScript community. So I tried TravisCI. Straight away not suitable because it’s not possible to chain builds.

Next I tried drone.io. Relatively new service. Initially seemed not very feature reach. But closer look actually showed that this is the right choice. It provides these features necessary for me:

  • Temporary Docker Linux instance (is deleted after build) with pre-installed Node.JS
  • Web hook for remote triggering builds
  • Web interface for specifying Bash build commands
  • Web interface for specifying private keys or credentials needed during build

This combination of features turned to be very powerful. There are few unpolished problems (e.g. visualization of build process makes Firefox unresponsive, missing option for following build progress – so need to scroll manually), but I can live with these.

I was also able to execute Protractor tests against Sauce Labs from drone.io and measure the code coverage. Measuring Protractor tests code coverage is described in separate blog. Any change against sub-project triggers also build of main project. Does this sound interesting? Let me describe this CI configuration in details.

Documentation

I wouldn’t dive into drone.io, Sauce Labs or Protractor basic usage. They were new for me and I easily and quickly came through their docs. Here is list of docs I used to put together this CI configuration.

Protractor – Souce Labs integration

Important part of this setup is Protractor integration with Sauce Labs. Sauce Labs provide Selenium server with WebDiver API for testing. Protractor uses Sauce Labs by default when you specify their credentials. So credentials are the only special configuration in test/protractor/protractorConf.js (bottom of the snippet). Other configuration was taken from grunt-protractor-coverage example. I am using this grunt plug-in for running Protractor tests and measuring code coverage.

// A reference configuration file.
exports.config = {
  // ----- What tests to run -----
  //
  // Spec patterns are relative to the location of this config.
  specs: [
    'test/protractor/*Spec.js'
  ],

  // ----- Capabilities to be passed to the webdriver instance ----
  //
  // For a full list of available capabilities, see
  // https://code.google.com/p/selenium/wiki/DesiredCapabilities
  // and
  // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js
  capabilities: {
    'browserName': 'chrome'
    //  'browserName': 'firefox'
    //  'browserName': 'phantomjs'
  },
  params: {
  },
  // ----- More information for your tests ----
  //
  // A base URL for your application under test. Calls to protractor.get()
  // with relative paths will be prepended with this.
  baseUrl: 'http://localhost:3000/',


  // Options to be passed to Jasmine-node.
  jasmineNodeOpts: {
    showColors: true, // Use colors in the command line report.
    isVerbose: true, // List all tests in the console
    includeStackTrace: true,
    defaultTimeoutInterval: 90000

  },
  
  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY
};

You may ask now, how can I use localhost in the configuration, when remote selenium server is used for testing. Good question. Sauce Labs provide very useful feature called Sauce Connect. It is a tunnel that emulates access to your machine from Selenium server. This is super useful when you need to bypass company firewall. It will be used later in main project CI configuration.

Setting up CI for sub-projects

The idea is that each change in sub-project would trigger a build of main project. That is why we need to copy a Build Hook of main project from Settings -> Repository section.
 
primediser-hook

Main project kicked off by hitting the Web hook via wget Linux command from sub-project. As you can see in following picture, sub-project informs main project about a change and process its own build afterwards. Drone.io doesn’t provide concurrent builds (not sure if this is limitation of open source projects only), so main project will wait for sub-project’s build to finish. After that main project is build.

primediser-client
CI Configuration of Main Project

So build of main project is now triggered from sub-projects. Main project configuration is slightly more complicated. I uses various commands:

  • First of all we need to specify Sauce Labs credentials in Environment Variables section
export SAUCE_USERNAME=********
export SAUCE_ACCESS_KEY=****************************
  • Download, extract and execute Sauce Connect tunnel for Linux. This will make accessible some ports on build image for Selenium testing. Notice that execution of the tunnel is done with & (ampersand). This means that tunnel execution is done in forked process and current console can continue executing build.
wget https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-latest-linux.tar.gz
tar -xzvf sc-latest-linux.tar.gz
cd sc-4.2-linux/bin
./sc &
cd ../..
  • Now standard Node.JS/Grunt build workflow is executed. Downloading and installing all NPM dependencies creates enough time for Sauce Connect tunnel to start.
npm install phantomjs
time npm install -g protractor
time npm install -g grunt-cli bower
time npm install
grunt
  • Last command is closing the Sauce Labs tunnel after build, so that Docker build image wouldn’t hang.
kill %1

 
primediser

Such configured job now takes 9 minutes. drone.io limit is 15 minutes. Hopefully there will be enough space for all end-to-end tests I am going to create. If you are curious about build output/progress, take a look at my drone.io jobs below.

Project Links

Github repositories:

Drone.io jobs:

Lubos Krnac

Lubos is a Java/JavaScript developer/Tech Lead and Automation enthusiast. His religion is to constantly improve his developments skills and learn new approaches. He believes TDD drives better design and nicely decoupled code. Past experience includes C++, Assembler and C#.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button