Angular Project Blackjack: 8 – Gulp Tasks

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Now that we have gulp setup and running and merging our files via gulp-concat, we don’t need to stop there. Let’s go ahead and make some more tasks to run and make our lives easier.

Testing

Since our unit tests are ready to be run from the command line via karma, we can add them as a gulp task. Karma is actually built into gulp, so there are no extra packages to install! In our gulpfile.js, let’s create a karma variable and a task to run our tests:

var karma = require('karma').server;

gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

We’re telling gulp to run our tests once and where to locate our karma configuration. Since gulp is powered by node, we can use the global variable __dirname to get the path that gulp is running from. You should now be able to run gulp test and watch our tests run!

Building

If you’ve completed the previous post and done any sort of debugging on it,
one thing you may have noticed is that debugging a concatenated app.js is very
confusing. We’ve optimized our project for our end users but we’ve made life difficult
for ourselves. This is where the whole “source” vs “release” builds comes in. What
we can do is have a separate folder that our production version will go into.
There are a lot of names used for these production folders: prod, release, deploy,
but we will use build in our project.

The first thing we’ll do to prepare our build is to change the js files that our index.html
loads depending on environment. We will do this by using gulp injection (not to be confused
with Angular’s dependency injection). Install the package with npm install gulp-inject --save-dev.
Next, we’ll need to prepare our index.html file for injection. This is done by inserting html comment markers that gulp will use:

<!DOCTYPE html>
<html data-ng-app="blackjack">
<head lang="en">
    <meta charset="UTF-8">
    <title>Blackjack</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        /* This helps the ng-show/ng-hide animations start at the right place. */
        /* Since Angular has this but needs to load, this gives us the class early. */
        .ng-hide {
            display: none!important;
        }
    </style>

    <!-- bower:css -->
    <!-- endinject -->

    <!-- inject:css -->
    <!-- endinject -->


</head>
<body>
    <div data-ng-include="'app/layout/shell.html'"></div>

    <!-- bower:js -->
    <!-- endinject -->

    <!-- Begin: Source JS -->
    <!-- inject:js -->
    <!-- endinject -->
    <!-- End: Source JS -->

</body>
</html>

You’ll notice that all of our files are gone! Don’t fret, they will be back when our task is completed. We’ve got a few different injection types in our index: bower:css, inject:css, bower:js, and inject:js.

Our injections will be separate based on if they are bower components or our code. The bower components that are injected will be the same on both environments (although, we could use the minified versions of each in the production environment, but it is a bit more configuration than I’d care to throw at this post!). What will change is our code that is getting injected. We want the concatenated version of our js files to go in the production release, while we want our files to be individually included for the development environment.

One more thing we want to do is to make sure our build folder could easily be deployed to a server. This means we want all the files necessary to serve our application within the build folder. There will be a few tasks that copy the necessary files to the build folder. Let’s look at our gulpfile.js:

var gulp = require('gulp');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');
var karma = require('karma').server;
var del = require('del');
var inject = require('gulp-inject');
var bowerFiles = require('main-bower-files');

//Configuration
var buildFolder = __dirname + '/build/';

//Tasks
gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

gulp.task('index',['clean'],function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'));
});

// Production Build Tasks
/**
 * Injects release files into index.html
 */
gulp.task('inject', ['concat', 'css', 'html', 'vendor'], function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'))
        .pipe(inject(gulp.src('./build/vendor/*.js', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/vendor/*.css', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/app.js', {read: false}), {relative: true}))
        .pipe(inject(gulp.src('./build/content/*.css', {read: false}), {relative: true}))
        .pipe(gulp.dest('./build'));
});

/**
 * Copies vendor files from bower to the build/vendor folder
 */
gulp.task('vendor', function () {
    return gulp.src(bowerFiles())
        .pipe(gulp.dest('./build/vendor'));
});

/**
 * Cleans the build folder
 */
gulp.task('clean', function() {
    del(buildFolder + '**');
});

/**
 * Concats application js files into app.js
 */
gulp.task('concat', function () {
    return gulp.src(['src/app/**/*.module.js', 'src/app/**/*.js', '!src/app/**/*.spec.js'])
        .pipe(concat('app.js'))
        .pipe(ngAnnotate())
        .pipe(gulp.dest(buildFolder));
});

/**
 * Copies application css to build folder
 */
gulp.task('css', function () {
    return gulp.src('./src/content/*.css')
        .pipe(gulp.dest('./build/content'));
});

/**
 * Copies html files to build folder
 */
gulp.task('html', function () {
    return gulp.src('./src/app/**/*.html')
        .pipe(gulp.dest('./build/app'));
});

// Development Build Tasks
/**
 * Updates the index.html file
 * with all the injections
 */
gulp.task('injectDev', function(){
    return gulp.src('./src/index.html')
        .pipe(inject(gulp.src(['./src/app/**/*.module.js','./src/app/**/*.js','!./src/app/**/*.spec.js'], {read: false}), {relative: true, name: ''}))
        .pipe(inject(gulp.src(bowerFiles(), {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./src/content/*.css',{read:false}), {relative: true}))
        .pipe(gulp.dest('./src'));
});




gulp.task('build', ['inject']);

gulp.task('buildDev', ['injectDev']);

We now have two main tasks: build and buildDev. As you can see, these execute a series of sub tasks in our gulp file. One nice thing I found was the npm package main-bower-files. This uses your bower.json dependencies and builds a list of files for use in gulp. It makes it much easier to include bower files into your injections.

With these build tasks complete, we should be able to run http-server from either the src folder or the build folder and have our application work as expected.

Here is the result of our work.

Up Next: Playing the game

Angular Project Blackjack: 7 – File Concatenation with Gulp

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

We’ve created quite a lot of files in just our application so far. Unfortunately, for every one of those files, it means we need to add an include to our index.html file and increase the request count for visitors just to load the app. Wouldn’t it be nice if we could just package all of our code into one file and just load that file in the application? That’s exactly what we’ll set out to do here.

There are a TON of applications that will get us the end result we are looking for: browserify, require, webpack, etc. Although, in my opinion, the simplest and easiest to understand is gulp-concat. At its very basic form, gulp-concat literally merges the files you give it into one output file. To get it going, we’ll need to have gulp installed, and get to gulp installed, we really should know what it is doing first!

Gulp (or gulp) is a javascript powered task runner. You define your tasks in a gulpfile.js file and run them from the command line. It is easiest to run gulp when installed globally, so we’ll install gulp via npm globally and also add it to our developer dependencies. We’ll also install two different gulp plugins: “gulp-concat” and “gulp-ng-annotate”. “ng-annotate” provides us with a safeguard for dependency injection. If it comes across code with a missing injection, while it is processing it will put those in.

npm install --global gulp
npm install --save-dev gulp gulp-concat gulp-ng-annotate

Let’s configure our file concatenation task in our gulpfile.js:

var gulp = require('gulp');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');

gulp.task('concat', function () {
    return gulp.src(['src/app/**/*.module.js', 'src/app/**/*.js', '!src/app/**/*.spec.js'])
        .pipe(concat('app.js'))
        .pipe(ngAnnotate())
        .pipe(gulp.dest('src/app/'));
});

We’ve created a task called ‘concat’ that can be run from the command line with ‘gulp concat’. Inside this task, we are loading source files with the ‘src’ command and then piping those files into a flow of commands. Since all of our angular configuration modules need to be loaded before the ‘run’ code is loaded, we will put them first. We also don’t want our tests in there, so we’ll tell it to ignore all the ‘spec’ files in those paths. The first task is to merge them together with the ‘concat’ command. We are merging these into a file called app.js. Next, we’ll fix any issues with our injections via ‘ngAnnotate()’. Finally, we’ll tell the task to output the file into the ‘src/app’ directory.

If you run this task now, you’ll see our new ‘app.js’ file get created. We can now load our single app.js file in the index.html file instead of the multiple files we were loading before. This will help reduce the amount of requests each page load will make and keep our html source cleaner. You should now be able to run http-server and have it load only the app.js file.

<!DOCTYPE html>
<html data-ng-app="blackjack">
<head lang="en">
    <meta charset="UTF-8">
    <title>Blackjack</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        /* This helps the ng-show/ng-hide animations start at the right place. */
        /* Since Angular has this but needs to load, this gives us the class early. */
        .ng-hide {
            display: none!important;
        }
    </style>

    <!-- Begin: Vendor CSS -->
    <link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- End: Vendor CSS -->

    <!-- Begin: Source CSS -->
    <link href="content/blackjack.css" rel="stylesheet">
    <!-- End: Source CSS -->


</head>
<body>
    <div data-ng-include="'app/layout/shell.html'"></div>

    <!-- Begin: Vendor JS -->
    <script src="bower_components/angular/angular.min.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>
    <!-- End: Vendor JS -->

    <!-- Begin: Source JS -->
    <script src="app/app.js"></script>
    <!-- End: Source JS -->
</body>
</html>

Now that we have gulp ready for tasks, our next step (and blog post) will be to automate some of our development activities.

Here is our project up to this point:

https://github.com/adamweeks/angular-blackjack/tree/blog-post-7

Angular Project Blackjack: 6 – Do You Even TDD Bro?

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Previously, I had mentioned that test driven development hasn’t really “stuck” for me and I find myself switching back to BDD for the most part. I find that this is usually because I don’t plan features out far enough in advance and I’m more of the “experiment and refactor” kind of developer. I have found instances where TDD actually works better for me, and today’s topic is one of those instances.

We are going to be working on the card service. This service will allow us to get a deck of cards, shuffle the deck, deal from the deck. Since this blackjack project is a game based off of set rules, we know how the cards should behave. We can write tests against these rules and then we’ll write our service to meet the rules. The rules are:

  • We should be able to get a new deck of cards with the object type “Deck”
  • The deck should contain 52 cards
  • There should be no duplicate cards in a deck
  • Each card should have a rank and a suit.
  • We should be able to ‘deal’ a card from a deck that has undealt cards in it.
  • Attempting to ‘deal’ from a deck with no undealt cards returns false
  • When a card is dealt, it is no longer in the cards array
  • We should be able to shuffle the deck and randomize all undealt cards.
  • We should be able to ‘reset’ a deck that will move all dealt cards into the undealt status and shuffle.

Now let’s write those tests:

describe('CardService Unit Tests', function () {
    var CardService;
    beforeEach(function () {
        module('blackjack.card');
        inject(function (_CardService_) {
            CardService = _CardService_;
        });
    });

    it('should be true', function () {
        expect(true).toBe(true);
    });

    describe('Deck related tests', function () {
        var deck;
        beforeEach(function () {
            deck = CardService.newDeck();
        });

        it('should be able to get a new deck of cards with the object type "Deck"', function () {
            expect(deck instanceof CardService.Deck).toBe(true);
        });

        it('The deck should contain 52 cards', function () {
            expect(deck.cards.length).toBe(52);
        });

        it('Each card should have a value and a rank.', function () {
            deck.cards.forEach(function (card) {
                expect(card.rank).toBeDefined();
                expect(card.suit).toBeDefined();
            });
        });

        it('There should be no duplicate cards in a deck', function () {
            var cardNames = [];
            deck.cards.forEach(function (card) {
                expect(cardNames.indexOf(card.name())).toBe(-1);
                cardNames.push(card.name());
            });
        });

        it('We should be able to "deal" a card from a deck that has undealt cards in it.', function () {
            expect(deck.deal).toBeDefined();
            var card = deck.deal();
            expect(card instanceof CardService.Card).toBe(true);
        });

        it('Attempting to "deal" from a deck with no undealt cards returns false', function () {
            deck.cards = [];
            var card = deck.deal();
            expect(card).toBe(false);
        });

        it('When a card is dealt, it is no longer in the cards array', function () {
            var card = deck.deal();
            expect(deck.cards.indexOf(card)).toBe(-1);
        });

        it('We should be able to shuffle the deck and randomize all undealt cards.', function () {
            expect(deck.shuffle).toBeDefined();
            var originalDeck = angular.copy(deck.cards);
            deck.shuffle();
            expect(originalDeck).not.toEqual(deck.cards);
        });

        it('We should be able to "reset" a deck that will move all dealt cards into the undealt status and shuffle.', function () {
            expect(deck.reset).toBeDefined();
            deck.deal();
            expect(deck.cards.length).not.toBe(52);
            deck.reset();
            expect(deck.cards.length).toBe(52);
        });

    });
});

So we’ve got the “T” part of our TDD done. Let’s start working on getting these tests passing. The Karma runner will watch our files for changes and rerun the tests. In my opinion, the fun part about doing TDD is watching the tests start passing. This is the implementation to pass those tests:

https://github.com/adamweeks/angular-blackjack/blob/blog-post-6/src/app/card/card.service.js

Once all of the tests are passing, we should be fairly confident that our service is ready to be used. Let’s put it in place and start dealing cards! We’re only making a few minor changes to game.controller.js:

game.init = function () {
    game.started = false;
    game.player = PlayerService.newPlayer('Ringo', 100);
    game.deck = CardService.newDeck();
};

game.start = function () {
    game.started = true;
    game.player.changeScore(-100);
    game.deck.shuffle();
    game.playerCards = [];
    game.hit();
    game.hit();
};

game.hit = function () {
    game.playerCards.push(game.deck.deal());
};

Our controller now has a deck object, as well as a playerCards array. Calling the hit function tells the deck to deal a card into the playerCards array. On the view side, we’ll wire up a button that will fire the hit function, as well as displaying the cards via an ng-repeat.

You’ll notice that we don’t have any actual blackjack game logic in place yet, and a player can keep dealing themselves cards until the deck runs out and they still win! All of that is for yet another blog post! We want to keep things as single-responsibility as possible. Consider an actual deck of cards and a dealer. If you were to place them down on any table, they could deal and shuffle cards regardless of what game they were playing. Yes, they would be a pretty terrible dealer if they didn’t have anyone enforcing the rules on them, but the cards wouldn’t change and the act of dealing wouldn’t change. That is what we are trying to recreate with this service. Now, if we wanted to build a poker app, we could use this exact service to deal cards without having to change a line of code!

[View the results of our code here.] (https://github.com/adamweeks/angular-blackjack/tree/blog-post-6)

Up Next: Gulp!

Angular Project Blackjack: 5 – Player Service

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Now that we have used views, directives and controllers, let’s move on to another big part of the AngularJS library, “Services”. If you’ve used angular for any amount of time, I’m pretty sure you’ve already googled “What is the difference between service, factory and provider?”. I know I have and I still haven’t fully grasped it yet. The best answer I can give is that if you need it to be runtime configurable, use a provider. Otherwise, use a factory. Why? Because John Papa said to! Services and factories are nearly identical, but using the revealing module pattern, you can make them really look like header and implementation files, which makes the Objective-C developer in me very happy!

(One thing to note, I’ll use the word “Service” throughout this post and even name my factory “PlayerService”. This is because it makes more sense in the usage of these factories elsewhere in the code to call it a service. When you read “Inject the NetworkService”, you understand what that code object does better than calling it “Inject the NetworkFactory”.)

In our blackjack game, we will have a player who is playing the game. This player will have points/money and a name. Assigning the player a name and adding/subtracting points will all need logic somewhere. We could simply define all of this code in the Game Controller, but, that goes against our single responsibility rule. Instead, let’s make this into a PlayerService!

Our player service is going to live in a new folder, /src/app/player. We’ll create two files, the module for the folder (player.module.jsand the service (player.service.js). We’re going to go a little bit deeper into the code for the service because it requires us to make a javascript object class as well.

(function(){
    'use strict';

    angular
        .module('blackjack.player')
        .factory('PlayerService', PlayerService);

    function PlayerService(){
        var service = {
            newPlayer: newPlayer
        };

        /////////////

        function Player(playerName, initialScore) {
            this.score = initialScore;
            this.name = playerName;
        }

        Player.prototype.changeScore = function(amountToChange){
            if(!angular.isDefined(this.score)){
                this.score = 0;
            }

            this.score += amountToChange;
        };

        Player.prototype.resetScore = function () {
            this.score = 0;
        };

        function newPlayer(playerName, initialScore) {
            var player = new Player(playerName, initialScore);
            return player;
        }

        return service;
    }
})();

Right now, the only function available to PlayerService is ‘newPlayer’. This will create a new javascript ‘Player’ object and return it. We also have some (what we would call in OOP, ‘class methods’) prototype functions on the objects. These are the functions that we will call from our controller to modify our player object. Our ‘newPlayer’ function on the PlayerService creates the Player object and sets the parameters that are passed in to it.

Now that we have our service laid out, how do we go about using it? Well, once we update our index.html and our main blackjack.module.js to include our new service, we can simply inject our player service into the game controller. Our game.controller.js:

(function(){
    'use strict';

    angular
        .module('blackjack.game')
        .controller('GameController',GameController);

    GameController.$inject = ['PlayerService'];

    function GameController(PlayerService){
        var game = this;

        game.init = function () {
            game.started = false;
            game.player = PlayerService.newPlayer('Ringo', 100);
        };

        game.start = function () {
            game.started = true;
            game.player.changeScore(-100);
        };

        game.end = function () {
            game.started = false;
            game.player.changeScore(200);
        };

        game.reset = function () {
            game.player.resetScore();
            game.started = false;
        };

        game.init();
    }
})();

The two big lines to pay attention to here are:

GameController.$inject = ['PlayerService'];
function GameController(PlayerService){

This is AngularJS’s injection. The PlayerService is now available throughout our GameController and can be called from within it. Angular provides us with a service locator that will find these services simply by updating $inject.

We now have our player get created with a new game initialization and he’s winning his blackjack hand every time we “end” the game. Everything seems to be working great. That is, until we go back and run our unit tests and see we have a “Error: [$injector:unpr]” error being reported. That is because we haven’t given our game controller tests access to the PlayerService. Give it access to it by adding it to the listed modules it loads:

module('blackjack.player');

While we’re in there, let’s add a quick test as well:

it('should have a player defined', function (){
    expect(gameController.player).toBeDefined();
});

Since we’re writing tests, might as well write some for our PlayerService as well:

'use strict';

describe('PlayerService Unit Tests', function () {
    var PlayerService;
    beforeEach(function () {
        module('blackjack.player');
        inject(function (_PlayerService_) {
            PlayerService = _PlayerService_;
        });
    });

    it('should be true', function () {
        expect(true).toBe(true);
    });

    it('should create a new player with name and score', function () {
        var playerName = 'testPlayer';
        var playerScore = 10101;
        var player = PlayerService.newPlayer(playerName, playerScore);
        expect(player.name).toBe(playerName);
        expect(player.score).toBe(playerScore);
    });

});

One interesting thing to notice in our testing code is the weird “_PlayerService_” way of doing the injection. By surrounding the service with underscores, Angular knows to call the actual service here. When we assign the underscored version of PlayerService to our local var PlayerService, we are giving our test a copy of that service from within the test. This is important because it allows us to do things like mock results, change functions, etc., all from within our test. The reason it makes a copy is so that if the service is injected in other tests, our modifications aren’t passed over to them and they get a “clean” version of the service.

Our tests should be passing now with no errors. You can visit the code base here.

Up Next: TDD Card Service

Angular Project Blackjack: 4 – Game Directive

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Going back to our first controller, we had to throw a lot of code into our shell.html file to display our game. This code had to know a lot about how the GameController functioned and if we wanted to have multiple copies throughout the application, we would need to duplicate this code every time.

Thankfully, AngularJS gives us the ability to place complex objects into our html code with very simple format. This ability is called angular “directives”. Angular is built on directives. In fact, we’ve already used a few built in directives in the shell.html file: “ng-if”, “ng-controller”, “ng-click”. Let’s get started making our own!

We are going to create a new file called game.directive.js in our app/game folder. Here is that file:

(function(){
    'use strict';

    angular
        .module('blackjack.game')
        .directive('blackjackGame', blackjackGame);

    function blackjackGame(){
        return {
            restrict: 'E',
            templateUrl: 'app/game/game.directive.html',
            controller: 'GameController',
            controllerAs: 'game'
        }
    }
})();

Whatever we call our directive will be what is used in the implementation of it in html. Angular auto converts our camel case “blackjackGame” into “blackjack-game” for usage. There is also a trend (that I’m following in this example) of prefixing your directive with some sort of namespace tactic. Our namespace is ‘blackjack’. Had we called our directive “game” instead, then down the road used some sort of third party directive with the same name, we would’ve had issues. This way eliminates those issues.

The format for the directive method has some complicated options, but we will keep this one reasonably sane. I’ve previously written about the ‘restrict’ option, but just know that ‘E’ will require us to use the element name like so:

<blackjack-game></blackjack-game>

The controller and controllerAs replaces the “ng-controller=’GameController as game'” from shell.html. The templateUrl tells angular where to get the html to fill this directive with. game.directive.html looks like so:

<div>
 <div ng-if="!game.started">
 <button class="btn btn-primary" ng-click="game.start()">Start Game</button>
 </div>
 <div ng-if="game.started">
 Game In Progress
 <button class="btn btn-danger" ng-click="game.end()">End Game</button>
 </div>
</div>

Starting to look familiar? We’ve simply ripped this out of the shell.html file. Now our main area in shell.html looks like this:

<div class="primary-area">
 <h1>Welcome to Angular Blackjack</h1>
 <blackjack-game></blackjack-game>
</div>

This makes the main html component easy to read and understand what is happening on the page. There are lots more options for directives, but since this is a very basic one, we won’t configure it any more.

We’ve now created our first custom directive and replaced lots of lines of code in our template with one simple line!

Here is the result of our work

Hope you all are enjoying this series. Please feel free to reach out with any questions or concerns.

Angular Project Blackjack: 3 – Unit Testing

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Now that we have our application running and our first controller done, the next thing we want to get setup is our testing framework. Having a testing framework ready to go is always beneficial, no matter what kind of development style you choose. I haven’t been able to get on board with TDD, but that is just my personal preference. I do like to have unit tests that cover my code, but I find it easier to write the tests after the code rather than before.

We are going to use the karma test runner suite to run our jasmine tests. Karma is a tool developed by the AngularJS team to run unit tests against a browser. It has the option to watch files for changes and re-run tests as soon as it detects them. It also reports passes/failures of the tests. Jasmine is the tool that we will write our tests in. We will test each part of the code, expecting certain things to happen, causing the tests to pass or fail.

Let’s get our environment setup to do some testing. Since this is first time we’ll be using npm to install something we want to save to the project, let’s first create a package.json file in the projects root directory:

{
  "name": "angular-blackjack",
 "version": "0.0.1"
}

Now we are able to install packages with npm and save them to our package.json file so that they can be installed on any other machine that we take the project to.

npm install karma --save-dev
npm install karma-phantomjs-launcher --save-dev
npm install jasmine-core --save-dev
npm install karma-jasmine --save-dev
bower install angular-mocks --save-dev
karma init karma.config.js

This will initialize our karma test runner file karma.config.js. When it asks for which browser you would like to test on, enter ‘PhantomJS’. This is our “headless” browser that runs nicely on the command line. The most important part right now is to make sure karma is loading all of our source files properly like so:

files: [
'./src/bower_components/angular/angular.min.js',
'./src/bower_components/angular-mocks/angular-mocks.js',
'./src/app/blackjack.module.js',
'./src/app/**/*.module.js',
'./src/app/**/*.js',
//Test Specs
'./src/app/**/*.spec.js'
] 

We are telling karma to load all of our javascript files in the src directory, but we want to specifically tell it to load the ‘.spec.js’ files. These will be our tests.

In order to execute tests, inject angular code and core services, we also install angular-mocks. From the documentation, angular-mocks (or ngMocks) “provides support to inject and mock Angular services into unit tests”. This means we do things like “inject”, “dump”, and “module”. Things that are necessary to test our code.

Following the style guide, we want to keep our tests along side of our source code and name the tests as close to the files they are testing as possible. Our first tests will be on our game.controller.js file, so we will put it in the same directory and call it game.controller.spec.js. Let’s go through our test:

describe('GameController Unit Tests', function () {
    var gameController;
    beforeEach(function () {
        module('blackjack.game');
        inject(function ($controller) {
            gameController = $controller('GameController');
        });
    });

    it('should be true', function () {
       expect(true).toBe(true);
    });

    it('should have a start and end function', function () {
        expect(gameController.start).toBeDefined();
        expect(typeof gameController.start).toBe('function');
        expect(gameController.end).toBeDefined();
        expect(typeof gameController.end).toBe('function');
    });
});

First, our describe() function, well, describes the tests that are run in the second parameter. Everything within beforeEach() function will be executed, I hate to say this, before each test is run. Notice a pattern here? The Jasmine framework makes very readable code. Next, we’ll load our blackjack.game module with the module() function. The inject() function is where things get a bit different. Since we will be creating a controller to test, we will use the $controller service. We’ll create our ‘GameController’ within the inject function, so we have access to the $controller service we just injected. If we were to need the $controller service outside of the inject function, there is another method we could use, but that is (yet another) blog post!

Now that our gameController variable has been defined, we can run some actual tests on it. The first test:

expect(true).toBe(true);

is a good way to reveal any setup issues you may have. If the tests fail, you know you’ve done something wrong. For the next test, we will actually verify that our controller has the ‘start’ and ‘end’ functions available:

it('should have a start and end function', function () {
    expect(gameController.start).toBeDefined();
    expect(typeof gameController.start).toBe('function');
    expect(gameController.end).toBeDefined();
    expect(typeof gameController.end).toBe('function');
});

These ‘toBeDefined’ functions verify that, yes, these functions are defined on the controller. But, if there was a property called ‘start’, this would validate as true also. That is why we have the ‘typeof’ validation as well.

These tests can be run on the command line with

karma start karma.conf.js

We now have our unit testing framework setup and running a test against our first controller that we wrote. Not a bad start, especially without using any sort of generator or starter template!

One final note. I switched over to WebStorm a few months ago and I couldn’t be happier with the IDE. Coming from a Visual Studio and an XCode background, this IDE makes me feel like home. Here is WebStorm’s visualization of the tests we just wrote, pretty great right!?!

Screenshot 2015-02-11 22.23.42

Here’s the results of our work!

Up next: Making A Directive

Angular Project Blackjack: 2 – Our First Controller

This post is part two of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.

In our last post, we got our project directory setup, loading angular via bower and serving our files locally with http-server. Before we do anything else, let’s make it look somewhat decent via bootstrap. We’ll download it via bower. Then we’ll link the files in our index.html

bower install bootstrap --save

We’ll update the shell.html with with the bootstrap base template taken straight from the docs.
https://github.com/adamweeks/angular-blackjack/blob/blog-post-2/src/app/layout/shell.html

Now that we’re looking half-way decent, we’ll start by putting our game on the page. In AngularJS, our controllers are the ‘C’ to the MVC. They also can be the ‘VM’ in MVVM, depending on how you look at it. I’m a big MVC guy and since the C in MVC stands for ‘Controller’, so I’m just going to go with that. Let’s create our controller for the game, called GameController. As we go along, this will eventually contain the entire game. Since we are just getting started though, the only things our game controller will do right now is to start and end a game. Here is the meat to our controller:

function GameController(){
    var game = this;

    game.init = function () {
        game.started = false;
    };

    game.start = function () {
        game.started = true;
    };

    game.end = function () {
        game.started = false;
    };

    game.init();
}

I’m setting up this controller to always be used in a ‘controllerAs’ situation. This eliminates the need to use $scope in your controllers, and it is highly suggested by most Angular professionals. I also almost always use an init function in my controllers. I’ve found that it makes testing a bit easier when you want to see how your controller behaves with different variables. It is also good to have all your initialization code easy to find and manage. I’ve seen some controllers with variables being init-ed all over the place and it is tough to follow. Our start and end functions will be called on buttons in the view.

Let’s implement this controller on the view side.

<div ng-controller="GameController as game">
    <div ng-if="!game.started">
        <button class="btn btn-primary" ng-click="game.start()">Start Game</button>
    </div>
    <div ng-if="game.started">
        Game In Progress
        <button class="btn btn-danger" ng-click="game.end()">End Game</button>
    </div>
</div>

Our view now has two states, game started and game stopped. The ‘ng-if’ displays the proper button for each state. You’ll see we’ve wired up the buttons with ‘ng-click’ to call those functions we defined on the controller.

We’ve got our game controller being displayed and the buttons are functioning properly, so we will leave it here. Take a look at the resulting code here.

One thing to note, I would normally put this game controller into a directive and just throw the directive into shell.html, but since we haven’t discussed directives yet, we’ll leave that for another post!

Up next: Unit Testing!