Wallsplattr 0.4 out now

After I spent some time working on the 0.3 version in isolation I then started talking to some people at work, mainly Stu, Gary and Dan. They all pointed out various things that actually started to bug me, so 0.4 addresses those issues. There is nothing visually different between the 0.3 and 0.4 releases, however you have much more control over the google map in version 0.4.

The newly created ChangeLog clearly indicates the changes for 0.4, but for those not wanting to venture off to another site, here it is:

  • Fixed GH-1 Moved maxMarkerCount to application.ini
  • Fixed GH-2 Make the location and Zoom of the Google map configurable in application.ini
  • Fixed GH-3 Markers are now removed on a rolling basis rather than all at once
  • Fixed GH-4 Now displays an Info Window rather than going direct to twitter.com
  • Added ChangeLog.markdown
  • Added docs/Map.markdown to help with map settings
  • docs/TweetStructure.markdown to help with understanding the data that is available in a tweet
  • Validation fixes for HTML and JavaScript
  • Moved phpunit.xml.dist file to top level, which was a comment I saw on twitter about checking out an application and running phpunit from root (Makes sense)
  • Allowed the configuration of the type of map to be viewed (application.ini)
  • Other general tidy up fixes

I quite like this version, and I’m starting to learn a lot more about JavaScript, which is really the goal of this project: To Learn. I think docs/Map.markdown is an interesting file to checkout as that talks about how you can configure the map, which is where all the changes for this release are located.

In this release I have tried to be more more verbose with what is going on, so you can see many new .markdown files have been created and I’ve tried to update the Docblocks as and where appropriate

0.4 version can be downloaded from here

Web Storage in HTML5

We have some really clever people in our office, all working on different things in their spare time. So it is quite nice to go around and see what people are looking at, and I saw someone playing around with HTML5 bits and bobs. I quite liked the Web Storage functionality so decided to have a go knocking up a little feature for our CRM system.

So, first of all I read the specification which is currently still work in progress, however most modern browsers already support Web Storage. The feature I wanted to write was to keep a local history of accounts viewed in our CRM. So you could click a tab and out popped the last x accounts that have been viewed and you could then click them to revisit the account. The example is a little contrived and you could easily do this in a multitude of other ways, but today I wanted to learn Local Storage.

The first issue that I needed to consider was not all browsers support this yet, so if it wasn’t supported I was simply not going to show the history tool. To find out if the browser is support you can write:

# See if the Local Storage feature is available in the browser
    var myStorage = {}

    myStorage.isLocalAvailable = function () {
        if ('localStorage' in window && window.localStorage !== null) {
            return true;
        } else {
            return false;
        }
    };

The main aspect here is the new ‘localStorage’ variable that we can check exists. This is the object we can use to deal with local storage. From now on we can wrap the functionality with this check to understand if we can use local storage or not. Some other wrapper functions I wrote were to get/set the item in local storage, simply so we had a common entry point. This is an advantage because if you wanted to use Session Storage rather than Local Storage there are less places to change the code. So here are my getter/setter functions

# Getter
    myStorage.getLocalItem = function (key) {
        if (myStorage.isLocalAvailable()) {
            return $.parseJSON(localStorage.getItem(key));
        } else {
            return null;
        }
    };

    # Setter
    myStorage.setLocalItem = function (key, value) {
        if (myStorage.isLocalAvailable()) {
            localStorage.setItem(key, JSON.stringify(value));
        }
    };

The reason we are stringifying and JSONifying is because the Local Storage can only have key/pair values of strings. I wanted to store an array of objects, which it simply doesn’t like. It took myself and Dan a fair amount of digging to find this out.

The last code snippet is the actual guts of the work, which looks for the details I want to store and then sets and gets them

myStorage.init = function () {
        //Local storage checks
        if (myStorage.isLocalAvailable()) {
            var username = $('#historyPopup > #historyUsername:first').html();
            var id = $('#historyPopup > #historyId:first').html();
            if ($('#historyBar').hasClass('hide')) {
                $('#historyBar').removeClass('hide');
            }

            // Create the HistoryItem
            var item = new HistoryItem(username, id);

            try {
                var items = [];
                var localItems = null;

                localItems = myStorage.getLocalItem('crmHistory');
                if (localItems !== null) {
                    items = localItems;
                }

                $(items).each(function () {
                    if (this.id === item.id) {
                        items.splice($(items).index(this), 1);
                    }
                });

                if (item.username !== null) {
                    if (items.length === 10) {
                        items.shift();
                    }
                    items.push(item);
                }

                items.reverse();

                $(items).each(function () {
                    $('#historyPopup > ul').append(
                            '

Javascript and Jenkins

As I’ve mentioned previously, we have now started to unit test our JavaScript code at work, and therefore have investigated other tools that can help out along the way. Since I am a PHP Developer and our team already have all of our builds running on our Hudson platform, I looked for similar tools.

We are now using the following tools for our JavaScript builds:

This all runs in the following build file on Hudson:

<project default="build" basedir=".">

    <property name="testdir" value="Test" />
    <property environment="env" />
    <property name="display" value="5" />
    <property name="port" value="4224" />

    <available file="${basedir}/Source/${testdir}" type="dir" property="test.present" />

    <target name="display">
        <echo>Running build for ${ant.project.name}</echo>
    </target>

    <target name="clean">
        <exec executable="rm">
            <arg line="-fr ${basedir}/build" />
        </exec>

        <mkdir dir="${basedir}/build/api"/>
        <mkdir dir="${basedir}/build/code-browser"/>
        <mkdir dir="${basedir}/build/coverage"/>
        <mkdir dir="${basedir}/build/logs"/>
        <mkdir dir="${basedir}/build/pdepend"/>
        <mkdir dir="${basedir}/build/sonar"/>
        <mkdir dir="${basedir}/build/graph"/>

    </target>

    <target name="php-codesniffer">
        <exec executable="php" dir="${basedir}/Source" output="${basedir}/build/logs/checkstyle.xml">
            <arg line="-dmemory_limit=-1" />
            <arg line="/usr/bin/phpcs" />
            <arg line="--report=checkstyle" />
            <arg line="--standard=Squiz" />
            <arg line="--extensions=js,css" />
            <arg line="--ignore=jquery" />
            <arg line="${basedir}/Source/" />
        </exec>
    </target>

    <target name="unit-tests" if="test.present">
        <exec executable="java" dir="${basedir}/Source/${testdir}">
            <arg line="-jar /local/services/hudson/common/javascript/jstestdriver/js-test-driver.1.2.2.jar" />
            <arg line="--port ${port}" />
            <arg line="--browser /usr/lib/firefox/firefox-bin" />
            <arg line="--testOutput ${basedir}/build/logs/" />
            <arg line="--tests all" />
            <env key="LD_LIBRARY_PATH" value="/usr/lib/firefox/" />
            <env key="PATH" value="${env.PATH}:/usr/lib/firefox/" />
            <env key="DISPLAY" value=":${display}" />
        </exec>
    </target>

    <target name="start-xvfb" if="test.present">
        <exec dir="/usr/bin" executable="Xvfb" failonerror="false" spawn="true">
            <arg line=":${display} -ac -screen 0 1024x768x16" />
        </exec>
        <echo taskname="start-xvfb" message="Using display ${display}" />
    </target>

    <target name="stop-xvfb" if="test.present">
        <exec executable='bash' failonerror="false" spawn="true">
            <arg line='ps -ef | grep "Xvfb :$1" | grep -v grep | awk "{print $2}" | xargs kill' />
        </exec>
    </target>

    <target name="generate-coverage" if="test.present">
        <exec executable='/local/services/hudson/common/tools/genhtml' failonerror='false'>
            <arg line="-t ${ant.project.name}" />
            <arg line="-o ${basedir}/build/coverage" />
            <arg line="${basedir}/build/logs/jsTestDriver.conf-coverage.dat" />
        </exec>
    </target>

    <target name="documentation">
        <exec executable="java" dir="${basedir}/Source">
            <arg line="-jar /local/services/hudson/common/javascript/jsdoctoolkit/jsrun.jar" />
            <arg line="/local/services/hudson/common/javascript/jsdoctoolkit/app/run.js" />
            <arg line="-t=/local/services/hudson/common/javascript/jsdoctoolkit/templates/jsdoc/" />
            <arg line="-d=${basedir}/build/api" />
            <arg line="-r=10" />
            <arg line="--verbose" />
            <arg line="-a" />
            <arg line="${basedir}/Source" />
        </exec>
    </target>

    <target name="build" depends="start-xvfb,display,clean,checkout-code,php-codesniffer,unit-tests,stop-xvfb,generate-coverage,documentation" />

    </project>

As you can see, most of our tools are deployed into /local/services/hudson/common/ which makes keeping them up to date a lot easier. We have capistrano scripts that deploy the tools to the Hudson master/slaves so they are all in sync. This is performed every time we create a new job on Hudson, again using Capistrano scripts.

We use the following Plugins in order to provide feedback:

We are still in the early stages of getting our JavaScript codebase on Hudson, so we are still getting to grips with the tools and what we can achieve. This is a great challenge coming from a PHP perspective, where we have achieved all this.

If you know of more/better tools then please let me know

Writing JavaScript unit tests with JsTestDriver

I’ve finally managed to get some traction on writing JavaScript unit tests at work, and the team decided to go with JsTestDriver. I really like JsTestDriver and have managed to get it fully reporting into our Hudson server, so thought I would take the opportunity to document what I’ve done so far.

First off we need to downloaded the latest release of JsTestDriver from here, and set the server running in the background

$ java -jar ~/jstestdriver/js-test-driver.1.2.2.jar --port 4224 --browser open

This runs the server in the background on port 4224 (it can be anything you like) and then opens the default browser (On the mac it is Chrome for me). Running this command should give you the following screen (depending on your default browser):

This now allows us to run JsTestDriver tests in the Chrome browser on port 4224. If you leave that running in the background we can now write our first unit test. Imagine this bit of code which adds a “Postage and Packaging” cost for an online shopping basket.

var basket = {}, basketItemsSetup = [];

    var BasketItem = function (desc, price, classHandle) {
        this.description = desc;
        this.price = price;
        this.classHandle = classHandle;
    };

    basket.addPostage = function () {
        var theDataElement = $('input[type=hidden].postage');
        var description = $(theDataElement).attr('name');
        var price = $(theDataElement).val();
        var classHandle = $(theDataElement).attr('class');
        basketItemsSetup[basketItemsSetup.length] = new BasketItem(description, price, classHandle);
    };

This function is defined in the following Zend Application folder structure: “Application->public->scripts->basket.js”. So we now can create a test file in “Application->tests->public->scripts->basketTest.js

We need to be able to mock away the html that the function is looking for, and that is simple in JsTestDriver. We can do this using the following notation within a unit test

/*:DOC += <input type="hidden" name="Postage and packaging" value="4.99" class="postage" /> */

So, this is my unit test for the above code, defined in “Application->tests->public->scripts->basketTest.js”:

/**
     * Basket Tests
     */
    BasketTest = TestCase("BasketTest");

    /**
     * Setup functionality. Make sure we are in a known state before we run
     * the tests
     *
     * @return void
     */
    BasketTest.prototype.setUp = function() {
        basketItemsSetup = [];
    }

    /**
     * Test that when you postage it correctly adds a new
     * BasketItem to the basketItemsSetup array and that the values
     * match what is in the html
     *
     * @return void
     */
    BasketTest.prototype.testAddPostageAppendsToItemsSetupArray = function() {
        /*:DOC += <input type="hidden" name="Postage and packaging" value="4.99" class="postage" /> */
        basket.addPostage();
        assertArray(basketItemsSetup);
        assertEquals(1, basketItemsSetup.length);
        assertEquals('4.99', basketItemsSetup[0].price);
        assertEquals('Postage and packaging', basketItemsSetup[0].description);
        assertEquals('postage', basketItemsSetup[0].classHandle);
    };

The first line

/**
     * Basket Tests
     */
    BasketTest = TestCase("BasketTest");

Creates a new test class that we can write our unit tests for. The second set of code

/**
     * Setup functionality. Make sure we are in a known state before we run
     * the tests
     *
     * @return void
     */
    BasketTest.prototype.setUp = function() {
        basketItemsSetup = [];
    }

Is called before each test is run, as JsTestDriver is based on the XUnit family. What we have done here is reset the basketItemsSetup array so we now have a predefined state before each test. And the last chunk of code is the unit test itself:

/**
     * Test that when you postage it correctly adds a new
     * BasketItem to the basketItemsSetup array and that the values
     * match what is in the html
     *
     * @return void
     */
    BasketTest.prototype.testAddPostageAppendsToItemsSetupArray = function() {
        /*:DOC += <input type="hidden" name="Postage and packaging" value="4.99" class="postage" /> */
        basket.addPostage();
        assertArray(basketItemsSetup);
        assertEquals(1, basketItemsSetup.length);
        assertEquals('4.99', basketItemsSetup[0].price);
        assertEquals('Postage and packaging', basketItemsSetup[0].description);
        assertEquals('postage', basketItemsSetup[0].classHandle);
    };

This has the magical mocking of html in there. So first off we define the html which we want our javascript function basket.addPostage() to interrogate. The assertions we want to make are:

  1. That the basketItemsSetup variable is still an array after the method is called
  2. That the array has one item in it
  3. That the BasketItem inside has the correct price, description and classHandle

There is one last thing we need to setup: The JsTestDriver configuration file. This, for us, is defined in “Application->tests->jsTestDriver.conf”:

server: http://localhost:4224

    load:
      - public/scripts/jquery.js
      - ../public/scripts/basket.js
      - public/scripts/basketTest.js

    plugin:
     - name: "coverage"
       jar: "/local/services/hudson/common/javascript/jstestdriver/coverage.jar"
       module: "com.google.jstestdriver.coverage.CoverageModule"

This configuration states that the tests should be run against localhost on port 4224 and to load the javascript files. The last bit of configuration is to state where the code coverage plugin is. We have that defined in our Hudson application path.

So, lets run the tests

$ cd ~/Application/tests
    $ java -jar ~t/jstestdriver/js-test-driver.1.2.2.jar --tests all

    .Chrome: Runner reset.

    Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
      Chrome 7.0.517.44 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)
    /Users/bselby/Application/tests/public/scripts/jquery.js: 100.0% covered
    /Users/bselby/Application/public/scripts/basket.js: 16.384182% covered
    /Users/bselby/Application/tests/public/scripts/basketTest.js: 27.8481% covered

To add more browsers to run the tests against, open up that browser and browse to http://127.0.0.1:4224/

Now we can capture the new browser, in this instance: Firefox. Click on “Capture The Browser”:

Now if we run the tests again we should get the output for 2 tests (1 for chrome, 1 for firefox):

$ cd ~/Application/tests
    $ java -jar ~t/jstestdriver/js-test-driver.1.2.2.jar --tests all

    Chrome: Runner reset.
    Firefox: Runner reset.
    ..Chrome: Runner reset.
    Firefox: Runner reset.

    Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (4.00 ms)
      Chrome 7.0.517.44 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (3.00 ms)
      Firefox 3.6.12 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (4.00 ms)
    /Users/bselby/Application/tests/public/scripts/jquery.js: 100.0% covered
    /Users/bselby/Application/public/scripts/basket.js: 16.384182% covered
    /Users/bselby/Application/tests/public/scripts/basketTest.js: 27.8481% covered

Great, but now we need a coverage report. To do this add —testOutput=build to the command

$ cd ~/Application/tests
    $ java -jar ~t/jstestdriver/js-test-driver.1.2.2.jar --tests all --testOutput=build

    Chrome: Runner reset.
    Firefox: Runner reset.
    ..Chrome: Runner reset.
    Firefox: Runner reset.

    Chrome: Runner reset.
    Firefox: Runner reset.
    ..Chrome: Runner reset.
    Firefox: Runner reset.

    Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (4.00 ms)
      Chrome 7.0.517.44 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (3.00 ms)
      Firefox 3.6.12 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (4.00 ms)

See how the output has changed, and that is because the coverage information has now been placed in “Application->tests->build->jsTestDriver.conf-coverage.dat”. This now needs converting into a html document so we can easily understand it. To do this we can use genhtml

$ cd ~/Application/tests
    $ ~/tools/genhtml -o build/coverage build/jsTestDriver.conf-coverage.dat

    Reading data file build/jsTestDriver.conf-coverage.dat
    Found 3 entries.
    Found common filename prefix "/Users/bselby/Application"
    Writing .css and .png files.
    Generating output.
    Processing file public/scripts/basket.js
    Processing file tests/public/scripts/basketTest.js
    Processing file tests/public/scripts/jquery.js
    Writing directory view page.
    Overall coverage rate:
      lines......: 20.2% (52 of 257 lines)
      functions..: no data found
      branches...: no data found

If we open this in our browser we get:

I hope this gives an overview of how to write JavaScript unit tests using JsTestDriver. I will follow up with an article of getting this running in Eclipse as a “builder” and also how to set it up running in Hudson. Sadly I cannot get the Eclipse plugin to work in Helios on Snow Leopard or Windows so I have defined a builder instead