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:

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:

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.
 

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.


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:

export SAUCE_USERNAME=********
export SAUCE_ACCESS_KEY=****************************
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 ../..
npm install phantomjs
time npm install -g protractor
time npm install -g grunt-cli bower
time npm install
grunt
kill %1

 

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:

Exit mobile version