Jasmine Tutorial: Unit Testing Promises In Angular Services

Leave a comment Standard

So recently I’ve been doing a lot of Angular unit tests with Jasmine and Grunt. I have to say, testing promises is extremely frustrating and most of the examples I found out there either 1) didn’t work or 2) wouldn’t allow you to test content in a then block if you had multiple chained promises, or if you had a promise being called from and returned from another service. After many hours of frustrating trial and errors I found a solution I’m happy with so I want to share.

Let’s assume this is your controller:

'use strict';

angular.module('myModule').controller('myCtrl', ['$scope', 'api',
function ($scope, $api) {

  $scope.data = null;
  $scope.message = null;

  /**
  * Load some data from the API service
  * @return boolean
  **/
  $scope.loadData = function () {

    var config = {
      method: 'GET',
      url: 'http://api.domain.com/someRESTfulEndpoint',
      headers: {'Content-Type': 'application/json'},
      data: null
    };

    return $api.load(config).then(function (request) {
      if (request) {
        $scope.message = 'Your data was successfully loaded';
        $scope.data = request.response;
        return true;
      } else {
        $scope.message = 'The API service failed';
      }
      return false;
    });
  };
}]);

And here is the service making your API calls:

'use strict';

angular.module('myModule').service('api', ['$rootScope', '$http',
  function ($rootScope, $http) {

    /**
    * Makes the $http request given the proper configuration options
    * @param Object configruation for the $http request
    * @return Object $http promise that resolves to request payload or error
    **/
    this.load = function (options) {
      return $http({
        method: options.method || 'GET',
        url: options.url || 'http://api.domain.com/someDefaultEndpoint',
        headers: options.headers || null,
        data: options.data || null
      }).then(
        function (request) {
          //we got a response back, return just the API payload data
          if (request.status === 200 && request.data) {
            return request.data;
          } else {
            return {
              results: false,
              message: 'API request error',
              response: request
           };
         }
       }
     );
   };
}]);

Finally, this is your working unit tests:

'use strict';

describe('testing myModule', function () {

  beforeEach(module('myModule'));

  var $controller, $rootScope, $scope, $api, $httpBackend, $q;

  beforeEach(
    inject(function (_$controller_, _$rootScope_, api, _$httpBackend_, _$q_) {
      $controller = _$controller_;
      $rootScope = _$rootScope_;
      $httpBackend = _$httpBackend_;
      $q = _$q_;
      $api = api;
      $scope = $rootScope.$new();

      $httpBackend.when('GET').respond('test http GET call');
      $httpBackend.when('POST').respond('test http POST call');

      $controller('myCtrl', {
        $scope: $scope,
        $api: $api
      });
    })
  );

  it('has a $scope.loadData() function', function() {

    var testConfig = {
      method: 'POST',
      url: 'http://api.domain.com/endpoint',
      data: { some: 'data'}
    };

    var fakeData = { data: 'is fake' };

    //we spy on the API service and also return a fake promise call
    spyOn($api, 'load').and.callFake(
      function() {
        var deferred = $q.defer();
        deferred.resolve({result: true, response: fakeData});
        return deferred.promise;
      });
    });

    $scope.loadData(testConfig); //call the function we're testing in the controller
    $scope.$apply(); //we have to call this so Angular will try and resolve the promise
    expect($api.load.calls.count()).toEqual(1); //make sure it's called the api service
    expect($scope.message).toEqual('Your data was successfully loaded');
    expect($scope.data).toEqual(fakeData);
  });

  it('returns an error if the API fails', function() {

    var testConfig = {
      method: 'POST',
      url: 'http://api.domain.com/endpoint',
      data: { some: 'data'}
    };

    spyOn($api, 'load').and.callFake(
      function() {
        var deferred = $q.defer();
        deferred.resolve({
        result: false,
        message: 'api error'
      });
      return deferred.promise;
    });

    $scope.loadData(testConfig);
    $scope.$apply();
    expect($scope.message).toEqual('api error');
    expect($api.load.calls.count()).toEqual(1);
  });
});

Continue reading

Advertisements

HTML5 Tutorial: Simple Music Player

Leave a comment Standard

So this is a pretty simple example but I was playing around with flash for my chat room and this was byproduct of that experimentation. It’s funny how even when I’m trying to learn something new I do my best to make it useful for other situations which is a good habit to get into if you’re not doing it already.

Try out the working demo or download the code

Caveats

This will only work on a browser that is HTML5 compliant and has support for audio. That means older browsers and most versions of Internet Explorer are not supported. Firefox, Chrome and Opera work as well as the newest versions of Internet Explorer.

The Audio Tag

If you’re familiar with HTML then most of the index file below will look pretty familiar. The thing you really have to pay attention to is the new <audio> tag. It looks like this:

<audio id="rock" src="http://whiteoakstables.net/chat/audio/rock.wav" preload="auto"></audio>

You may notice that it looks a whole lot like a hyperlink with the src that defines the location of the file. Preload is an optional attribute, it tells your browser to start downloading the file as soon as the DOM, or page elements, have finished loading. In addition to setting preload to true I’ve added an ID attribute so I can reference this particular song in my music player.

The Index File

<!DOCTYPE html>
<html>
 <head>
 <script data-require="jquery@*" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
 <link data-require="bootstrap-glyphicons@*" data-semver="3.2.1" rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/img/glyphicons-halflings.png" />
 <link data-require="bootstrap@*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
 <script data-require="bootstrap@*" data-semver="3.3.1" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
 <link rel="stylesheet" href="style.css" />
 <script src="script.js"></script>
 </head>
 <body>
 <h1 class="text-center">Bootstrap HTML5 Music Player</h1>
 <audio id="rock" src="http://whiteoakstables.net/chat/audio/rock.wav" preload="auto"></audio>
 <audio id="blues" src="http://whiteoakstables.net/chat/audio/blues.wav" preload="auto"></audio>
 <audio id="electronica" src="http://whiteoakstables.net/chat/audio/electronica.wav" preload="auto"></audio>
 <audio id="classical" src="http://whiteoakstables.net/chat/audio/classical.wav" preload="auto"></audio>
 <audio id="latin" src="http://whiteoakstables.net/chat/audio/latin.wav" preload="auto"></audio>
 <audio id="indie" src="http://whiteoakstables.net/chat/audio/indie.wav" preload="auto"></audio>
 <div class="audiocontroller">
 <i class="audio-logo glyphicon glyphicon-headphones"></i>
 <i class="dynamiclink glyphicon glyphicon-play" id="play"></i></a>
 <i class="dynamiclink glyphicon glyphicon-pause" id="pause"></i>
 <select id="currentTrack">
 <option value="rock">Rock</option>
 <option value="blues">Blues</option>
 <option value="electronica">Electronica</option>
 <option value="classical">Classical</option>
 <option value="latin">Latin</option>
 <option value="indie">Indie</option>
 </select>
 <i class="dynamiclink glyphicon glyphicon-stop" id="stop"></i>
 </div>
 <p class="text-center">
 <a id="author" href="http://design1online.com" target="_blank">by Design1online.com, LLC</a>
 </p>
 </body>
</html>

The Javascript File

//Simple HTML5 Music Player by design1online.com, LLC

var currentTrack = null;
var paused = false;

$(document).ready(function() {
 currentTrack = $('#currentTrack').val();
 
 $("#play").click(function() { play(); });
 $("#stop").click(function() { stop(); });
 $("#pause").click(function() { pause(); });
});

function play() {

 //set the track back to the beginning
 if (!paused) {
 stop();
 currentTrack = $('#currentTrack').val();
 
 //make sure track starts from the begining each time
 document.getElementById(currentTrack).load();
 }

 //play the track
 document.getElementById(currentTrack).play();
}
 
//stop playing the current track
function stop() {
 if (currentTrack) {
 document.getElementById(currentTrack).pause();
 paused = false;
 }
}

//stop the track but don't go back to the beginning
function pause() {
 paused = true;
 document.getElementById(currentTrack).pause();
}

How It All Works

So now let’s talk about the javascript file. At first glance it looks relatively simple because it is! When you want to select a track you simply ask for it using getElementById and then you tell it to play or to pause. One thing that is sadly lacking is a stop() functionality. With html5 you can only pause an audio file — which does stop the playback but it also keeps the audio file at the same position as when you called pause on it. This means that if you wanted to play the audio file from the beginning again and you just called play() it would continue playing from where it left off. That’s where the load() function comes into play — this will tell the audio track to start loading if it wasn’t set to preload and it will reset the track back to the beginning.

The only other thing to mention is the currentTrack variable. I use this to keep track of which of the songs is currently selected and the user wasn’t to listen to.

Linux Tutorial: Fix Heartbleed on Ubutntu 14.04

Leave a comment Standard

I’ll keep this short and sweet!

sudo apt-get update && sudo apt-get install --only-upgrade bash

PHP Tutorial: Number Guessing Game (no database required)

Comments 4 Standard

Wow I guess it’s been a while since I’ve posted anything. I’ve been busy moving and working on my games but I did find a few minutes to make this little game. This randomly picks a number and then lets the player try to guess the number. If you like this game you might also like my Ajax Tic-Tac-Toe game or Ajax Hangman game. This assumes that you’ve already done some of my other game tutorials so you are familiar with my game manager and how it works. You can read the code below or you can be lazy and can download the code or view a working example.

<?php
/***
* File: oop/class.guessnumber.php
* Author: design1online.com, LLC
* Created: 8.21.2014
* License: Public GNU
***/

class guessnumber extends game
{
    var $guesses;              //int - maximum guesses per game
    var $randomNumber = null;  //int - the random number they're trying to guess
    var $min = 50;             //int - the lowest value the random number can have
    var $max = 500;            //int - the highest value the random number can have

    /**
    * Purpose: default constructor
    * Preconditions: none
    * Postconditions: parent object started
    **/
    function guessnumber()
    {
        /**
        * instantiate the parent game class so this class
        * inherits all of the game class's attributes 
        * and methods
        **/
        game::start();
    }
    
    /**
    * Purpose: start a new guessing game
    * Preconditions: maximum number of guesses
    * Postconditions: game is ready to be displayed
    **/
    function newGame($max_guesses = 5, $min = null, $max = null)
    {
        //set the min and max values for the random number
        if (!$min)
            $min = $this->min;
         else
            $this->min = $min;
            
        if (!$max)
            $max = $this->max;
        else 
            $this->max = $max;
            
        //setup the game
        $this->start();
        
        //generate the random number
        $this->randomNumber = rand($min, $max);
            
        //set how many guesses they get before it's a game over
        if ($max_guesses)
            $this->setGuesses($max_guesses);
    }
    
    /**
    * Purpose: set or retrieve maximum guesses before game over
    * Preconditions: the data submitted from the form
    * Postconditions: guess is calculated
    **/
    function playGame($postdata)
    {
        //player pressed the button to start a new game
        if ($postdata['newgame'] || empty($this->guesses))
            $this->newGame();
            
        //player is trying to guess a number
        if (!$this->isOver() && $postdata['number'])
            echo $this->guess($postdata['number']);
                
        //display the game
        $this->displayGame();
    }
    
    /**
    * Purpose: set or retrieve maximum guesses they can make
    * Preconditions: amount of guesses (optional)
    * Postconditions: guesses has been updated
    **/
    function setGuesses($amount = 0)
    {        
        $this->guesses += $amount;
    }
    
    /**
    * Purpose: display the game interface
    * Preconditions: none
    * Postconditions: start a game or keep playing the current game
    **/
    function displayGame()
    {
        //while the game isn't over
        if (!$this->isOver())
        {
            echo "<div id=\"range\">I'm thinking of a number from " . number_format($this->min) . " to " . number_format($this->max) . ".</div>
                  <div id=\"select_number\">
                        Your Guess:
                        <input type=\"text\" name=\"number\" />
                        <input type=\"submit\" name=\"submit\" value=\"Guess\" />
                  </div>";
        }
        else
        {
            //they've won the game
            if ($this->won)
                echo successMsg("Congratulations you guessed my number $this->randomNumber! You've won the game.<br/>
                                Your final score was: $this->score");
            else if ($this->health < 0)
            {
                echo errorMsg("Game Over! Good try, my number was $this->randomNumber.<br/>
                                Your final score was: $this->score");
            }

            echo "<div id=\"start_game\"><input type=\"submit\" name=\"newgame\" value=\"New Game\" /></div>";
        }
    }
    
    /**
    * Purpose: guess a number in this word
    * Preconditions: a game has started
    * Postconditions: the game data is updated
    **/
    function guess($number)
    {            

        if ($this->isOver())
            return;

        if (!$number || !is_numeric($number))
            return errorMsg("Oops! Please enter a number.");
            
         if ($number < $this->min)
            return errorMsg("You must enter $this->min or higher.");
            
         if ($number > $this->max)
            return errorMsg("You must enter $this->max ot lower.");

        
        //if the word contains this number
        if ($number == $this->randomNumber) {
            $this->won = true;
        }
        else //word doesn't contain the number
        {
        
            //reduce their health
            $this->setHealth(ceil(100/$this->guesses) * -1);
            
            //calculate 1/6 the random number to get a distance factor
            $distance = floor($this->randomNumber / 8);
            

            if ($this->isOver())
                return;
            else {
                if ($number < $this->randomNumber - ($distance * 4) || $number > $this->randomNumber + ($distance * 4))
                    return errorMsg("So cold.");
                else if ($number < $this->randomNumber - ($distance * 3) || $number > $this->randomNumber + ($distance * 3))
                    return errorMsg("You're getting warmer...");
                else if ($number < $this->randomNumber - ($distance * 2) || $number > $this->randomNumber + ($distance * 2))
                    return errorMsg("Warm, Warm, Warm.");
                else if ($number < $this->randomNumber - $distance || $number > $this->randomNumber + $distance)
                    return errorMsg("You getting hot!");
                else
                    return errorMsg("You're really hot!!");
            }
        }
    }
}

Angular Autocomplete with Complex Object Arrays — NO JQUERY

Leave a comment Link

There are many times when you want to perform an autocomplete that works by searching all of the object properties and not just a single field or the key. If so you’ll enjoy this plunker.

Angular-UI Tutorial: Unique Filter on Object Arrays

Comments 2 Link

This plunker will show you how to use the unique filter included in the Angular UI utilities to strip out duplicates in an object array. Enjoy!

PHP Tutorial: Dynamically Cache and Combine All CSS Files

Comments 10 Standard

Does your website use a lot of CSS files? If so did you know you can increase your website’s speed by caching and combining all of your CSS files into a single file?

Why Is It Useful?

Every time your browser loads a page on your website it has to make a request for all of the resources included or displayed on your page. Resources consist of things like image files, JS files, XML files and CSS files. If you find that you have more than one CSS file that you need to include on your website the requests required to load each of those files can sometimes take longer than a single request to load a single larger file. If you combine and cache your CSS files into a single file you can improve your website’s response time.

What’s a Cache?

A cache is a snapshot of a file at a moment in time. Typically you want to cache files that are static, meaning they don’t change often. If your file changes are fairly frequent then you end up generating a cache file too frequently for it to be of any use. That’s why this is a great technique to use with CSS files since they aren’t static but they don’t change that often either.

Setup

  1. Create two files, one called cache.css and another called cache.php.
  2. Place both of them in the same folder directory that you keep your style sheets in. We’ll use the CSS file to create a snapshot of all of our CSS files in our style sheet directory and we’ll use the PHP file to check generate and return the contents of our cache file.
  3. Change the permissions on the cache.php file to 0755. This will allow your PHP file to modify your cache.css file.
  4. Update the website page headers so instead of loading each of your individual .css files you’re now only loading your cache.php file

Before

<html>
     <head>
		<link href="stylesheets/filename1.css" rel="stylesheet" type="text/css" />
		<link href="stylesheets/filename2.css" rel="stylesheet" type="text/css" />
		<link href="stylesheets/filename3.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
          This is what the header would look like before updating to the single file. Notice how you're making reference links to multiple .css files. Each of those requires your browser to make a request in order to load the three separate files.
    </body>
</html>

After

<html>
     <head>
		<link href="stylesheets/cache.php" rel="stylesheet" type="text/css" />
    </head>
    <body>
          This is what the header looks like. It now references our cache.php file which will give it the cached version of our CSS files.
    </body>
</html>

The Code

Inside of your cache.php file put the following code:

<?php 
/**
* File: cache.php
* Author: design1online.com, LLC
* Purpose: updates and loads the current css cache file
**/
$path = "path_to_your_public_directory/";
$dir = "name_of_your_css_folder_directory/";
$cachefilename = "cache.css";
$files_to_cache = array(
 	"filename1.css",	//add all of your css file names
 	"filename2.css", 	//that are inside your_css_folder_directory
 	"filename2.css"		//that you want to cache
);

//check for file changes
if ($handle = opendir($path . $dir)) {
 	while (false !== ($entry = readdir($handle))) 
        {
 		//if any of the $files_to_cache have changed recently we need to update the cache file
                //to do this we compare the last modified times on the files to see if one of our
                //$files_to_cache was updated more recently than our cache file
 		if (is_file($entry) && is_readable($entry) && substr($entry, -3, 3) == "css" &&
 		date(date(filemtime($entry)) > filemtime($path . $dir . $cachefilename)))
                {
                        //we've found a $file_to_cache that was updated more recently than the cache file
                        //so open the cache file and prepare to overwrite the contents with the changes
			$cachefile = fopen($path . $dir . $cachefilename, "w");

			//write all of the $files_to_cache to the cache file
			foreach ($files_to_cache as $filename)
			{
				$copyfile = fopen($path . $dir . $filename, "r");
				fwrite($cachefile, fread($copyfile, filesize($filename)));
				fclose($copyfile);		
			}

                        //close the connection to the cache file - this also updates the last modified time
                        //on the cache file so that it won't try to re-generate the cache file again until 
                        //a $files_to_cache has been changed again
			fclose($cachefile);

                        //since we found one file that was changed and we've updated the cache file 
                        //we no longer need to look for other files that may have potentially been updated, so 
                        //this will tell the loop to stop running
			break;
		}
        }
}

//output the contents of the cache file
//the header content type will make sure the
//browser interprets the file contents as text/css
header("Content-type: text/css");

//clear the buffers to prevent overflows
ob_clean();
flush();

//open and return the contents of the cachefile
//you'll notice your cache file contains all of your
//$files_to_cache merged into one
readfile($path . $dir . $cachefilename);

Last But Not Least

Update the $path, $dir and $cachefile name as needed at the top of your new cache.php file. This will tell the file where to find your css stylesheets and which ones you want to cache. Since this script looks at the last modified dates on our $files_to_cache we need to make sure and update one of the files in our $files_to_cache list in order for it to generate our initial cache.css file. To do this I would recommend making a change to one of your $files_to_cache or using touch to update the last modified time stamp on one of your $files_to_cache. Once you’ve done that you can view any page you updated to use cache.php as it’s stylesheet (see setup step 4) in any web browser to view the results of your efforts.