nock – Mocking HTTP requests in NodeJs

When writing unit tests for a NodeJs app you sometimes want to mock out HTTP requests. Nock (https://github.com/pgte/nock) is a really nice lib for doing just that.

It’s installed in the usual way with npm:


npm install nock

and can be included in a test like this:


var nock = require('nock');

Now, let’s say, you want to mock out an API call to http://www.example.com/api/product/15, expecting some basic product data as a result:


nock('http://example.com')
.get('/api/product/15')
.reply(200, {
    data: {
        id: '15',
        name: 'Flamethrower 5000',
        price: '5000'
    }
});

You can put this, for example, in the ‘beforeEach’-block of a mocha or jasmine test.

The basic example above will get you pretty far, but as you can read in the excellent docs (https://github.com/pgte/nock), you can do all kinds of fancy stuff with nock such as:

* get / post / put / delete
* custom headers for requests and responses
* http and https
* response repetition
* chaining
* filtering & matching (e.g.: http://www.example.com/api/*)
* logging
* recording (this is really cool for complex responses, you can record and then play back http requests and responses)
* … much much more!

Pretty much everything you’ll ever use in your applications, you will be able to mock out with a clean and simple syntax. Really cool.

Have fun nocking :)

~m

ES6 Harmony Quick Development Setup

ECMAScript 6, also called “harmony” is the next version of JavaScript and it’s packed with all kinds of awesome improvements and fancy features. (GitHub page for ES6 features).

Unfortunately, harmony is not out yet and is in fact still a work in progress. There are some features already implemented in the newest versions of Chrome and Firefox, but it will be some time before we will be able to enjoy the full range of features available within the proposal. There is, howevery, a way to test harmony right now using the magical technique of transpiling, i.e. compiling JS files written in ES6 back to ES5 so they can be executed in all of today’s browsers as well as in NodeJS.

This post will show an easy way to get a development setup running for ES6 with tests and all using Google’s traceur compiler (traceur-compiler). There is also a grunt-task for this compiler, which makes it easier to use (grunt-traceur).

I expect you have npm, grunt and grunt-cli already installed, there are lots of tutorials on the web on how to do this.

The first step is to install grunt-traceur using

npm install grunt-traceur

Then, create a Gruntfile.js and fill it with the following contents:


module.exports = function(grunt) {
    grunt.initConfig({
        traceur: {
            options: {
                'blockBinding': true 
            },
            custom: {
                files: {
                    'build/all.js': ['js/**/*.js'] 
                } 
            }
        }
    });

    grunt.loadNpmTasks('grunt-traceur');
};

The ‘blockBinding’ flag enables the use of ‘let’ and ‘const’, the new block-scoped variable definition mechanisms.

Now, create a ‘js’ folder where we will put all of our JavaScript files. If you execute

grunt traceur

on your shell will take all .js files within the ‘js’ folder and compile them into the ‘all.js’ file within the build folder.

Important Note: If you want to use the compiled ‘all.js’ file, you need to include the traceur-runtime.js before including the compiled sources, because some of the harmony features need some workarounds implemented in this. The file can be found in ‘node_modules/traceur/bin/‘ within the ‘grunt-traceur’ npm folder. You can also find it in the officlal GitHub repo mentioned above.

Important Note2: If you are using JSHint, you need to add the “esnext” flag to your .jshintrc to make it ES6-aware.

Now, we will write the Conway’s Game of Life implementation from (Conway’s Game of Life implementation) in ES6:

index.js


var getCellRepresentation = function(x, y) {
    return "x" + x + "y" + y; 
};
 
class Cell {
    constructor(x, y, alive) {
        this.x = x;
        this.y = y;
        this.alive = alive;
    }

    isAlive() {
        return this.alive; 
    }
}
 
class Board {
    constructor() {
        this.cells = {}; 
    }

    addCell(cell) {
        this.cells[getCellRepresentation(cell.x, cell.y)] = cell; 
    }

    getCellAt(x, y) {
        return this.cells[getCellRepresentation(x, y)]; 
    }

    getAliveNeighbors(cell) {
        let x = cell.x,
            y = cell.y,
            aliveCells = 0;

        for (let i = -1; i < 2; i++) {
            for(let j = -1; j < 2; j++) {
                if(i === 0 && i === j) {
                    continue;
                }
                let currentCell = this.getCellAt(x + i, y + j);

                if(currentCell && currentCell.isAlive()) {
                    aliveCells++;
                }
            }
        }
        return aliveCells;
    }

    calculateNextState(cell) {
        let tempCell = new Cell(cell.x, cell.y, cell.alive),
            livingNeighbors = this.getAliveNeighbors(cell);

        if(cell.isAlive()) {
            if(livingNeighbors === 2 || livingNeighbors === 3) {
                tempCell.alive = true;
            } else {
                tempCell.alive = false;
            }
        } else {
            if(livingNeighbors === 3) {
                tempCell.alive = true;
            }
        }
        return tempCell;
    }

    step() {
        let cells = this.cells,
            tempBoard = {},
            keys = Object.keys(cells);

        keys.forEach((c) => {
            let cell = this.cells[c],
                newCell = this.calculateNextState(cell);
            tempBoard[c] = newCell;
        });

        this.cells = tempBoard;
    }
}

This implementation actually doesn’t even use many of the new fancy things, just classes, the fat-arrow syntax for nested closures (=>) and let instead of var. But especially the new classes show you how well structured your future modules will be and that you don’t have to fight for nice code-structure anymore 😉

We also want to rewrite our Jasmine tests, we have lots of closures there, but not much else, so again, not many features used (code at the bottom of this post):

The two files we created are our index.js and spec.js in the ‘js’ folder. Now we use grunt-traceur to compile them to ES5 and run the tests using karma (karma).

Important to note here, is that this won’t work in PhantomJS, because it’s ES5 and PhantomJS’s JS engine is really old. We will have to use Chrome or something else as our test-browser in the karma.conf.js.


module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      'node_modules/grunt-traceur/node_modules/traceur/bin/traceur-runtime.js',
      'build/*.js'
    ],
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch:false, 
    browsers: ['Chrome'],
    captureTimeout: 60000,
    singleRun: false
  });
};

As mentioned above, we have to include the traceur-runtime here as well. Now we can go ‘karma start’ and ‘karma run’ and see our green bars and be happy!

Alright, that’s it! A short glimpse into the world of ES6. I believe that, although it is a little cumbersome to get running and there are some issues still with the tools we are used to in our daily development, starting to learn how to use harmony and getting good at it will pay off big within the next few years for every professional JavaScript developer and…it’s a LOT of fun 😀

Code:
spec.js


/*global describe, it, expect, sinon, stub, assert, before, beforeEach, afterEach, Board, Cell */
/*jshint expr:true */
describe('Conways Game of Life', () => {

    it('is a sanity test', () => {
        expect(true).toBe(true);
    });
    let board, cell; 

    beforeEach(() => {
        board = new Board();
        cell = new Cell(1, 1, true);
        board.addCell(cell);
    });

    describe('addCell', () => {

        it('adds a cell to a board', () => {
            expect(board.cells.x1y1).toEqual(cell);
        });

    });

    describe('getCellAt', () => {

        it('returns the cell at the provided coordinates', () => {
            expect(board.getCellAt(1, 1)).toEqual(cell);
        });

    });

    describe('getAliveNeighbors', () => {

        it('returns 0 if there are no other cells', () => {
            expect(board.getAliveNeighbors(cell)).toEqual(0);
        });

        it('returns 1 if there is one alive cell next to the cell', () => {
            let neighborCell = new Cell(0, 1, true);
            board.addCell(neighborCell);

            expect(board.getAliveNeighbors(cell)).toEqual(1);
        });

        it('returns 8 if there are 8 neighbors available', () => {
            board.addCell(new Cell(0, 1, true));
            board.addCell(new Cell(0, 2, true));
            board.addCell(new Cell(0, 0, true));
            board.addCell(new Cell(2, 1, true));
            board.addCell(new Cell(1, 0, true));
            board.addCell(new Cell(2, 2, true));
            board.addCell(new Cell(1, 2, true));
            board.addCell(new Cell(2, 0, true));

            expect(board.getAliveNeighbors(cell)).toEqual(8);
        });

        it('returns 1 if there are 7 dead cells next available', () => {
            board.addCell(new Cell(0, 1, true));
            board.addCell(new Cell(0, 2, false));
            board.addCell(new Cell(0, 0, false));
            board.addCell(new Cell(2, 1, false));
            board.addCell(new Cell(1, 0, false));
            board.addCell(new Cell(2, 2, false));
            board.addCell(new Cell(1, 2, false));
            board.addCell(new Cell(2, 0, false));

            expect(board.getAliveNeighbors(cell)).toEqual(1);
        });
    });

    describe('calculateNextState', () => {

        it('dies if there are less than 2 living neighbors', () => {
            board.addCell(new Cell(0, 0, true));

            expect(board.calculateNextState(cell).isAlive()).toBe(false);
        });

        it('dies if there are more than 3 living neighbors', () => {
            board.addCell(new Cell(0, 1, true));
            board.addCell(new Cell(0, 2, true));
            board.addCell(new Cell(0, 0, true));
            board.addCell(new Cell(1, 2, true));

            expect(board.calculateNextState(cell).isAlive()).toBe(false);
        });

        it('lives if there are 2 or 3 living neighbors', () => {
            board.addCell(new Cell(0, 0, true));
            board.addCell(new Cell(0, 1, true));

            expect(board.calculateNextState(cell).isAlive()).toBe(true);
        });

        it('comes back to live if there are exactly 3 living neighbors ', () => {
            board.addCell(new Cell(0, 0, true));
            board.addCell(new Cell(0, 1, true));
            board.addCell(new Cell(0, 2, true));
            cell.alive = false; 

            expect(board.calculateNextState(cell).isAlive()).toBe(true);
        });

    });

    describe('step', () => {

        it('calculates the new state for all dying cells', () => {
            board.addCell(new Cell(0, 0, true));

            board.step();

            expect(board.getCellAt(0, 0).isAlive()).toBe(false);
            expect(board.getCellAt(1, 1).isAlive()).toBe(false);
        });

        it('calculates the new state for all living cells', () => {
            board.addCell(new Cell(0, 0, true));
            board.addCell(new Cell(1, 2, true));

            board.step();

            expect(board.getCellAt(0, 0).isAlive()).toBe(false);
            expect(board.getCellAt(1, 1).isAlive()).toBe(true);
        });

        it('calculates the new state correctly for many cells', () => {
            board.addCell(new Cell(0, 1, true));
            board.addCell(new Cell(0, 2, true));
            board.addCell(new Cell(0, 0, false));
            board.addCell(new Cell(2, 1, true));
            board.addCell(new Cell(1, 0, true));
            board.addCell(new Cell(2, 2, true));
            board.addCell(new Cell(1, 2, false));
            board.addCell(new Cell(2, 0, false));
            board.step();

            expect(board.getCellAt(1, 1).isAlive()).toBe(false);
            expect(board.getCellAt(0, 1).isAlive()).toBe(true);
            expect(board.getCellAt(2, 2).isAlive()).toBe(true);
        });
    });

    describe('Cell', () => {

        it('is either alive or dead', () => {
            expect(cell.isAlive()).toEqual(true);
        });
    });
});

~m

Angular Default Request Headers and Interceptors

When building web applications with AngularJS, making requests using the $http service is usually a core concern. The documentation of $http is quite extensive. In this post, we will take a look at setting defaults for requests and at registering request interceptors.

First, defaults. Defining a request default is fairly simple


.config(function($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = true;
}

You can also do this directly on $http somewhere in your code, but if you have default headers which won’t change, you can just as well set it in your app’s configuration step.
With these defaults, you can do simple things, such as setting default headers for your requests e.g. an Authorization Header for basic HTTP Auth:


// only use it for get requests
$http.defaults.headers.get.Authorization = 'Basic 1233225235'

You can also do more advanced things, such as transforming requests and responses by default:


$httpProvider.defaults.transformResponse = [...array of transformation functions...]

The transformReponse and transformRequest defaults take an array of transformation functions which represent a transformation chain.
This can be very useful if you have to transform the data you get from a server for every request and want to save some boilerplate code.

So basically you can save lots of code and reduce complexity by using $http’s defaults.

However, you can’t be very specific with these defaults, if they are set, they are used for all requests. Sometimes you might want to do certain transformations or apply certain headers just for a few select requests. Of course you can just deactivate the defaults and reactivate them again after the request, but this basically defeats the purpose of having them in the first place.

The solution to this are $http’s interceptors. Interceptors can be registered with four different modes:
* request – intercept outgoing request
* requestError – intercept outgoing request error
* response – intercept incoming response
* responseError – incoming incoming error response

The following is a simple example of an interceptor which, if it encounters a status code 403 (permission denied), will redirect the user to the login page.


angular.module('InterceptorApp',[])
  .factory('httpErrorInterceptor', function($q, $location) {
    return {
      'responseError': function(rejection) {
        // permission denied, better login!
        if(rejection.status === 403) {
          $location.url('app/login');
        }
        $q.reject(rejection);
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpErrorInterceptor');
});

The next example shows an interceptor which, when a url with ‘product’ in it is called, adds an Authorization Header.


angular.module('InterceptorApp',[])
  .factory('httpProductRequestTransformationInterceptor', function($q, $location) {
    return {
      'request': function(config) {
          if(config.url.indexOf('product')) {
              config.headers.Authorization = 'BASIC 12345';
          }
          return config;
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpProductReponseTransformationInterceptor');
});

There are countless examples of using interceptors to implicitly add or remove configuration for certain requests as well as transform responses.

Another thing that’s important to note is, that interceptors can also be pushed implicitly like in the following example, where, when we receive a 404 error, we broadcast a ‘page-not-found’ event, which we can then catch somewhere else for error handling:


angular.module('InterceptorApp',[])
  .factory('httpProductRequestTransformationInterceptor', function($q, $location) {
    return {
      'request': function(config) {
          if(config.url.indexOf('product')) {
              config.headers.Authorization = 'BASIC 12345';
          }
          return config;
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push(function($q, $rootScope) {
        'responseError': function(rejection) {
            if(rejection.status === 404) {
                $rootScope.$broadcast('page-not-found');
            }
            $q.reject(rejection);
        }
    });
});

That’s it. Have fun playing around with these awesome AngularJS features :)

~m

NodeJS mocking with proxyquire

I recently stumbled upon proxyquire (https://github.com/thlorenz/proxyquire), which is a mocking framework for NodeJS’s require. It basically gives you the possibility to stub out externally required dependencies of your module under test.

An example would be, if you use a library such as mongoose (https://github.com/learnboost/mongoose/), where you have code like the following in your modules:


    var mongoose = require('mongoose'),
        Product = mongoose.model('Product'),
 
    findAllProducts = function() {
        Product.find({}, function(err, products) {
            // do something with the product
        };
    };
    
    module.exports.findAllProducts = findAllProducts;

So, basically, you just have the concept of a Mongo model, which is used to find some records in MongoDB. Of course, when writing a Unit Test, you don’t want the test to actually go to the database, instead you want to “fake” what Product.find() does. Proxyquire can help with that.

It can be easily installed and setup using


npm install --save-dev proxyquire

in your project root.

Then, just add


var proxyquire = require('proxyquire');

to the top of your test file.

Mocking out a dependency is very straightforward as well, in the test:


  describe('productmodule', function() {
      var productmodule,
          mongooseStub;
 
      before(function() {
          mongooseStub = {
              model: function() {
                  return {
                      find: function(query, callback) {
                          callback(); 
                      } 
                  };
              } 
          };
 
          productmodule = proxyquire('../modules/product', {'mongoose': mongooseStub});
      });

  });

What is important here is, that the path for the module under test, is the path from the test to the module, whereas the path to the stubbed out module is the same path that is used in the module under test. Every dependency, which is not explicitly mocked out is the real thing.

As for the mocking, you can do just about whatever you want to do here, even use something like SinonJS (http://sinonjs.org/) to spy on one of the mocked out methods.

If you’re interested in some more examples, there are plenty on the project’s GitHub page, which can be found here

I hope this helps you test some of your awesome NodeJS apps 😉

~m

Transforming a timer into a simple AngularJS directive

In my last post Building a very simple timer in AngularJS, I created a simple timer which can now be transformed into a directive.

Building angular directives provides reusability and isolation of functionality, There are many different ways of building directives in Angular, I chose a very simple way, exposing an API through events and replacing my directive with with the rendered timer.

If you compare the result below with the non-directive version, you can see the benefit, suddenly the component is placed in the view and the controller is only responsible for interactive with it, not implementing it’s functionality. Furthermore, the controller and view logic shrank down. This way, the directive can grow in isolation, and additional features and configuration options can be added more easily and across all parts of the application that use it.

A working version of the timer directive can be found here

timer.js


angular.module('TimerApp',[])
  .controller('TimerCtrl', function($scope, $timeout) {
    $scope.startTimer = function() {
        $scope.$broadcast('timer-start');
    };

    $scope.stopTimer = function() {
        $scope.$broadcast('timer-stop');
    };

    $scope.$on('timer-stopped', function(event, remaining) {
        if(remaining === 0) {
            console.log('your time ran out!');
        }
    });
  })

.directive('timer', function($compile) {
  return {
    restrict: 'E',
    scope: false,
    replace: false,
    controller: function($scope, $element, $timeout) {
      var timeoutId = null;
       
      $scope.counter = 30;
      $scope.$on('timer-start', function() {
        $scope.counter = 30;
        start();
      });
       
      $scope.$on('timer-stop', function() {
        $scope.$broadcast('timer-stopped', $scope.counter);
        $scope.counter=30;        
        $timeout.cancel(timeoutId);
      });
       
      function start() {
        timeoutId = $timeout(onTimeout, 1000);  
      }
       
      function onTimeout() {
        if($scope.counter === 0) {
          $scope.$broadcast('timer-stopped', 0);
          $timeout.cancel(timeoutId);
          return;
        }
        $scope.counter--;
        timeoutId = $timeout(onTimeout, 1000);
      }
 
      var elem = $compile('<p>{{counter}}</p>')($scope);
      $element.append(elem);
    }
  };
}); 

timer.html


   <timer/>
   <button ng-click='startTimer()'>Start</button> | <button ng-click='stopTimer()'>Stop</button>

That’s it! This method of first implementing something simple that works and then transforming it into a reusable component can be used to great effect with AngularJS. There is a lot more to directives than has been covered in this post and I will try to write some more on the topic, touching on some more advanced topics in the future, so stay tuned! 😉

~m

Building a very simple Timer in AngularJS

I recently stumbled upon the situation of needing a very simple countdown timer in my ionic app (http://ionicframework.com/). With ionic being based on the amazing AngularJS (https://angularjs.org/) I was pretty sure, that this problem had already been solved a hundred times. But instead of integrating a heavily over engineered timer-directive of some kind, I decided to do something we should do a lot more often in these situations:

Implementing a simple solution which does exactly what you need and nothing more.

While the obvious downside of reinventing the wheel is a valid argument, I think for very simple tasks such as this, the benefits of getting to know the framework you are working with a little better as well as practicing the necessary skills outweigh the time you might save by just slapping another big dependency on your growing little app.

Alright, with that out of the way, let’s get coding! The goal is to have a simple timer which can be started, stopped and reset on some kind of user interaction.

The way I went about this was not to write a directive (at first), which would be the standard angular way, but to get a very simple version of it to work and then think about transforming it into something which is reusable.

The timer can be started and stopped, which resets it. It also emits a timer-stopped-event, to be able to react in some way to the time running out.

Click here to see the timer in action!

timer.js


angular.module('TimerApp', [])
.controller('TimerCtrl', function($scope, $timeout) {
    $scope.counter = 90;

    var mytimeout = null; // the current timeoutID

    // actual timer method, counts down every second, stops on zero
    $scope.onTimeout = function() {
        if($scope.counter ===  0) {
            $scope.$broadcast('timer-stopped', 0);
            $timeout.cancel(mytimeout);
            return;
        }
        $scope.counter--;
        mytimeout = $timeout($scope.onTimeout, 1000);
    };

    $scope.startTimer = function() {
        mytimeout = $timeout($scope.onTimeout, 1000);
    };

    // stops and resets the current timer
    $scope.stopTimer = function() {
        $scope.$broadcast('timer-stopped', $scope.counter);
        $scope.counter = 90;
        $timeout.cancel(mytimeout);
    };

    // triggered, when the timer stops, you can do something here, maybe show a visual indicator or vibrate the device
    $scope.$on('timer-stopped', function(event, remaining) {
        if(remaining === 0) {
            console.log('your time ran out!');
        }
    });
});

timer.html


    {{counter}}
    <button ng-click='startTimer()'>Start
    <button ng-click='stopTimer()'>Stop

And this is it. Relatively straight-forward I would say. It does everything I wanted it to do and is still easy to extend a little. Of course, this functionality could (and should) easily be transformed into a reusable directive, which I will cover in my next post, but here the only goal was to get the simplest possible thing working.

If you’re wondering how to get this to run inside an angular app, just take a look at yeoman’s (http://yeoman.io/) angular-generator to create a simple app to put this code into :)

Links:
* http://jsbin.com/haliz/1/edit?html,js,output

~m

Playing around with async.js

In this post, I will take a look at the async.js JavaScript library. It was first written for NodeJS and then ported to be usable within browsers as well.

Async.js is a utility library, providing functions for realizing asynchronous control-flow such as parallel asynchronous method execution and the likes. It uses the nodeJS convention of using a callback function as the last argument of an async function.

A standard example of using async.js looks like this:


async.parallel([
    function(callback){
        setTimeout(function(){
            callback(null, 1); // err, result
        }, 50);
    },
    function(callback){
        setTimeout(function(){
            callback(null, 2); // err, result
        }, 100);
    }
],
function(err, results){
    // err propagates all errors within the parallel functions
    // results holds all the results, [1, 2] in this case, because the first function returns earlier (50 ms)
});

In the following examples, we will look at how we can use async.js to create some cool asynchronous control-flow illustrations.

If your browser does not support HTML5 Canvas, you won’t be able to view the examples. (and also you should seriously think about updating your browser…)

First, some setup:


var drawFromTo = function(from, to, ctx, y, color) {
    ctx.beginPath();
    ctx.moveTo(from, y);
    ctx.lineTo(to, y);
    ctx.strokeStyle = color;
    ctx.stroke();
};
 
var genericIntervalDraw = function(from, distance, canvas, fromTop, color, steps, callback) {
    var ctx = canvas.getContext('2d');
    var lastPoint = from;   
 
    var intervalID = setInterval(function() {
        drawFromTo(lastPoint, lastPoint + steps, ctx, fromTop,  color);
        lastPoint += steps;
        if(lastPoint >= from + distance) {
            clearInterval(intervalID);
            callback(null, lastPoint, fromTop);
        }
    }, 10);
};

Just some simple stuff really, we got a method for drawing a coloured line to a canvas and another method to do the drawing asynchronously. It just draws a “step” long part of the line every 10 ms. This will be our way of demonstrating when the asynchronous calls are executed.

In our first example, we create three lines in parallel:

|

async.parallel(
    [
        function(callback) {
            genericIntervalDraw(0, 450, paraCanvas, 30, 'red', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 450, paraCanvas, 60, 'blue', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 450, paraCanvas, 90, 'green', 1, callback);
        }
    ], function(err, results) {
         console.log('parallel done');
    }
);

Next, the same, but in sequence:

|

async.series(
    [
        function(callback) {
            genericIntervalDraw(0, 150, seriesCanvas, 30, 'red', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, seriesCanvas, 60, 'blue', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, seriesCanvas, 90, 'green', 1, callback);
        }
    ], function(err, results) {
         console.log('series done');
    }
);

As you can see, it’s really easy to chain together various asynchronous function calls in a coherent and readable way.
Now, another interesting use-case is the “waterfall”-pattern, where the functions are executed sequentially, each using the result of the previous execution such as in the following example:

|

async.waterfall(
    [
        function(callback) {
            genericIntervalDraw(0, 150, waterfallCanvas, 30, 'red', 1, callback);
        },
        function(arg1, arg2, callback) {
            genericIntervalDraw(arg1, 150, waterfallCanvas, arg2 + 30, 'blue', 1, callback);
        },
        function(arg1, arg2, callback) {
            genericIntervalDraw(arg1, 150, waterfallCanvas, arg2 + 30, 'green', 1, callback);
        }
    ], function(err, results) {
         console.log('waterfall done');
    }
);

Another useful feature of async.js is the possibility to execute an array of functions. This provides the user with a way of dynamically adding and removing function calls to a list and then execute them.

|


async.iterator(
    [
        genericIntervalDraw(0, 450, iteratorCanvas, 30, 'red', 1, function() {}),
        genericIntervalDraw(0, 450, iteratorCanvas, 60, 'blue', 1, function() {}),
        genericIntervalDraw(0, 450, iteratorCanvas, 90, 'green', 1, function() {}),
    ]
);

Another really cool function of async is, that you can pass a limit to some of the parallel-execution methods, specifying how many of the asynchronous functions can be executed at the same time. In the following example, there are 6 functions, but a maximum of two functions will be executed at once. Async takes care of that for us:

|

async.eachLimit(
    [
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 30, 'red', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 60, 'green', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 90, 'grey', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 120, 'pink', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 150, 'cyan', 1, callback);
        },
        function(callback) {
            genericIntervalDraw(0, 150, limitCanvas, 180, 'blue', 1, callback);
        },
    ],
    2,
    function(item, callback) {
        item(callback);
    },
    function(err) {
        console.log('limit done');
    }
);

There are, of course, many more functions available in async.js. A full list, together with examples and documentation can be found here.

Of course the different control-flow methods can be combined and nested. The following and last example shows the serial execution of the parallel execution of two serial execution strings with two coloured lines each, followed by two lines drawn in parallel.

|

async.series(
    [
        function(callback) {
            async.parallel(
                [
                    function(callback) {
                        async.series(
                            [
                                function(callback) {
                                    genericIntervalDraw(0, 150, nestingCanvas, 30, 'blue', 1, callback);
                                },
                                function(callback) {
                                    genericIntervalDraw(150, 150, nestingCanvas, 60, 'red', 1, callback);
                                }
                            ],
                            function(err, results) {
                                callback(null, null);
                            }
                        );
                    },
                    function(callback) {
                        async.series(
                            [
                                function(callback) {
                                    genericIntervalDraw(0, 150, nestingCanvas, 90, 'green', 1, callback);
                                },
                                function(callback) {
                                    genericIntervalDraw(150, 150, nestingCanvas, 120, 'pink', 1, callback);
                                }
                            ],
                            function(err, results) {
                                callback(null, null);
                            }
                        );
                    }
                ],
                function(err, results) {
                    callback(null, null);
                }
            );
        },
        function(callback) {
            async.each(
                [
                    function(callback) {
                        genericIntervalDraw(300, 150, nestingCanvas, 150, 'cyan', 1, callback);
                    },
                    function(callback) {
                        genericIntervalDraw(300, 150, nestingCanvas, 180, 'grey', 1, callback);
                    }
                ],
                function(item, callback) {
                    item(callback);
                },
                callback
            );
        }
    ],
    function(err, results) {
        console.log('nesting done');  
    }
);

Cool stuff! I hope these examples show the power of async.js and how it can help you build complex control-flows using multiple asynchronous function calls. Have fun playing around with it!

Full source:

canvas.js
canvas.html


Writing Conway’s Game of Life in JavaScript using TDD Pt.3

In this third and final part of this series (Part 1, Part 2), we will look at integrating the little program we wrote in the first two parts (Conway’s Game of Life) into a website, to create a fun little animated widget.

Let’s first take a look at what we have. We created an implementation of Conway’s Game of Life from scratch using TDD. We are pretty confident that it does what we expect it to do. We can create a new board, add cells to it and then calculate new game states based on the game’s rules. Pretty Cool.

Our logical next step is a way to draw a board. We won’t be using any fancy frameworks or Canvas for this. A cell will be represented by a letter, e.g.: ‘w’ and the board will just be a div-element. This approach is both simple and should also perform reasonably well. Also, because we will just be using our vanilla JavaScript code and jQuery, this should be able to run in just about any browser on any device, which is nice.

We will start with the simple case of initialising the whole board randomly, with a black background, white color and the ‘o’ letter as our cell-representation. Also, we won’t be using TDD for the few lines of JavaScript needed for the integration of our game, because it’s really mostly configuration and calling our already well tested methods.

Before we write any more JavaScript code, we will create a simple HTML skeleton we will use to display our results:


<html>
    <head>
        <script type="text/javascript" src="jquery.min.js"></script>
        <script type="text/javascript" src="app/conway.js"></script>
        <style type="text/css">
            div.conway {
                overflow: hidden;
                font-family: courier;
                float: left;
                width: 500px; 
                height: 288px;
                background-color: #000;
                font-size: 10px;
                color: #fff;
            }
        </style>
    </head>
    <body>
        <div>
            <button id='startCW'>Random Conway's Game of Life</button> | <button id='resetCW'>Reset</button>
            <div class='conway' id='conway'></div> 
        </div>
    </body>
</html>

We just do some CSS styling, include our conway.js and minified jquery as well as create our ‘canvas’, which is just a div element with class and id ‘conway’. The two buttons above the canvas don’t do anything yet, but will be our triggers to create a new randomized game and to reset the running game.

Now let’s get codin’! The first task, if you are using some program you just wrote somewhere on a website is to wrap it into a module. This can be achieved using the Module-Pattern, a best practice for handling namespace issues and for modularising your codebase in JavaScript.

To do this, simply do the following:


var CONWAY = (function() {
    var my = {};

    ...all your conway's code here...

    return my;
}());

We create the module ‘CONWAY’. Now within the {}=brackets, we have the ‘my’ variable, which is what will be exposed publicly. Everything else within the module such as variables simply created with the ‘var’ keyword, will be private to the module. To learn more about the module-pattern, this is a helpful resource, but there are loads of good tutorials on the topic somewhere around the web. For now it’s just important to us, that our whole program resides within our module and that we will expose the methods we will be calling using the ‘my’ variable.

Now, let’s write a quick method for initialising the board randomly:


var CONWAY_GLOB_MAX_X = 99;
var CONWAY_GLOB_MAX_Y = 23;

var randomInit = function(board) {
    for (var y = 0; y <= CONWAY_GLOB_MAX_Y; y++) {
        for (var x = 0; x <= CONWAY_GLOB_MAX_X; x++) {
            board.addCell(new Cell(x, y, (Math.random() > 0.8)));
        }
    }  
};

First, we create some constants which act as our outer boundaries. You have to fiddle around with these values depending on the size of your div and the font size and font-type you want to use. Other than that, we just iterate over the board limits and add cells to the board which are either dead or alive based on the random expression. Here, as well, you can fiddle with the numbers, but in the end it just controls how many cells will be alive and how many will be dead in the first frame, from then on the game will balance itself out using it’s rules.

Also notice, that we did not add the randomInit function to our ‘my’-object, thus it is private.

Alright, next will be a function to actually ‘draw’ the game onto our canvas:


var draw = function(board, canvas) {
    var output = ' ';

    for(var y = 0; y <= CONWAY_GLOB_MAX_X; y++) {
        for(var x = 0; x <= CONWAY_GLOB_MAX_Y; x++) {
            var cell = board.getCellAt(x, y);
            output += cell && cell.isAlive() ? 'o' : ' ';
        }
        output += '
'; } canvas.html(output); };

This is not very sophisticated, but it will do the job. We iterate through our whole board again, retrieve the cell at each point and, if there is a living cell there, print ‘o’ – our cell representation and just an empty space if the cell is dead or if there isn’t even a cell there. After each row, we print a line-break and at the end, we put everything in our canvas element. The method will be called with a board and the element in which to write.

To put the initialisation and the drawing code together, we create a ‘doConway’ method, which will actually start the whole magic:


var doConway = function(body) {
    var board = new Board(); // create new board
  
    randomInit(board); // init board with random dead and alive cells
  
    draw(board, body); // draw the board
  
    return setInterval(function() { // update every 130 ms
        board.step();
        draw(board, body);
    }, 130);
};

So this is a little more tricky, but not really. We create a board, initialise it and draw it. So far so good, but we want it to be animated. To achieve this, we use the ‘setInterval’ method, which allows us to call a function every x milliseconds. In this case, we want the calculation of our next board, and the drawing of this new state to happen every 130 milliseconds, which will give it a nice smooth animation effect. You can fiddle with this number to make it slower or faster. The doConway-function returns the intervalID which gets returned by setInterval. This is a reference to the running interval which we will save globally to be able to stop the animation whenever we want to.

Now, in order to start and stop the whole thing, we need an ‘init’ and a ‘reset’ method, so let’s write them:


my.initConway = function(id, body) {
    clearInterval(id);
    return doConway(body);
};

my.resetConway = function(id, body) {
    body.empty(); 
    clearInterval(id);
};

First of all, notice that we added these methods to the ‘my’ object, so these two will actually be public. They represent the public API of our CONWAY module. This is all we will need to be able to call from outside. They both don’t do very much actually, the ‘init’ function kills the running interval with the given id and starts a new Conway on the given DOM-element, returning the new intervalID. The ‘reset’ function just clears the given canvas element and stops the animation.

Well, it seems we have everything we need, so let’s just add two simple click handlers for our buttons and let the magic begin:


<script>
    var conwayRandIntervalId = null;

    $(function() {
        var conwayBody = $('#conway');
        $('#resetCW').click(function() {
            CONWAY.resetConway(conwayRandIntervalId, conwayBody); 
        });

        $('#startCW').click(function() {
            conwayRandIntervalId = CONWAY.initConway(conwayRandIntervalId, conwayBody); 
        });
    }); 
</script>

We create a global reference for the intervalID of our current animation called ‘conwayRandIntervalId’. Then we use jQuery to, onLoad of the page, add click handlers to the two buttons, each calling their designated methods within the ‘CONWAY’ module. We also specify ‘conwayBody’ to be our div with the id ‘conway’.

Now, clicking on the ‘Random Conway’s Game of Life’ button will start a new, randomised animation and ‘reset’ will simply clear the canvas.

Yeehaw! DONE! Cool stuff.

This, however, was just the very basic version of this script. Below you’ll find a more sophisticated version of the whole thing, able to be initialised with random font-colors and pre-defined init-functions to create cool little animations such as the famous Glider Gun, Spaceships etc. A Live Demo of the whole app can be found here.

I hope you enjoyed this series and am always very appreciative of any sort of feedback you are willing to provide!

FIN.

Downloads:

Writing Conway’s Game of Life in JavaScript using TDD Pt.2

In the first part of this 3-part entry, we did some project setup and wrote the basis of our Conway’s Game of Life implementation.

At this moment, the game consists of a board which can have cells added to it. We also have a way to retrieve cells from the board using their identification as well as a way to calculate the number of living neighbors a specific cell has.

The next step is to implement the four rules for calculating a new board state:

• If a living cell has less than two living neighbors, it dies
• If a living cell has more than three living neighbors, it dies
• If a living cell has exactly two or three living neighbors, it lives
• If a dead cell has exactly three living neighbors, it comes back to life

We start with the first rule and write a test for it:


describe('calculateNextState', function() {
  it('dies if there are less than 2 living neighbors', function() {
    board.addCell(new Cell(0, 0, true));
        
    expect(board.calculateNextState(cell).isAlive()).toBe(false);
  });
});

So we add an additional cell to the board and expect it to die, because there are less than 2 living neighbors next to it.
Remember, that in our “beforeEach” method in part 1 of this post, we made it so that one cell is always added to the board before each test:


var board, cell; 
   
beforeEach(function() {
  board = new Board();
  cell = new Cell(1, 1, true);
  board.addCell(cell);
});

So now there are 2 cells on the board next to each other. We expect our new method “calculateNextState” to return a cell object. So let’s make the test pass:


calculateNextState: function(cell) {
  return new Cell(cell.x, cell.y, false);
}

We just return a new temporary cell, which is dead. Now, to write a test to generalize our implementation, we tackle the second rule:


it('dies if there are more than 3 living neighbors', function() {
  board.addCell(new Cell(0, 1, true));
  board.addCell(new Cell(0, 2, true));
  board.addCell(new Cell(0, 0, true));
  board.addCell(new Cell(1, 2, true));

  expect(board.calculateNextState(cell).isAlive()).toBe(false);
});

We add 4 additional cells, all of which are alive. We expect the cell under test do die, which it does with our current implementation. We are forced to write another test in order to generalize our implementation, so on the third rule:


it('lives if there are 2 or 3 living neighbors', function() {
  board.addCell(new Cell(0, 0, true));
  board.addCell(new Cell(0, 1, true));
  
  expect(board.calculateNextState(cell).isAlive()).toBe(true);
});

Here we add 2 additional cells to the board and expect the cell under test to be alive in the next iteration. We finally have a failing test again! Let’s make it pass:


calculateNextState: function(cell) {
  var tempCell = new Cell(cell.x, cell.y, cell.alive);
  var livingNeighbors = this.getAliveNeighbors(cell);

  if(livingNeighbors == 2 || livingNeighbors == 3) {
   tempCell.alive = true;
  } else {
   tempCell.alive = false;
  }

  return tempCell;
}

So this is quite a jump from our previous implementation, but the problem at hand isn’t all that complicated, so we felt confident enough. First, we count the cell’s living neighbors and simply apply our three rules in the form of an if-statement.
Now there is only one rule left to implement, so let’s get on to writing a test for it:


it('comes back to live if there are exactly 3 living neighbors ', function() {
  board.addCell(new Cell(0, 0, true));
  board.addCell(new Cell(0, 1, true));
  board.addCell(new Cell(0, 2, true));
  cell.alive = false; 

  expect(board.calculateNextState(cell).isAlive()).toBe(true);
});

We add 3 additional cells and set the “alive” status of our cell under test to false. Unfortunately, this test passes, although we didn’t implement this yet. This is a tricky situation, because we didn’t implement this yet. At this stage it is a good idea to try and write a failing test for this rule:


it('does not come back to live if there are exactly 2 living neighbors ', function() {
  board.addCell(new Cell(0, 0, true));
  board.addCell(new Cell(0, 1, true));
  cell.alive = false; 

  expect(board.calculateNextState(cell).isAlive()).toBe(false);
});        

Yes! This test fails. We add 2 additional cells and expect our dead cell to stay dead, but instead it comes back to life. Now it’s time to make it pass:


calculateNextState: function(cell) {
  var tempCell = new Cell(cell.x, cell.y, cell.alive);
  var livingNeighbors = this.getAliveNeighbors(cell);
  
  if(cell.alive) {
    if(livingNeighbors == 2 || livingNeighbors == 3) {
     tempCell.alive = true;
    } else {
     tempCell.alive = false;
    }
  } else {
   if(livingNeighbors == 3) {
     tempCell.alive = true;
   }
  }
  
  return tempCell;
}

Alright, so we just check if the cell is alive or dead and act according to our rules in each case. Simple stuff, right? 😉

This example, I think, is a nice reminder of how when using TDD you need the discipline to write all the necessary tests in order not to fall into the “green-tests”-trap. What this means is, that just because all your tests are green does not mean that your implementation does not contain any errors or that you thought of every possible case.

Soooo… what’s next? We have a board, cells, neighbors and even a way to calculate the next iteration for each one of our cells. The next step should probably be to…well…actually do that, calculate the new board state that is.

With our tests in place, we are pretty confident that the logic concerning the rules and a cell’s neighbors is stable. For testing a method, which just executes our existing methods on the whole board we will focus on three different tests. A test with all dead cells, a test with all living cells and a test with many mixed cells. Let’s get to it:


describe('step', function() {
  it('calculates the new state for all dying cells', function() {
    board.addCell(new Cell(0, 0, true));
    
    board.step();
    
    expect(board.getCellAt(0, 0).isAlive()).toBe(false);
    expect(board.getCellAt(1, 1).isAlive()).toBe(false);
  });
});               

Alright, simple enough, we populate the board with 2 cells and expect them both to die. Let’s make it pass:


step: function() {
  var cells = this.cells;
  var tempBoard = {};
  
  for(var c in this.cells) {
    var cell = this.cells[c];
    var newCell = this.calculateNextState(cell);
    tempBoard[c] = newCell;
  }
  
  this.cells = tempBoard;
}

This implementation is actually pretty simple. We expect all of our above-mentioned tests to pass with it. We just get our cells, iterate through them, calculate their new state and add them to a temporary board. When we are done, we replace our current board with the temporary one. Of course, if we were concerned about performance issues at this point, we could come up with a faster, more sophisticated solution. But right now, this should suffice and it makes our first test pass. So let’s write another one:


it('calculates the new state for all living cells', function() {
  board.addCell(new Cell(0, 0, true));
  board.addCell(new Cell(1, 2, true));

  board.step();

  expect(board.getCellAt(0, 0).isAlive()).toBe(false);
  expect(board.getCellAt(1, 1).isAlive()).toBe(true);
});

This time, we add 3 living cells and assert that the one with 2 neighbors will survive, while the cell with 1 neighbor dies. It passes, so on the our final test:


it('calculates the new state correctly for many cells', function() {
  board.addCell(new Cell(0, 1, true));
  board.addCell(new Cell(0, 2, true));
  board.addCell(new Cell(0, 0, false));
  board.addCell(new Cell(2, 1, true));
  board.addCell(new Cell(1, 0, true));
  board.addCell(new Cell(2, 2, true));
  board.addCell(new Cell(1, 2, false));
  board.addCell(new Cell(2, 0, false));
  board.step();
  
  expect(board.getCellAt(1, 1).isAlive()).toBe(false);
  expect(board.getCellAt(0, 1).isAlive()).toBe(true);
  expect(board.getCellAt(2, 2).isAlive()).toBe(true);
});

In this test, we go crazy and add a bunch of cells to the board, asserting their correct state afterwards. This can get quite complex, so it can be helpful to draw a small board with all cells and their states on a piece of paper to be able to visualize it a little better. Alas, the test passes and we are confident that our implementation of the “step” function is stable. We could, of course go ahead and write an infinite amount of tests covering all possible board states, but with our stable tests for the internal functions and the relative simplicity of the “step” function, this should suffice.

Well! This concludes part 2 of this series, I hope it was enjoyable and / or enlightening in one way or the other. In the third and final part we will be looking at a way to include this wonderful little program we just created into a website!

Full source code of this example so far:

Tests: (spec.js)


describe('Conways Game of Life', function() {
 
  it('is a sanity test', function() {
    expect(true).toBe(true);
  });
  var board, cell; 
 
  beforeEach(function() {
    board = new Board();
    cell = new Cell(1, 1, true);
    board.addCell(cell);
  });
 
  describe('addCell', function() {
   it('adds a cell to a board', function() {
        expect(board.cells.x1y1).toEqual(cell);
   });
  });
 
  describe('getCellAt', function() {
     it('returns the cell at the provided coordinates', function() {
       expect(board.getCellAt(1, 1)).toEqual(cell);
     });
  });
 
  describe('getAliveNeighbors', function() {
      
      it('returns 0 if there are no other cells', function() {
        expect(board.getAliveNeighbors(cell)).toEqual(0);
      });
   
      it('returns 1 if there is one alive cell next to the cell', function() {
        var neighborCell = new Cell(0, 1, true);
        board.addCell(neighborCell);
  
        expect(board.getAliveNeighbors(cell)).toEqual(1);
      });
   
      it('returns 8 if there are 8 neighbors available', function() {
        board.addCell(new Cell(0, 1, true));
        board.addCell(new Cell(0, 2, true));
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(2, 1, true));
        board.addCell(new Cell(1, 0, true));
        board.addCell(new Cell(2, 2, true));
        board.addCell(new Cell(1, 2, true));
        board.addCell(new Cell(2, 0, true));
         
        expect(board.getAliveNeighbors(cell)).toEqual(8);
      });
   
      it('returns 1 if there are 7 dead cells next available', function() {
        board.addCell(new Cell(0, 1, true));
        board.addCell(new Cell(0, 2, false));
        board.addCell(new Cell(0, 0, false));
        board.addCell(new Cell(2, 1, false));
        board.addCell(new Cell(1, 0, false));
        board.addCell(new Cell(2, 2, false));
        board.addCell(new Cell(1, 2, false));
        board.addCell(new Cell(2, 0, false));
   
        expect(board.getAliveNeighbors(cell)).toEqual(1);
      });
    });
  
    describe('calculateNextState', function() {
      it('dies if there are less than 2 living neighbors', function() {
        board.addCell(new Cell(0, 0, true));
          
        expect(board.calculateNextState(cell).isAlive()).toBe(false);
      });
  
      it('dies if there are more than 3 living neighbors', function() {
        board.addCell(new Cell(0, 1, true));
        board.addCell(new Cell(0, 2, true));
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(1, 2, true));
   
        expect(board.calculateNextState(cell).isAlive()).toBe(false);
      });
  
  
      it('lives if there are 2 or 3 living neighbors', function() {
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(0, 1, true));
        
        expect(board.calculateNextState(cell).isAlive()).toBe(true);
      });
  
      it('comes back to live if there are exactly 3 living neighbors ', function() {
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(0, 1, true));
        board.addCell(new Cell(0, 2, true));
        cell.alive = false; 
  
        expect(board.calculateNextState(cell).isAlive()).toBe(true);
      });
      
      it('does not come back to live if there are exactly 2 living neighbors ', function() {
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(0, 1, true));
        cell.alive = false; 
    
        expect(board.calculateNextState(cell).isAlive()).toBe(false);
      });
              
    });
      
    describe('step', function() {
      it('calculates the new state for all dying cells', function() {
        board.addCell(new Cell(0, 0, true));
    
        board.step();
    
        expect(board.getCellAt(0, 0).isAlive()).toBe(false);
        expect(board.getCellAt(1, 1).isAlive()).toBe(false);
      });

      it('calculates the new state for all living cells', function() {
        board.addCell(new Cell(0, 0, true));
        board.addCell(new Cell(1, 2, true));
    
        board.step();
    
        expect(board.getCellAt(0, 0).isAlive()).toBe(false);
        expect(board.getCellAt(1, 1).isAlive()).toBe(true);
      });
  
      it('calculates the new state correctly for many cells', function() {
        board.addCell(new Cell(0, 1, true));
        board.addCell(new Cell(0, 2, true));
        board.addCell(new Cell(0, 0, false));
        board.addCell(new Cell(2, 1, true));
        board.addCell(new Cell(1, 0, true));
        board.addCell(new Cell(2, 2, true));
        board.addCell(new Cell(1, 2, false));
        board.addCell(new Cell(2, 0, false));
        board.step();
    
        expect(board.getCellAt(1, 1).isAlive()).toBe(false);
        expect(board.getCellAt(0, 1).isAlive()).toBe(true);
        expect(board.getCellAt(2, 2).isAlive()).toBe(true);
      });
  
    });               
});

Implementation (conway.js):


var Cell = function(x, y, alive) {
  this.x = x;
  this.y = y;
  this.alive = alive;
};
  
Cell.prototype = {
  isAlive : function() {
    return this.alive;
   }
};
  
var Board = function() {
  this.cells = {}; 
};
  
Board.prototype = {
  addCell: function(cell){
    this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
  },
  getCellAt: function(x, y) {
    return this.cells[getCellRepresentation(x, y)];  
  },
  getAliveNeighbors: function(cell) {
    var x = cell.x;
    var y = cell.y;
    var aliveCells = 0;
      
    for (var i = -1; i < 2; i++) {
      for(var j = -1; j < 2; j++) {
        if(i === 0 && i == j) {
          continue;
        }
        var currentCell = this.getCellAt(x + i, y + j);
  
        if(currentCell && currentCell.isAlive()) {
          aliveCells++;
        }
      }
    }
  
    return aliveCells;
  },
  calculateNextState: function(cell) {
    var tempCell = new Cell(cell.x, cell.y, cell.alive);
    var livingNeighbors = this.getAliveNeighbors(cell);
  
    if(cell.alive) {
      if(livingNeighbors == 2 || livingNeighbors == 3) {
       tempCell.alive = true;
      } else {
       tempCell.alive = false;
      }
    } else {
     if(livingNeighbors == 3) {
       tempCell.alive = true;
     }
    }
  
    return tempCell;
  },
  step: function() {
    var cells = this.cells;
    var tempBoard = {};
  
    for(var c in this.cells) {
      var cell = this.cells[c];
      var newCell = this.calculateNextState(cell);
      tempBoard[c] = newCell;
    }
  
    this.cells = tempBoard;
  }
};
  
function getCellRepresentation (x, y) {
 return "x" + x + "y" + y; 
}

Writing Conway’s Game of Life in JavaScript using TDD Pt.1

In this 3-part entry, we write Conway’s Game of Life in JavaScript using Test Driven Development.

Conway’s Game of Life is a relatively simple cellular automaton. The game consists of a board of undefined size and cells, which can be either alive or dead. There are four simple rules, which determine the board state after every update:

• If a living cell has less than two living neighbors, it dies
• If a living cell has more than three living neighbors, it dies
• If a living cell has exactly two or three living neighbors, it lives
• If a dead cell has exactly three living neighbors, it comes back to life

So, with all that out of the way, let’s start coding!

First, we create a project with the following structure:

project structure

As a test runner, we use karma, with the following config:


module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      'app/*.js',
      'test/*.js'
    ],
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch:false, 
    browsers: ['PhantomJS'],
    captureTimeout: 60000,
    singleRun: false
  });
};

This is a very standard config. We can start karma using “karma start” and run tests using the “karma run” command. For further information on installing and configuring the karma test runner, check out it’s official documentation here.

For writing our unit tests, we will use Jasmine, as configured above. You can install the karma-jasmine plugin like this.

Now, let’s finally write some code!…but where to start? A Conway’s game consists of cells which are located somewhere on a (in this case) 2-dimensional board. We probably want to have some way to add cells to a board, in order to initialize the game properly.

First, the test:


  describe('addCell', function() {
 
   it('adds a cell to a board', function() {
     var board = new Board();
     var cell = new Cell(1, 1, true);
 
     board.addCell(cell);
 
     expect(board.cells.x1y1).toEqual(cell);
   });
 
 });

What’s happening here is that we create a “describe” block in order to organize our tests for improved code readability and within it our first unit test. In this test, we first do some setup, creating a board, a cell and adding the cell to the board. After that, we assert that the cell has indeed been added to the board in a certain way.

As you can see, we already made a design decision here, expecting a “cells” object within the board, containing the cell saved with a formatted identifier created from the cells coordinates.

The next step is to run the failing test and to make it pass.

First, we introduce the concept of a Board and of a Cell:


var Board = function() {
  this.cells = {}; 
};
 
var Cell = function(x, y) {
  this.x = x;
  this.y = y;
};

The board just holds a collection object called “cells” and the cell is just a value object for its coordinates on the board.


Board.prototype = {
  addCell: function(cell){
    this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
  }
}
 
function getCellRepresentation (x, y) {
 return "x" + x + "y" + y; 
}

Here, we implement the “addCell” method, which just adds the passed in Cell object to the collection of cells, using an identifier containing the x and y coordinates of the cell (e.g.: x1y1 for the cell at x=1 and y=1). We use this concept of a “map” rather than an Array, because it may be advantageous to be able to directly reference each cell at a later point.

As a next step, we might want to calculate a cell’s neighbors. To do that, we need to have a way to get a cell at specified coordinates. First, a new test case:


  describe('getCellAt', function() {
 
   it('returns the cell at the provided coordinates', function() {
     var board = new Board();
     var cell = new Cell(1, 1, true);
 
     board.addCell(cell);
 
     expect(board.getCellAt(1, 1)).toEqual(cell);
   });
  });

Now, as the setup for both our test cases is the same (creating a board with a cell added to it), we can refactor it to a “beforeEach” method, which will get called before each test case:


  var board, cell; 
 
  beforeEach(function() {
    board = new Board();
    cell = new Cell(1, 1, true);
    board.addCell(cell);
  });

Now we can remove the setup code from the two test cases, which just leaves the “expect” clauses.

After running the failing test, we implement the “getCellAt” method as follows:


Board.prototype = {
…
getCellAt: function(x, y) {
    return this.cells[getCellRepresentation(x, y)];  
  }
}

We did all the heavy lifting for this method within the “addCell” method, so that now we just have to return the value at the specified coordinates.

The next step on our journey to calculating the living neighbors of a cell is to iterate through the whole neighborhood of a cell (-1, 0, 1) and count the cells who are alive. This is a more complex problem, so we will take the zero, one, many approach of testing. First, a test case for the case of zero living cells:


  describe('getAliveNeighbors', function() {
   it('returns 0 if there are no other cells', function() {
    expect(board.getAliveNeighbors(cell)).toEqual(0);
   });
  });

We implement the simplest thing that will make the test pass:


  getAliveNeighbors: function(cell) {
    return 0;
  }

…and then write another test case:


 it('returns 1 if there is one alive cell next to the cell', function() {
  var neighborCell = new Cell(0, 1, true);
  board.addCell(neighborCell);
  expect(board.getAliveNeighbors(cell)).toEqual(1);
 });

In this test case, we add another cell to the board and expect our method to return 1. In order to do that, we need to extend the Cell by the concept of an internal state (to determine if a cell is alive or dead).

First, extend the Cell:


var Cell = function(x, y, alive) {
  this.x = x;
  this.y = y;
  this.alive = alive;
};
 
Cell.prototype = {
  isAlive : function() {
    return this.alive;
  }
};

then, generalize the method to make the test pass:


  getAliveNeighbors: function(cell) {
    var x = cell.x;
    var y = cell.y;
    var aliveCells = 0;
 
    for (var i = -1; i < 2; i++) {
      for(var j = -1; j < 2; j++) {
        if(i === 0 && i == j) {
          continue;
        }
        var currentCell = this.getCellAt(x + i, y + j);
 
        if(currentCell && currentCell.isAlive()) {
          aliveCells++;
        }
      }
    }
 
    return aliveCells;
  }

The implementation is actually pretty simple, first we iterate through the whole neighborhood of the center cell (all cells directly next to it, with x and y values of -1, 0 and 1 added to the center cell’s x and y). We also want to ignore the center cell (x+0, y+0). Finally, we count the living cells and return that number.

This makes the test pass. We are pretty confident that this implementation is solid, but we only covered the “zero” and “one” test cases, so we will add another “many” test case just to be sure:


    it('returns 8 if there are 8 neighbors available', function() {
      board.addCell(new Cell(0, 1, true));
      board.addCell(new Cell(0, 2, true));
      board.addCell(new Cell(0, 0, true));
      board.addCell(new Cell(2, 1, true));
      board.addCell(new Cell(1, 0, true));
      board.addCell(new Cell(2, 2, true));
      board.addCell(new Cell(1, 2, true));
      board.addCell(new Cell(2, 0, true));
 
      expect(board.getAliveNeighbors(cell)).toEqual(8);
    });

…and an inverse, because we really want to be sure this works for all possible cases:


    it('returns 1 if there are 7 dead cells next available', function() {
      board.addCell(new Cell(0, 1, true));
      board.addCell(new Cell(0, 2, false));
      board.addCell(new Cell(0, 0, false));
      board.addCell(new Cell(2, 1, false));
      board.addCell(new Cell(1, 0, false));
      board.addCell(new Cell(2, 2, false));
      board.addCell(new Cell(1, 2, false));
      board.addCell(new Cell(2, 0, false));
 
      expect(board.getAliveNeighbors(cell)).toEqual(1);
    });

As expected, both tests pass and we are now able to successfully calculate a cell’s living neighbors, which is a core part of calculating the next state of the whole board.

This concludes part 1 of this series. In the next part, we will look at implementing the four rules mentioned above, in order to consequently be able to calculate new board states.

Full source code of this example so far:

Tests: (spec.js)


describe('Conways Game of Life', function() {
 
  var board, cell; 
 
  beforeEach(function() {
    board = new Board();
    cell = new Cell(1, 1, true);
    board.addCell(cell);
  });
 
  describe('addCell', function() {
   it('adds a cell to a board', function() {
     expect(board.cells.x1y1).toEqual(cell);
    });
  });
 
  describe('getCellAt', function() {
 
   it('returns the cell at the provided coordinates', function() {
      expect(board.getCellAt(1, 1)).toEqual(cell);
    });
  });
 
  describe('getAliveNeighbors', function() {
 
    it('returns 0 if there are no other cells', function() {
      expect(board.getAliveNeighbors(cell)).toEqual(0);
    });
 
    it('returns 1 if there is one alive cell next to the cell', function() {
      var neighborCell = new Cell(0, 1, true);
      board.addCell(neighborCell);
 
      expect(board.getAliveNeighbors(cell)).toEqual(1);
    });
 
    it('returns 8 if there are 8 neighbors available', function() {
      board.addCell(new Cell(0, 1, true));
      board.addCell(new Cell(0, 2, true));
      board.addCell(new Cell(0, 0, true));
      board.addCell(new Cell(2, 1, true));
      board.addCell(new Cell(1, 0, true));
      board.addCell(new Cell(2, 2, true));
      board.addCell(new Cell(1, 2, true));
      board.addCell(new Cell(2, 0, true));
 
      expect(board.getAliveNeighbors(cell)).toEqual(8);
    });
 
    it('returns 1 if there are 7 dead cells next available', function() {
      board.addCell(new Cell(0, 1, true));
      board.addCell(new Cell(0, 2, false));
      board.addCell(new Cell(0, 0, false));
      board.addCell(new Cell(2, 1, false));
      board.addCell(new Cell(1, 0, false));
      board.addCell(new Cell(2, 2, false));
      board.addCell(new Cell(1, 2, false));
      board.addCell(new Cell(2, 0, false));
 
      expect(board.getAliveNeighbors(cell)).toEqual(1);
    });
  });
});

Implementation (conway.js):


var Cell = function(x, y, alive) {
  this.x = x;
  this.y = y;
  this.alive = alive;
};
 
Cell.prototype = {
  isAlive : function() {
    return this.alive;
   }
};
 
var Board = function() {
  this.cells = {}; 
};
 
Board.prototype = {
  addCell: function(cell){
    this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
  },
  getCellAt: function(x, y) {
    return this.cells[getCellRepresentation(x, y)];  
  },
  getAliveNeighbors: function(cell) {
    var x = cell.x;
    var y = cell.y;
    var aliveCells = 0;
 
    for (var i = -1; i < 2; i++) {
      for(var j = -1; j < 2; j++) {
        if(i === 0 && i == j) {
          continue;
        }
        var currentCell = this.getCellAt(x + i, y + j);

        if(currentCell && currentCell.isAlive()) {
          aliveCells++;
        }
      }
    }

    return aliveCells;
  }
};