Monday, 17 March 2014

The Surprisingly Happy Tale of Visual Studio Online, Continous Integration and Chutzpah

Going off piste

The post that follows is a slightly rambly affair which is pretty much my journal of the first steps of getting up and running with JavaScript unit testing. I will not claim that much of this blog is down to me. In fact in large part is me working my way through Mathew Aniyan's excellent blog post on integrating Chutzpah with TFS. But a few deviations from this post have made me think it worth keeping hold of this record for my benefit (if no-one else's).

That's the disclaimers out of the way now...

...Try, try, try again...

Getting started with JavaScript unit testing has not been the breeze I’d expected. Frankly I’ve found the docs out there not particularly helpful. But if at first you don't succeed then try, try, try again.

So after a number of failed attempts I’m going to give it another go. Rushaine McBean says Jasmine is easiest so I'm going to follow her lead and give it a go.

Let’s new up a new (empty) ASP.NET project. Yes, I know ASP.NET has nothing to do with JavaScript unit testing but my end goal is to be able to run JS unit tests in Visual Studio and as part of Continuous Integration. Further to that, I'm anticipating a future where I have a solution that contains JavaScript unit tests and C# unit tests as well. It is indeed a bold and visionary Brave New World. We'll see how far we get.

First up, download Jasmine from GitHub - I'll use v2.0. Unzip it and fire up SpecRunner.html and whaddya know... It works!

As well it might. I’d be worried if it didn’t. So I’ll move the contents of the release package into my empty project. Now let’s see if we can get those tests running inside Visual Studio. I’d heard of Chutzpah which describes itself thusly:

“Chutzpah is an open source JavaScript test runner which enables you to run unit tests using QUnit, Jasmine, Mocha, CoffeeScript and TypeScript.”

What I’m after is the Chutzpah test adapter for Visual Studio 2012/2013 which can be found here. I download the VSIX and install. Pretty painless. Once I restart Visual Studio I can see my unit tests in the test explorer. Nice! Run them and...

All fail. This makes me sad. All the errors say “Can’t find variable: Player in file”. Hmmm. Why? Dammit I’m actually going to have to read the documentation... It turns out the issue can be happily resolved by adding these 3 references to the top of PlayerSpec.js:

/// <reference path="../src/Player.js" />
/// <reference path="../src/Song.js" />
/// <reference path="SpecHelper.js" />

Now the tests pass:

The question is: can we get this working with Visual Studio Online?

Fortunately another has gone before me. Mathew Aniyan has written a superb blog post called "Javascript Unit Tests on Team Foundation Service with Chutzpah". Using this post as a guide (it was written 18 months ago which is frankly aeons in the world of the web) I'm hoping that I'll be able to, without too many tweaks, get Javascript unit tests running on Team Foundation Service / Visual Studio Online ( / insert this weeks rebranding here).

First of all in Visual Studio Online I’ll create a new project called "GettingStartedWithJavaScriptUnitTesting" (using all the default options). Apparently “Your project is created and your team is going to absolutely love this.” Hmmmm... I think I’ll be judge of that.

Let's navigate to the project. I'll fire up Visual Studio by clicking on the “Open in Visual Studio” link. Once fired up and all the workspace mapping is sorted I’ll move my project into the GettingStartedWithJavaScriptUnitTesting folder that now exists on my machine and add this to source control.

Back to Mathew's blog. It suggests renaming Chutzpah.VS2012.vsix to Chutzpah.VS2012.zip and checking certain files into TFS. I think Chutzpah has changed a certain amount since this was written. To be on the safe side I’ll create a new folder in the root of my project called Chutzpah.VS2012 and put the contents of Chutzpah.VS2012.zip in there and add it to TFS (being careful to ensure that no dll’s are excluded).

Then I'll follow steps 3 and 4 from the blog post:

3. In Visual Studio, Open Team Explorer & connect to Team Foundation Service.
Bring up the Manage Build Controllers dialog. [Build –> Manage Build Controllers]
Select Hosted Build Controller
Click on Properties button to bring up the Build Controller Properties dialog.

4. Change Version Control Path to custom Assemblies to refer to the folder where you checked in the binaries in step 2.

In step 5 the blog tells me to edit my build definition. Well I don’t have one in this new project so let’s click on “New Build Definition”, create one and then follow step 5:

5. In Team Explorer, go to the Builds section and Edit your Build Definition which will run the javascript tests.
Click on the Process tab
Select the row named Automated Tests.
Click on … button next to the value.

Rather than following step 6 (which essentially nukes the running of any .NET tests you might have) I chose to add another row by clicking "Add". In the dialog presented I changed the Test assembly specification to **\*.js and checked the "Fail build on test failure" checkbox.

Step 7 says:

7. Create your Web application in Visual Studio and add your Qunit or Jasmine unit tests to them. Make sure that the js files (that contain the tests) are getting copied to the build output directory.

The picture below step 7 suggests that you should be setting your test / spec files to have a Copy to Output Directory setting of Copy always. This did not work for me!!! Instead, setting a Build Action of Content and a Copy to Output Directory setting of Do not copy did work.

Finally I checked everything into source control and queued a build. I honestly did not expect this to work. It couldn’t be this easy could it? And...

Wow! It did! Here’s me cynically expecting some kind of “permission denied” error and it actually works! Brilliant! Look up in the cloud it says the same thing!

Fantastic!

I realise that I haven’t yet written a single JavaScript unit test of my own and so I’ve still a way to go. What I have done is quietened those voices in my head that said “there’s not too much point having a unit test suite that isn’t plugged into continuous integration”. Although it's not documented here I'm happy to be able to report that I have been able to follow the self-same instructions for Team Foundation Service / Visual Studio Online and get CI working with TFS 2012 on our build server as well.

Having got up and running off the back of other peoples hard work I best try and write some of my own tests now....

Tuesday, 11 March 2014

Knockout + Globalize = valueNumber Binding Handler

I’ve long used Globalize for my JavaScript number formatting / parsing needs. In a current project I’m using Knockout for the UI. When it came to data-binding numeric values none of the default binding handlers seemed appropriate. What I wanted was a binding handler that:

  1. Was specifically purposed for dealing with numeric values
  2. Handled the parsing / formatting for the current locale (and I naturally intended to use Globalize for this purpose)

Like so much development we start by standing on the shoulders of giants. In this case it’s the fantastic Ryan Niemeyer who put up a post on StackOverflow that got me on the right track.

Essentially his approach provides an “interceptor” mechanism that allows you to validate numeric data entry on input and format numeric data going out as well. Very nice. Into this I plugged Globalize to handle the parsing and formatting. I ended up with the “valueNumber” binding handler:

ko.bindingHandlers.valueNumber = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
 
        /**
         * Adapted from the KO hasfocus handleElementFocusChange function
         */ 
        function elementIsFocused() {
            var isFocused = false,
                ownerDoc = element.ownerDocument;
            if ("activeElement" in ownerDoc) {
                var active;
                try {
                    active = ownerDoc.activeElement;
                } catch(e) {
                    // IE9 throws if you access activeElement during page load
                    active = ownerDoc.body;
                }
                isFocused = (active === element);
            }
 
            return isFocused;
        }
 
        /**
         * Adapted from the KO hasfocus handleElementFocusChange function
         *
         * @param {boolean} isFocused whether the current element has focus
         */
        function handleElementFocusChange(isFocused) {
            elementHasFocus(isFocused);
        }
 
        var observable = valueAccessor(),
            properties = allBindingsAccessor(),
            elementHasFocus = ko.observable(elementIsFocused()),
            handleElementFocusIn = handleElementFocusChange.bind(null, true),
            handleElementFocusOut = handleElementFocusChange.bind(null, false);
 
        var interceptor = ko.computed({
            read: function () {
                var currentValue = ko.utils.unwrapObservable(observable);
                if (elementHasFocus()) {
                    return (!isNaN(currentValue) && (currentValue !== null) && (currentValue !== undefined))
                        ? currentValue.toString().replace(".", Globalize.findClosestCulture().numberFormat["."]) // Displays correct decimal separator for the current culture (so de-DE would format 1.234 as "1,234")
                        : null;
                } else {
                    var format = properties.numberFormat || "n2",
                        formattedNumber = Globalize.format(currentValue, format);
 
                    return formattedNumber;
                }
            },
            write: function (newValue) {
                var currentValue = ko.utils.unwrapObservable(observable),
                    numberValue = Globalize.parseFloat(newValue);
                
                if (!isNaN(numberValue)) {
                    
                    if (numberValue !== currentValue) {
                        // The value has changed so update the observable
                        observable(numberValue);
                    }
                } else if (newValue.length === 0) {
                    if (properties.isNullable) {
                        // If newValue is a blank string and the isNullable property has been set then nullify the observable
                        observable(null);
                    } else {
                        // If newValue is a blank string and the isNullable property has not been set then set the observable to 0
                        observable(0);
                    }
                }
            }
        });
        
        ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
        ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
        ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
        ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
 
        if (element.tagName.toLowerCase() === 'input') {
            ko.applyBindingsToNode(element, { value: interceptor });
        } else {
            ko.applyBindingsToNode(element, { text: interceptor });
        }
    }
};

Using this binding handler you just need to drop in a valueNumber into your data-bind statement where you might previously have used a value binding. The binding also has a couple of nice hooks in place which you might find useful:

numberFormat (defaults to "n2")
allows you to specify a format to display your number with. Eg, "c2" would display your number as a currency to 2 decimal places, "p1" would display your number as a percentage to 1 decimal place etc
isNullable (defaults to false)
specifies whether your number should be treated as nullable. If it's not then clearing the elements value will set the underlying observable to 0.

Finally when the element gains focus / becomes active the full underlying value is displayed. (Kind of like Excel - like many an app, the one I'm working on started life as Excel and the users want to keep some of the nice aspects of Excel's UI.) To take a scenario, let's imagine we have an input element which is applying the "n1" format. The underlying value backing this is 1.234. The valueNumber binding displays this as "1.2" when the input does not have focus and when the element gains focus the full "1.234" is displayed. Credit where it’s due, this is thanks to Robert Westerlund who was kind enough to respond to a question of mine on StackOverflow.

Finally, here’s a demo using the "de-DE" locale:

PS Globalize is a-changing

The version of Globalize used in the binding handler is Globalize v0.1.1. This has been available in various forms for quite some time but as I write this the Globalize plugin is in the process of being ported to the CLDR. As part of that work it looks like the Globalize API will change. When that gets finalized I’ll try and come back and update this.

Wednesday, 5 March 2014

Caching and cache-busting with RequireJS

Having put together a demo of using TypeScript with RequireJS my attention turned quickly to caching. Or rather, IE forced me to think about caching.

Everyone has their own workflow, their own tools. The things they like to use as they put things together. And for me I’m a Visual Studio man – it’s not everyone’s bag but I really like it. I find the JavaScript tooling is now really solid combined with IE and it (generally) makes me more productive. I want to use it. But, as you know, nothing is perfect...

So there I was, delighted with the TypeScript / RequireJS demo. It was working just lovely. I started investigating the debugging story. What would happen if I change a script file on the fly? When I refresh IE does it pick up the tweaks?

Let’s find out. I'll open up alerter.ts and change this:

var name = "John";

to this:

var name = "Bobby";

And *boom*! Nothing. I’ve refreshed IE and I’m expecting to see “Welcome to Code Camp, Bobby”. But I’m still reading “Welcome to Code Camp, John”... I bet Chrome wouldn’t do this to me... And I’m right! It doesn’t. I don’t want to get too much into the details of this but it looks like it comes down to Chrome sending an "If-Modified-Since" request header where IE does not. I’m pretty sure that IE could be configured to behave likewise but I’d rather not have to remember that. (And furthermore I don’t want to have to remind every person that works on the app to do that as well.)

This raises a number of issues but essentially it gets me to think about the sort of caching I want. Like most of you I have 2 significant use cases:

  1. Development
  2. Production

For Development I want any changes to JavaScript files to be picked up – I do *not* want caching. For Production I want caching in order that users have better performance / faster loading. If I ship a new version of the app to Production I also want users to pick up the new versions of a file and cache those.

Research

I did a little digging. The most useful information I found was a StackOverflow post on RequireJS and caching. Actually I’d recommend anyone reading this to head over and read that from top to bottom. Read the question and all of the answers as well – pretty much everything will add to your understanding of RequireJS.

As with any set of answers there are different and conflicting views. Phil McCull’s (accepted) answer was for my money the most useful. It pointed back to the RequireJS documentation.

"urlArgs: Extra query string arguments appended to URLs that RequireJS uses to fetch resources. Most useful to cache bust when the browser or server is not configured correctly. Example cache bust setting for urlArgs:

urlArgs: "bust=" +  (new Date()).getTime()

During development it can be useful to use this, however be sure to remove it before deploying your code."

Phil’s answer suggests using urlArgs *both* for Production and for Development in 2 different ways. Using what amounts to a random number in the Development environment (as in the official docs) for cache-busting. For the Production environment he suggests using a specific version number which allows for client-side caching between different build versions.

Full disclosure, this is not the approach favoured by James Burke (author of RequireJS). He doesn’t go into why in the RequireJS docs but has elsewhere expounded on this:

For deployed assets, I prefer to put the version or hash for the whole build as a build directory, then just modify the baseUrl config used for the project to use that versioned directory as the baseUrl. Then no other files change, and it helps avoid some proxy issues where they may not cache an URL with a query string on it.

I’m not so worried about the proxy caching issue. My users tend to be people who use the application repeatedly and so the caching I most care about is their local machine caching. From what I understand urlArgs will work fine in this scenario. Yes the downside of this approach is that some proxy servers may not cache these assets. That’s a shame but it’s not a dealbreaker for me. As I said, I still have client side caching.

If you want to go a little deeper I recommend reading Steve Souders post on the topic (in case you’re not aware Steve is Google’s Mr Performance). Interestingly, looking at the comments on the post it sounds like the lack of support for proxy caching with querystrings may that may be starting to change.

But either way, I’m happy with this approach. As I always say, if it’s good enough for Stack Overflow then it’s good enough for me:

Implementation

I’m going to start off using the demo from my last blog post as a basis. Let’s take that and evolve it. As a result my solution is going to work with TypeScript and RequireJS (since the previous demo was about that) but the implementation I’m going to come up with would work as well with vanilla JS as it would with TypeScript compiled JS.

Let’s take a look at our index.html. First we’ll drop our usage of main.ts / main.js (our bootstrapper file that defines config and kicks off the "app"). We’ll pull out the use of data-main and instead, just after the reference to require we’ll add the contents of main.js much in the style of the RequireJS docs. We’ll also include a urlArgs that as a cache-buster that uses the approach outlined in the RequireJS docs:

<script src="/scripts/require.js"></script>
<script>
    require.config({
        baseUrl: "/scripts",
        paths: {
            "jquery": "jquery-2.1.0"
        },
        urlArgs: "v=" +  (new Date()).getTime()
    });
 
    require(["alerter"], function (alerter) {
        alerter.showMessage();
    });
</script>

Spinning up the site all runs as you would expect. The question is: does this work as a cache-buster? Let’s tweak alerter.ts / alerter.js. And:

Oh yeah! We’re cache-busting like gangbusters!

So now let’s comment out our existing urlArgs (which represents the Development solution from Phil’s answer) and replace it with a fixed value like this:

        //urlArgs: "v=" +  (new Date()).getTime()
        urlArgs: "v=1"

This represents the Production solution from Phil’s answer. Now let’s run, refresh a couple of times and ensure that our fixed querystring value results in a 304 status code (indicating “Not Modified” and cached item used):

It does! Now let’s increment the value:

        urlArgs: "v=2"

When we refresh the browser this should result in 200 status codes (indicating the cached version has not been used and the client has picked up a new version from the server).

Success! That’s our premise tested – both Development and Production scenarios. Now we want to turn this into a slightly more sophisticated reusable solution like this:

<script src="/scripts/require.js"></script>
<script>
    var inDevelopment = true,
        version = "1";
 
    require.config({
        baseUrl: "/scripts",
        paths: {
            "jquery": "jquery-2.1.0"
        },
        urlArgs: "v=" + ((inDevelopment)
            ? (new Date()).getTime()
            : version)
    });
 
    require(["alerter"], function (alerter) {
        alerter.showMessage();
    });
</script>

In the tweaked script above 2 variables are defined. The first is inDevelopment which models whether you are in the Development scenario (true) or the Production scenario (false). The second is version which represents the application version number. With this in place I can simply flip between the Development and Production scenario by changing the value of inDevelopment. And when a new version ships I can change the version number to force a cache refresh on the users.

What drives the values of inDevelopment / version is down to you. You could load the inDevelopment / version values from some application endpoint. You could hardcode them in your screen. The choices are yours. I’m going to finish off with a simple approach that I've found useful.

Let’s get the server involved!

I want the server to drive my urlArgs value. Why? Well this project happens to be an ASP.NET project which handily has the concept of Development / Production scenarios nicely modelled by the web.config’s compilation debug flag.

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
</configuration>

If debug is true then that reflects the Development scenario. If debug is false then that reflects the Production scenario.

So bearing that in mind I want to use the value of debug to drive my urlArgs. If I have my debug flag set to true I want to cache-bust all the way. Likewise, if debug is set to false then I want to serve up the version number so that caching is used until the version number changes. Time to break out the C#:

namespace RequireJSandCaching
{
    public static class RequireJSHelpers
    {
        private static readonly bool _inDebug;
        private static readonly string _version;
 
        static RequireJSHelpers()
        {
            _inDebug = System.Web.HttpContext.Current.IsDebuggingEnabled;
            _version = (_inDebug)
                ? "InDebug"
                : System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }
 
        public static string Version
        {
            get
            {
                return (_inDebug)
                    ? System.DateTime.Now.Ticks.ToString()
                    : _version;
            }
        }
    }
}

This is a static helper class called RequireJSHelpers. It has a static constructor which initialises 2 fields. _inDebug is taken from System.Web.HttpContext.Current.IsDebuggingEnabled which exposes the compilation debug value. _version is initialised, when debug is false, to the version number of the dll (driven by this AssemblyInfo.cs [assembly: AssemblyVersion("1.0.*")] attribute)

There’s 1 property on this helper class called version. Depending on whether the app is in debug mode or not this attribute either exposes the application version or effectively the C# equivalent to JavaScript’s (new Date()).getTime(). (Well strictly speaking they have a different starting point in history but that’s by-the-by... Both are of equal value as cache-busters.)

You probably see where this is all going.

Let’s clone our index.html page and call it serverUrlArgs.cshtml (note the suffix). Let’s replace the script section with this:

<script>
    require.config({
        baseUrl: "/scripts",
        paths: {
            "jquery": "jquery-2.1.0"
        },
        urlArgs: "[email protected]"
    });
 
    require(["alerter"], function (alerter) {
        alerter.showMessage();
    });
</script>

Which drives urlArgs from the RequireJSHelpers.Version property. If we fire it up now (with debug set to true in our web.config) then we see requests like this:

And if we set debug to false in our web.config then (after the initial requests have been cached) we see requests like this:

This leaves us with a simple mechanism to drive our RequireJS caching. If debug is set to true in our web.config then Require will perform cache-busting. If debug is set to false then RequireJS will perform only version-changing cache-busting and will, whilst the version remains constant, support client-side caching.

Finished. In case it helps I’ve put the code for this up on GitHub.