Custom colored UITabBar icons – an update

In a previous blogpost, which is still one of my most popular ones, I gave a method on how to create a tabBar with complete custom icons. It’s really more like a hack. But so far I haven’t found anything better yet. My method didn’t use any private API’s, and I have already succesfully submitted two apps in the App Store: Kust Afvalgids and Cultuurmarkt. The former one suffers from a little inconvenience: when you touch a given icon on your tabBar, you can still see the sort of “transparent” area covering the location of the icon, to indicate that this icon is active. You can get rid of that though. Here’s how.

A custom UITabBarItem implementation

To get rid of the annoying active state of an icon, all you have to do is implement your own class which inherits from UITabBarItem, and override the different icons. Then in the code you can create an instance of your custom UITabBarItem class, and assign it to the tabBarItem of your controller. Here’s how I implemented the subclass:

// CustomTabBarItem.h
@interface CustomTabBarItem : UITabBarItem {
    UIImage *customHighlightedImage;
    UIImage *customStdImage;
}

@property (nonatomic, retain) UIImage *customHighlightedImage;
@property (nonatomic, retain) UIImage *customStdImage;

@end
// CustomTabBarItem.m
#import "CustomTabBarItem.h"

@implementation CustomTabBarItem

@synthesize customHighlightedImage;
@synthesize customStdImage;

- (void) dealloc
{
    [customHighlightedImage release]; customHighlightedImage=nil;
    [customStdImage release]; customStdImage=nil;   
    [super dealloc];
}

-(UIImage *) selectedImage
{
    //return self.customHighlightedImage;
    return nil;
}

-(UIImage *) unselectedImage
{
    //return self.customStdImage;
    return nil;
}

@end

Two methods need to be overridden: selectedImage, which returns the icon you want to display in the active state; and unselectedImage, which returns the icon you want to display in the inactive state. As you can see in my code, I simply return nil. This indicates that I have no icons for both active & normal state. If you’ve read my previous blog post, you know I have just created different background images for the tabBar. Each background image already has the different images in it, so I don’t need separate icons for tabBarItems. If you do work like that, you just return the correct images in the correct methods (the parts which are commented out).

Now in each sub-controller of your tabBar, you make an instance of the CustomTabBarItem class, and assign it to self.tabBarItem. Example:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
        
    //tabItem.customHighlightedImage=[UIImage imageNamed:@"icon_active.png"];
    //tabItem.customStdImage=[UIImage imageNamed:@"icon_inactive.png"];
        
    self.tabBarItem=tabItem;
    [tabItem release]; 
    tabItem=nil;
}

Again, I provided sample code to show you how to specify the different images. Each time your view will appear, your tabBarItem will be set to your custom implementation. There is still one (little) problem: If you tap a selected tabBarItem twice, the active state will show. If you have a view transition, like a flip view animation, the active state will show. I have not found a way around this yet. I guess it’s somewhere in the internals of cocoa and I have no clue how to solve it. If you do find a solution, please let me know via the comments.

Conclusion

In my latest iPhone app, I have used the system of my previous blogpost to display different background images for the tabBar per controller. Then I have combined this with a custom tabBarItem implementation to get rid of the highlight state by returning nil for selectedImage & unselectedImage. If you don’t mind that this isn’t totally bug free, then this method is a good solution I think. Apps get approved with this code, since you don’t call any private API methods.

Update – For those of you who want to see an example of the two methods combined, here’s a piece of code:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self setCustomTabBarItem];
    
	for(UIView *view in self.tabBarController.tabBar.subviews) {
		if([view isKindOfClass:[UIImageView class]]) {
			[view removeFromSuperview];
		}
	}
	
    UIImageView *imgView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"custom_tabbar_image1.png"]] autorelease];
    imgView.frame = CGRectMake(imgView.frame.origin.x, imgView.frame.origin.y - 2.0, imgView.frame.size.width, imgView.frame.size.height);
	[self.tabBarController.tabBar insertSubview:imgView atIndex:0];
}

- (void)setCustomTabBarItem
{
    // Remove highlight from tabbar item:
    CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
        
    self.tabBarItem=tabItem;
    [tabItem release]; 
    tabItem=nil;
}

Forcing the translator locale in Twig

I’ve been using Symfony2 for only a week now, but I’m already fully immersed in the SF2 world. Learning Symfony2, YAML, Twig and MongoDB all at the same time. This means I’m spending lots of time either in the official documentation, or on Google. For a test case, I’m localizing a small web application, so it’s available in 2 languages. One problem I was facing was that I needed to force certain translation tags in a specific language. Example: The link text for the French version of the current page doesn’t have to be “French”, but rather “fran├žais”, if a French person is to understand it. If you’re also using Twig, you should be familiar with the syntax to provide a translation for a text or tag:

{% trans %}Hello world!{% trans %}

To force this in the French locale (provided you have a French localization in place), you can use the “in” modifier:

{% trans in 'fr' %}Hello %name%!{% trans %}

I couldn’t find this anywhere in the Symfony2 documentation. But luckily the code itself produced the information I needed. Hopefully this saves you a search in the code.

Custom MongoDB Security Provider in Symfony2

At work, we’ve decided to upgrade our entire workflow. One of the main decisions we’ve had to make, was which PHP framework we wanted to start using. I have quite some experience with Zend Framework, so I was kinda biased towards that framework. However, the team didn’t want to create a new codebase on ZF1. Since waiting till ZF2 is released was not an option, we decided to give Symfony2 a try.

To each build up some experience with SF2, each team member was tasked to create a blog system with SF2. I decided to use MongoDB as my preferred method of data persistence, since the rest of the team also showed quite some interest in using this technology. Following the documentation on the Symfony2 website, I quickly set up my BlogBundle, with an administration interface and everything. The next step was to secure the backoffice with user credentials coming from a MongoDB collection. This is when a whole world of hurt opened up: almost no information on the intertubes on how to do this. I figured out I would need a custom UserInterface and UserProviderInterface. People kept referring to the FOSUserBundle. The Friends of Symfony are a cool bunch, but I wanted to write my own code, as part of the learning process. A couple of hours and an almost-headache later, I had figured it out. Since there will probably be other people struggling with this, here’s how I did it.

Setting up the Security configuration

When you want to create parts of an application which need to be accessed by logged in users, you need to create firewall rules for this. Here’s my configuration.
File: /app/config/security.yml

security:
    encoders:
      MediaMates\BlogBundle\Document\User: md5

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        default:
          id: mm_security_provider

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
          pattern: ^/blog/backoffice/login$
          anonymous: ~
          security: false

        secured_area:
            pattern:    ^/
            anonymous: ~
            logout:
              path: /blog/backoffice/logout
              target: /blog
            form_login:
                login_path: /blog/backoffice/login
                check_path: /blog/backoffice/login_check

    access_control:
        - { path: ^/blog/backoffice, roles: ROLE_ADMIN}

Important here is the “providers” part. As you can see, I specified a provider named “default”. I want to use a custom provider, and I refer to it in the services via its ID. The ID here is “mm_security_provider”. The rest of the config is pretty standard. The firewall will allow anonymous users to the entire path, but the access controll requires an admin role if you access anything from the backoffice.

The “mm_security_provider” is still unknown to the dependency injection container, so we have to configure it. Here’s the configuration:
File: /src/MediaMates/BlogBundle/Resources/config/services.yml

services:
  mm_service_blog:
    class: MediaMates\BlogBundle\Service\Blog
    arguments: [@doctrine.odm.mongodb.document_manager]
  mm_service_user:
    class: MediaMates\BlogBundle\Service\User
    arguments: [@doctrine.odm.mongodb.document_manager]
  mm_security_provider:
    class: MediaMates\BlogBundle\Security\MongoProvider
    arguments: [@mm_service_user]

The “mm_security_provider” is configured to create an object of class MediaMates\BlogBundle\Security\MongoProvider. The provider will need to access the User collection in my MongoDB. I have taken a Service Oriented Architecture (SOA) approach, where I gather all business intelligence in so called Service Classes. My provider needs the correct service to talk to the database. So I configured an argument for the constructor which refers to my User service. In case you don’t work with a SOA, you can just replace the argument with @doctrine.odm.mongodb.document_manager. This should inject the Doctrine MongoDB ODM into your provider instead of a User service class.

Implementing the UserProviderInterface

Now that we have made the necessary configurations, it’s time to implement the actual custom provider that will allow the Security layer to talk to a MongoDB collection. First I’ll show you the code, and then I’ll explain what happens.
File: /src/MediaMates/BlogBundle/Security/MongoProvider.php

namespace MediaMates\BlogBundle\Security;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

use MediaMates\BlogBundle\Service;

class MongoProvider implements UserProviderInterface
{
    protected $userService;
    
    public function __construct (Service\User $userService)
    {
        $this->userService = $userService;
    }
    
    public function loadUserByUsername($username)
    {
        $user = $this->userService->getUserByUsername($username);

        if (null === $user) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found', $username));
        }
        
        return $user;
    }

    public function refreshUser (UserInterface $user)
    {
        return $this->loadUserByUsername( $user->getUsername() );
    }

    public function supportsClass($class)
    {
        return $class === 'MediaMates\BlogBundle\Document\User';
    }
}

The most important part is to extend Doctrine\ODM\MongoDB\DocumentRepository and implement Symfony\Component\Security\Core\User\UserProviderInterface. The interface only requires you to implement 3 methods: “loadUserbyUsername”, “refreshUser” and “supportsClass”. I have also implemented the constructor. The argument of the constructor, in my case, is my User service, like I specified earlier in my services configuration. In case you changed the configuration to the Doctrine MongoDB ODM, then you will have to update the constructor argument to be of the correct type.

loadUserByUsername is called by the Security layer, when the given username & password need to be verified. You have to find a user by his/her username. If this fails, you throw a UsernameNotFoundException. If you do find a user, you just return the object.

refreshUser is called by the Security layer, whenever it deems it necessary to reload the given user. This already happens after login, because it is possible that the object retrieved for verifying the credentials, is incomplete. With refreshUser, you should return a complete User object.

supportsClass returns wether or not the given class name is supported by your provider.

Implementing the UserInterface

The hard part is over now. Only thing remaining, is creating a User model which implements the correct interface, so that the Security layer knows what to expect. Here’s my model, complete with MongoDB ODM annotations, properties, getters & setters, and required methods by the interface.
File: /src/MediaMates/BlogBundle/Document/User.php

namespace MediaMates\BlogBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @MongoDB\Document
 */
class User implements UserInterface
{
    /**
     *
     * @MongoDB\Id
     */
    protected $userID;
    
    /**
     * @MongoDB\String
     */
    protected $username;
    
    /**
     * @MongoDB\String
     */
    protected $password;
    
    /**
     * @MongoDB\String
     */
    protected $firstname;

    /**
     * @MongoDB\String
     */
    protected $lastname;
    
    /**
     * @var string A salt for the password
     */
    protected $salt = "Blog";
    
    /**
     * The roles a user has
     *
     * @return array
     */
    public function getRoles ()
    {
        return array(
            'ROLE_ADMIN'
        );
    }
    
    /**
     * Erases the credential information
     */
    public function eraseCredentials ()
    {
        $this->password = null;
    }
    
    /**
     * Verifies if given user equals the current user
     *
     * @param mixed $user
     * @return Boolean
     */
    public function equals (UserInterface $user)
    {
        return ($this->getUsername() === $user->getUsername());
    }
    
    /**
     * Returns the salt
     *
     * @return string
     */
    public function getSalt ()
    {
        return $this->salt;
    }

    /**
     * Get userID
     *
     * @return id $userID
     */
    public function getUserID()
    {
        return $this->userID;
    }

    /**
     * Set username
     *
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * Get username
     *
     * @return string $username
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Set password
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Get password
     *
     * @return string $password
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Set firstname
     *
     * @param string $firstname
     */
    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;
    }

    /**
     * Get firstname
     *
     * @return string $firstname
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * Set lastname
     *
     * @param string $lastname
     */
    public function setLastname($lastname)
    {
        $this->lastname = $lastname;
    }

    /**
     * Get lastname
     *
     * @return string $lastname
     */
    public function getLastname()
    {
        return $this->lastname;
    }
}

Methods required by the interface are: “getUsername”, “getSalt”, “getRoles”, “eraseCredentials”, “equals”. I hard coded the getRoles return value. The eraseCredentials method should remove sensitive information from your model. In our case, the password. The equals method lets you implement how two User models should be compared. I just made a comparison against the username, but of course this could (an should!) be more elaborate.

Conclusion

As it turns out, writing your own provider for authenticating against a MongoDB User collection wasn’t all that hard. You just have to figure out which classes & methods to implement, and it just works. Nevertheless, Symfony2 is quite young, and documentation still is scarce. The official documentation has some examples yes, but they usually don’t go beyond the basics. The cookbook has some specific use cases, which is nice. I hope this write-up will help you to implement your custom provider. If you have any suggestions or remarks, don’t hesitate to comment.

Running Zend Framework modules from a Phar file

Using Zend Framework as an MVC application is probably the most common usage examples for Zend Framework. When you create such an MVC application, you will probably have heard about modules: reusable components of your application. Ideally these modules are drop-in and require little or no configuration before you can use them. This way you don’t have to recreate the same old “news module” for every client or customer. In my case, I usually just copy and paste the module from one base project into a new project. That’s easy. But it would be cooler to package your module as a Phar file, and run that file instead.

Bootstrapping Phar modules

Unfortunately, there is no support in Zend Framework 1.x for running a module from a Phar file. But we can build that support ourselves! With a little knowledge about bootstrapping, we can integrate Phar support for modules.

During the bootstrapping process, so called resources are used to prepare the application for running. A lot of resources are readily available, like a database resource for your database connection, cachemanager for configuring caches, … And there is a “Modules” resource. This one takes care of bootstrapping each module. The purpose of bootstrapping a module, is mainly to get the module namespace known in your application and to tell the FrontController where it can find a module.

This built-in modules resource loops over each directory it finds inside the specified modules folder, and then finds and executes the Bootstrap.php file inside each module. Since it iterates over directories, any Phar file we put there is just ignored. We will have to make sure that Phar files also get bootstrapped. We can do this by providing our own modules resource. The built-in one is called Zend_Application_Resource_Modules, and resides in Zend/Application/Resource/Modules.php. We don’t have to start hacking that file to add Phar support. We can create our own Modules.php resource, which extends the Zend Framework one. By providing our own implementation of an existing resource, we can add extra functionality on top of the existing code, without having to hack the existing code.

Here’s my implementation:

// file: library/MyLib/Application/Resource/Modules.php
class MyLib_Application_Resource_Modules extends Zend_Application_Resource_Modules
{
    /**
     * Initialize modules
     *
     * @return array
     */
    public function init()
    {
		// call parent functionality:
		parent::init();
		
		// find out module directory:
        $bootstrap = $this->getBootstrap();
        $bootstrap->bootstrap('FrontController');
        $front = $bootstrap->getResource('FrontController');
		$modulesDirectory = $front->getModuleDirectory() . '/modules'; // TODO hard coded...
		
        $default = $front->getDefaultModule();
        $curBootstrapClass = get_class($bootstrap);

		// find all PHAR modules, and bootstrap those:	
		$iterator = new DirectoryIterator($modulesDirectory);
		
		foreach ($iterator as $file) {
			if (!$file->isDot() && strpos($file->getFilename(), '.phar') > 0) {
				$module = str_replace('.phar', '', $file->getFilename());
				
				// bootstrap the module:
				$this->_bootstrapPharModule($file, $bootstrap, $default, $curBootstrapClass);
				
				// add to the modules in the FC
				$front->addControllerDirectory('phar://' . 
						$file->getPath() . 
						DIRECTORY_SEPARATOR . 
						$file->getFilename() . 
						DIRECTORY_SEPARATOR . 
						'controllers', 
				$module);
			}
		}
		
		return $this->_bootstraps;
    }
	
	/**
	 * Bootstraps a single PHAR module
	 *
	 * @param DirectoryIterator $file
	 * @param Zend_Application_Bootstrap $bootstrap
	 * @param string $default
	 * @param string $curBootstrapClass
	 * @return void
	 * @throws Zend_Application_Resource_Exception When bootstrap class was not found
	 */
	protected function _bootstrapPharModule (DirectoryIterator $file, $bootstrap, $default, $curBootstrapClass)
	{
		$fullPharPath = $file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename();
		include($fullPharPath);
		$module = str_replace('.phar', '', $file->getFilename());
		
		$bootstrapClass = $this->_formatModuleName($module) . '_Bootstrap';
		if (!class_exists($bootstrapClass, false)) {
			$bootstrapPath  = 'phar://' . $fullPharPath . '/Bootstrap.php';
			if (file_exists($bootstrapPath)) {
				$eMsgTpl = 'Bootstrap file found for module "%s" but bootstrap class "%s" not found';
				include_once $bootstrapPath;
				if (($default != $module)
					&& !class_exists($bootstrapClass, false)
				) {
					throw new Zend_Application_Resource_Exception(sprintf(
						$eMsgTpl, $module, $bootstrapClass
					));
				} elseif ($default == $module) {
					if (!class_exists($bootstrapClass, false)) {
						$bootstrapClass = 'Bootstrap';
						if (!class_exists($bootstrapClass, false)) {
							throw new Zend_Application_Resource_Exception(sprintf(
								$eMsgTpl, $module, $bootstrapClass
							));
						}
					}
				}
			} else {
				// nothing to bootstrap, so let's move on
				return;
			}
		}
		
		if ($bootstrapClass == $curBootstrapClass) {
			// If the found bootstrap class matches the one calling this
			// resource, don't re-execute.
			return;
		}

		$moduleBootstrap = new $bootstrapClass($bootstrap);
		$moduleBootstrap->bootstrap();
		$this->_bootstraps[$module] = $moduleBootstrap;
	}
}

What happens is not very difficult: we find each Phar module, find the Bootstrap.php file in it, execute it and let the FrontController know we have bootstrapped a new module. The actual reading from a Phar file is handled transparently by the Phar module, and doesn’t need any special treatment in Zend Framework. Credits for this code should actually go to the Zend Framework: I looked into the original file, and almost literally copied what I could reuse.

Conclusion

Adding Phar support isn’t very hard. Too bad it isn’t built-in the framework, but luckily for us, the framework is flexible enough so we can add it. Once the module is bootstrapped, all classes can be used in your application. Once the FrontController knows where to locate your module, it’s accessible through the standard /module/controller/action request scheme. If you want to know more about creating your own Phar file, I recommend reading this excellent article from Cal Evans. It gave me exactly the information I needed for building my own Phar files.

Automated Javascript unit testing with JsTestDriver

Introduction

In a previous post about unit testing your Javascript, I explained how you can unit test your own Javascript library code with the QUnit test suite. Now that we can unit test our Javascript, it would be cool to automate the process. We’ll accomplish that by using JsTestDriver, a Javascript test runner, built in Java.

What is a “test runner”?

A test runner is something that runs your tests. In my previous blog post on the subject, we wrote an HTML file which then contained references to the library & scripts to run. By opening that HTML file in a web browser, we ran our tests. In this case, the test runner was the browser itself, and the HTML file was a wrapper for your tests.

Having a browser as a test runner is good, because you are then testing your code in that specific browser. However, it is tedious work to open up the HTML wrapper in each and every browser you have available. Testing takes ages this way. What would be better is some sort of “thing” that can run your test code in a lot of browsers, preferably in parallel, with as little effort as possible. That’s where JsTestDriver comes in sight.

How does JsTestDriver work?

JsTestDriver simply works by starting a server, listening on a port (usually port 4224), where you connect browsers to. All the kinds of browsers you want your code tested agains, should connect to the JsTestDriver server. JsTestDriver then “captures” your browser, and puts it available for running tests in. Then you execute your test suite, and each captured browser will execute your test code. Here’s a small schema I copied from their site:

jstestrunner_overview.png

This kind of setup makes it easier to test your code against multiple browsers. Usually you won’t have different browsers on different OS’es available. What can help, is building virtual machines in VirtualBox and installing the browsers in them. Then all you have to do is point these browsers to the same JsTestRunner server instance, and you have your own little test farm.

Setting JsTestDriver server up

First thing you need to do, is download JsTestDriver from the project page over at google. At the time of writing, the most recent version was JsTestDriver-1.2.2.jar. I have a dedicated folder for it, where I also added the Code Coverage plugin. But more on that plugin later.

Get the server running by entering the following in your shell:

	java -jar /path/to/JsTestDriver-1.2.2.jar --port 4224

Now you have to leave your shell open to keep the server running. If all went well, you should now be able to surf to http://localhost:4224 and see the following:

Screen shot 2011-01-24 at 18.46.14.png

You can now go ahead, and add your browser to the list of browsers that will execute the test suite, by clicking the link “Capture This Browser”. You should now see this in your browser:

Screen shot 2011-01-24 at 18.48.21.png

Now your browser is ready and waiting for tests to come in.

Preparing your test suite

JsTestDriver can work with multiple Javascript test suites, but adheres to the JUnit standard. Therefor, if you want to keep on using QUnit, you’ll have to use an adapter. Luckily there’s one readily available, and using the adapter is very easy.

To use the adapter, first go to http://code.google.com/p/js-test-driver/source/browse/#svn/trunk/JsTestDriver/contrib/qunit/src and download equiv.js and QUnitAdapter.js to your test suite. Next you have to tell JsTestRunner where to find these files by adding them into your config file.

For each set of Javascript unit tests you want to run, you have to create a config file for JsTestDriver. In this config file, you specify the location of your files, and optionally which plugins you want to use. Here’s my config file:

server: http://localhost:4224

load:
# Add these lines to load the equiv function and adapter in order, before the tests
- external/equiv.js
- external/QUnitAdapter.js

# Load jQuery external dependency for the TAG library:
- external/jquery.js

# Load the files we want to test:
- lib/tag.js
- lib/tag/*.js

# Load the tests:
- tests/tag.js
- tests/tag/*.js

As you can see, first I load the files necessary for the QUnit adapter. Then I load jQuery, because it’s a dependency for my own library. Next all files of my library are loaded, and lastly the unit tests. Save this file as “jsTestDriver.conf” alongside your other files.

Limitations

The QUnitAdapter does a nice job converting the QUnit tests to something JsTestDriver can understand. However, it is not possible to do asynchronous tests with JsTestDriver. This means that any test where you use the “start()” and “stop()” functionality of QUnit, you will get an error. Keep this in mind when running your tests. They may run fine when you use the browser as your test runner, but fail when you use JsTestDriver.

Running your tests

Now you have to open up a new shell, and just enter the following:

java -jar /path/to/JsTestDriver-1.2.2.jar --config /path/to/jsTestDriver.conf --tests all

If you’re familiar with PHPUnit, you should see similar output:

Screen shot 2011-01-24 at 19.09.54.png

A dot represents a passed test. An “F” means a Failed test, and “E” indicates that an error happened.

If you have more browsers installed, just go ahead and capture them by surfing to http://localhost:4224. If you then run your test suite again, you’ll see just how many of your tests passed/failed/errored in each browser.

Code coverage

Unit tests are all great, but it’s even better if you had an idea about which parts of your code were actually executed, and which parts were skipped. There is a plugin for this available at the project website. Download, and copy coverage-1.2.2.jar in a “plugins” directory alongside the JsTestDriver-1.2.2.jar file.

Now we need to add it to the jsTestDriver.conf for our test suite:

server: http://localhost:4224

load:
# Add these lines to load the equiv function and adapter in order, before the tests
- external/equiv.js
- external/QUnitAdapter.js

# Load jQuery external dependency:
- external/jquery.js

# Load the files we want to test:
- lib/tag.js
- lib/tag/*.js

# Load the tests:
- tests/tag.js
- tests/tag/*.js

# Code coverage
plugin:
- name: "coverage"
  jar: "/path/to/plugins/coverage-1.2.2.jar"
  module: "com.google.jstestdriver.coverage.CoverageModule"

With the config updated, it’s time to run the tests again. We run the same command as previously mentioned, but with the added option to specify a code coverage output folder.

java -jar /path/to/JsTestDriver-1.2.2.jar --config /path/to/jsTestDriver.conf --tests all --testOutput /path/to/coverage/output

If the specified output directory doesn’t exist yet, it will be created, and in it you will now find some XML files and a jsTestDriver.conf-coverage.dat file in the directory you specified. These files contain all the data of the coverage report, exported into the a format that can be used by other programs to show you a nice overview. If you have LCOV installed on your system, you can convert the .dat file easily to a readable HTML file by entering this command:

genhtml /path/to/jsTestDriver.conf-coverage.dat.

If you now open up the generated index.html file in your favorite browser, you get something like this:

LCOV - jsTestDriver.conf-coverage.dat_1295967277319.png

With this coverage report, you can see which parts of your code were executed during the unit tests, and more importantly, which parts weren’t. It’s up to you now, to update your unit tests to get your coverage percentage higher. We would all love to achieve 100% coverage, but I think that if you have about 70% coverage, your library is already well tested.

Conclusion

With JsTestDriver, it’s possible to quite easily automate your Javascript unit testing in more browsers. The process of capturing browsers, and then distributing the unit tests in parallel to them certainly speeds up the whole unit testing itself. Code coverage is very nice to have, as it presents you with a document you can show your client or employer.

The only downside I have discovered so far, is the lack of the ability to create asynchronous tests. Especially with so much website functionality relying on AJAX to update the interface, you really miss this feature. On the other hand, you can construct your code in such a way, that not testing AJAX functionality doesn’t necessarily result in big chunks of code without coverage. Only thing left is to get as many Virtual Machines and browsers connected to the JsTestDriver server, and get testing!