Continuous Integration for front-end JavaScript Lars Thorup ZeaLake Software Consulting April, 2013
Who is Lars Thorup? ● Software developer/architect ● JavaScript, C# ● Test Driven Development ● Continuous Integration ● Coach: Teaching agile and automated testing ● Advisor: Assesses software projects and companies ● Founder of ZeaLake
Continuous Integration Code CI-server Results function createBoard() { ... } static analysis errors test runner Tests test('createBoard', { ... coverage coverage analysis }); minification distributables
GruntJS ● Command line ● Good support for ● NodeJS ● RequireJS ● CoffeeScript ● Static analysis ● JSHint ● Lots of other plugins ● Run tests in PhantomJS ● Popular and actively ● QUnit developed ● Jasmine ● and others ● Code Coverage ● Istanbul ● Blanket
src/test/code.test.js describe('durationInEnglish', function () { it('should return "now" when duration is 0', function () { expect(durationInEnglish(0)).toBe('now'); }); it('should return "x seconds ago" when ...', function () { var now = new Date(2013, 04, 19, 11, 00, 17); var then = new Date(2013, 04, 19, 11, 00, 00); expect(durationInEnglish(now - then)).toBe('17 seconds ago'); }) });
src/js/code.js function durationInEnglish(milliseconds) { var seconds = milliseconds / 1000; if(seconds === 0) { return 'now'; } else if(seconds < 60) { return seconds + ' seconds ago'; } else { return 'whenever'; } }
package.json { "name": "gruntdemo", "version": "0.1.1-1", "devDependencies": { "grunt-cli": "0.1.6", "grunt": "0.4.1", "grunt-contrib-jshint": "~0.1.1", "grunt-contrib-jasmine": "0.4.1", "grunt-template-jasmine-istanbul": "0.2.0" } } >npm install
Gruntfile.js module.exports = function (grunt) { var gruntConfig = {}; // task definitions ... // grunt grunt.initConfig(gruntConfig); };
Gruntfile.js - jshint grunt.loadNpmTasks('grunt-contrib-jshint'); gruntConfig.jshint = { all: [ '*.js', 'src/**/*.js' ] }; >grunt jshint Running "jshint:all" (jshint) task >> 3 files lint free.
Gruntfile.js - jasmine grunt.loadNpmTasks('grunt-contrib-jasmine'); gruntConfig.jasmine = { src: { src: [ 'src/js/**/*.js' ], options: { specs: 'src/test/**/*.test.js', junit: { path: 'output/testresults' } } } >grunt jasmine:src }; Running "jasmine:src" (jasmine) task Testing jasmine specs via phantom .. 2 specs in 0.109s. >> 0 failures
Gruntfile.js - istanbul gruntConfig.jasmine.istanbul = { src: gruntConfig.jasmine.src.src, options: { specs: gruntConfig.jasmine.src.options.specs, template: require('grunt-template-jasmine-istanbul'), templateOptions: { coverage: 'output/coverage/coverage.json', report: [ {type: 'html', options: {dir: 'output/coverage'}}, {type: 'text-summary'} ] } >grunt jasmine:istanbul Running "jasmine:istanbul" (jasmine) task } Testing jasmine specs via phantom }; .. ========== Coverage summary =========== Statements : 85.71% ( 6/7 ) Branches : 75% ( 3/4 ) Functions : 100% ( 1/1 ) Lines : 85.71% ( 6/7 ) =======================================
Coverage report
Coverage details
Jenkins job
Jenkins report

Continuous Integration for front-end JavaScript

  • 1.
    Continuous Integration for front-endJavaScript Lars Thorup ZeaLake Software Consulting April, 2013
  • 2.
    Who is LarsThorup? ● Software developer/architect ● JavaScript, C# ● Test Driven Development ● Continuous Integration ● Coach: Teaching agile and automated testing ● Advisor: Assesses software projects and companies ● Founder of ZeaLake
  • 3.
    Continuous Integration Code CI-server Results function createBoard() { ... } static analysis errors test runner Tests test('createBoard', { ... coverage coverage analysis }); minification distributables
  • 4.
    GruntJS ● Command line ● Good support for ● NodeJS ● RequireJS ● CoffeeScript ● Static analysis ● JSHint ● Lots of other plugins ● Run tests in PhantomJS ● Popular and actively ● QUnit developed ● Jasmine ● and others ● Code Coverage ● Istanbul ● Blanket
  • 5.
    src/test/code.test.js describe('durationInEnglish', function() { it('should return "now" when duration is 0', function () { expect(durationInEnglish(0)).toBe('now'); }); it('should return "x seconds ago" when ...', function () { var now = new Date(2013, 04, 19, 11, 00, 17); var then = new Date(2013, 04, 19, 11, 00, 00); expect(durationInEnglish(now - then)).toBe('17 seconds ago'); }) });
  • 6.
    src/js/code.js function durationInEnglish(milliseconds){ var seconds = milliseconds / 1000; if(seconds === 0) { return 'now'; } else if(seconds < 60) { return seconds + ' seconds ago'; } else { return 'whenever'; } }
  • 7.
    package.json { "name": "gruntdemo", "version": "0.1.1-1", "devDependencies": { "grunt-cli": "0.1.6", "grunt": "0.4.1", "grunt-contrib-jshint": "~0.1.1", "grunt-contrib-jasmine": "0.4.1", "grunt-template-jasmine-istanbul": "0.2.0" } } >npm install
  • 8.
    Gruntfile.js module.exports =function (grunt) { var gruntConfig = {}; // task definitions ... // grunt grunt.initConfig(gruntConfig); };
  • 9.
    Gruntfile.js - jshint grunt.loadNpmTasks('grunt-contrib-jshint'); gruntConfig.jshint = { all: [ '*.js', 'src/**/*.js' ] }; >grunt jshint Running "jshint:all" (jshint) task >> 3 files lint free.
  • 10.
    Gruntfile.js - jasmine grunt.loadNpmTasks('grunt-contrib-jasmine'); gruntConfig.jasmine = { src: { src: [ 'src/js/**/*.js' ], options: { specs: 'src/test/**/*.test.js', junit: { path: 'output/testresults' } } } >grunt jasmine:src }; Running "jasmine:src" (jasmine) task Testing jasmine specs via phantom .. 2 specs in 0.109s. >> 0 failures
  • 11.
    Gruntfile.js - istanbul gruntConfig.jasmine.istanbul= { src: gruntConfig.jasmine.src.src, options: { specs: gruntConfig.jasmine.src.options.specs, template: require('grunt-template-jasmine-istanbul'), templateOptions: { coverage: 'output/coverage/coverage.json', report: [ {type: 'html', options: {dir: 'output/coverage'}}, {type: 'text-summary'} ] } >grunt jasmine:istanbul Running "jasmine:istanbul" (jasmine) task } Testing jasmine specs via phantom }; .. ========== Coverage summary =========== Statements : 85.71% ( 6/7 ) Branches : 75% ( 3/4 ) Functions : 100% ( 1/1 ) Lines : 85.71% ( 6/7 ) =======================================
  • 12.
  • 13.
  • 14.
  • 15.