Tuesday, February 16, 2016

CI & CD for pure HTML+JS browser apps using Travis-CI

When its said pure HTML+JS browser app, it really mean. There is no web services or server side HTML generation involved. Its simple HTML+JS site running inside browser. People may wonder why such an browser app needs continuous integration where there is no compilation involved.

That's true that it doesn't need compilation. But it always can have tests and those tests can be run after each git push and deploy to staging or production. Many of the enterprise people will not have such a scenario of pure client app. However lets look how can we setup Continuous Integration & Continuous Delivery for such an pure HTML+JS SPA.


  • Upon git push the integration process should start
  • It should unit test the tests written using Jasmine framework.
  • If the tests are working, deploy to a FTP location in a web site.

Tools & Frameworks used

The app uses HTML5,JS and jQuery. Below is the list used for CI & CD activities.
  • Github - where the code is stored
  • Travis-CI - which provides machine to run the CI & CD activities
  • Jasmine - Test Framework API
  • Karma - Test runner
  • PhantonJS - The headless browser where Karma can run the tests
  • NodeJS
  • NPM Modules other than required by above
    • ftp-deploy


Do not confuse with the usage of Node.JS. We are not going to run anything in server, its pure HTML+JS SPA. NodeJS is just to do npm package management and to upload to FTP for deployment. The test runner Karma which we are going to use comes as npm package. Also this is the language type supported in Travis-CI for pure HTML+CSS+JavaScript projects.


This assumes the understanding of
  • Basic web development using HTML+JS+CSS (browser side alone)
  • Using Github.
  • Understanding about NodeJS and its package management ecosystem npm.

Setting up source - production & test code


The application which we are going to use is HTML5 based Karel simulator. Instead of creating separate branch inside it's git repository, better we can create another repository named travis-ci-test in Github itself and uploaded the same source there. /Src is the folder which contains source code.


There is no question that the leader in client side web testing is Jasmine framework. This provides the API to write test code. Since this is not a post to discuss about Jasmine framework, lets go to Jasmine test code directly. The test JavaScript files can reside inside in the /Tests folder.

Running tests using SpecRunner.html

Jasmine is a JavaScript test framework written in JavaScript and run tests inside browser. For that we just need to create a html file and refer Jasmine js files, app/production js files and test scripts. In this case a SpecRunner.html is available in below location. Browsing it will run the tests and show the results.

Make sure the tests are running from SpecRunner.html. It is always recommended to do a big job in small tasks which can be done in less than 1 hour.

Setting up NodeJS & NPM

The above setup will work really great in our machine. But when in CI environment, we need to run the tests from program. Travis-CI doesn't have client side only JavaScript+HTML app support. But it has node_js support which we can leverage here to test our app.

Next step is to add some Node.JS things. Basically we need to convert our folder to Node.JS application. We can create/convert a Node.JS app by simply issuing 'npm init' command, from any command prompt inside our folder. It will ask for some questions and will create package.json file.

Introducing Karma & PhantomJS

Once our folder is npm enabled or in other sense we got a package.json, we can install packages. Karma is a test runner program which can run our Jasmine tests we wrote in previous step. The JavaScript needs a engine to execute the script and Karma is not a js engine. So it needs a js engine. It can either use any browser itself or can use headless browser called PhantomJS. PhantomJS comes as npm package. All the packages mentioned till here can be installed via npm. See the devDependencies section in package.json file for the packages what we need.

In our local machine we can use npm install <package name> --user-dev command to install the packages. But in CI machine, it will do automatically by reading the package.json.

Setting up Karma & PhantomJS

Karma wants to know where are the production & test js files to run tests. For that it needs a config file. In this case, its  karma.config.js

This file can be created using karma init command. It will ask some questions and create the file. This file is self explanatory.

How npm knows Karma is the test runner to be used

Once the setup is done we are going to execute the tests by issuing below command.

npm test

How does npm knows to run the tests using Karma? Its again configuration inside package.json.

There is a section called 'Scripts' and it contains 'test' section inside. Test node expects an executable command. There we can give the command to run Karma along with karma config we created in previous step.

Once this step is proper we should be able to issue 'npm test' command and it should should run the tests and show results. Testing every step is important.

Upload to FTP via ftp-deploy npm module

Now comes to deployment. If all the tests are passed, we can deploy to our staging or production environment. In this case I want to deploy to a FTP location. The hosted CI platform Travis-CI which we are going to use, doesn't have direct support to deploy a folder to FTP. We had to rely on curl command which can transfer one file at a time. One file at a time doesn't work most of the time.

Now there are 2 ways
  • Use tools such as grunt / gulp which can orchestrate the CI activities. They have FTP deploy methods.(Google knowledge. Didn't try myself)
  • We can write some code in shell script or NodeJS and hook it after the tests run successfully.
As there are already enough tools and technologies just for CI & CD than the production code, lets write some custom NodeJS code to upload files to FTP. No to Grunt and Gulp for now.

The written FTP upload code uses ftp-deploy npm package to upload the files. Why should we rewrite code again for reading the folders, reading the files and to upload using FTP protocol. That is something many people already solved and made open source.

The FTP code can be hooked to the integration process after test by editing the package.json file. We can use 'posttest' section for it.

Testing everything in local machine

If the above environment is ready, we should be able to run everything in our machine. The command 'npm test' should run the unit tests and if everything pass, deploy to FTP folder. In our local environment to work we need to give the FTP credentials to ftpupload script via environment variables. We will see why its via environment variables in next section.

Intro to Travis-CI and getting started with it

Travis-CI is a free hosted CI&CD SaaS environment for open source projects. This is similar to AppVeyor. We could have used AppVeyor which is already referred in previous posts and have already used in other .Net projects. But this time lets try something new. Below are the steps to achieve the same.

  • Upload the code to Github.
  • Create account in Travis-CI using Github.
  • Connect to Github project.
  • Setup below environment variables for FTP access.
    • ftp_host
    • ftp_user
    • ftp_password - Do not turn the flag to display in build log. Build log is public
    • ftp_localPath - Path to the folder inside the CI machine. Usually src
    • ftp_remotePath - Path to where the files should be copied. Only relative url
Refer a build log for sample values. Once this is working. Just do a push to Github to start the integration process.

UI testing in Travis-CI

Travis-CI don't support running in chrome out of the box. They are telling we need to give some commands to get it running. To me it didn't work out. It tried 2-3 times and said failed to start Chrome. So decided to use PhantomJS. Anyway in this sample application, the main objective is test the JavaScript. So it works.

If there are any steps missing and difficult to follow just fork / download the sample. There is no good documentation than the code itself.



No comments: