Resolving relative URL’s from JavaScript

An entry about javascript | asp.net | asp.net mvc Publication date 22. August 2009 14:50

On the server side of an ASP.NET or ASP.NET MVC application, we’re used to working with relative URL’s in the style of “~/Images/logo.png”. This makes our application safely deployable to virtual directories within IIS applications without messing up the URL’s. However, what do we do if we need the same functionality in our JavaScript?

I’m solving this by sticking the following script in my Site.Master file:

Url = function() { }

Url.prototype =
{
    _relativeRoot: '<%= ResolveUrl("~/") %>',

    resolve: function(relative) {
        var resolved = relative;
        if (relative[0] == '~') resolved = this._relativeRoot + relative.substring(2);
        return resolved;
    }
}

$Url = new Url();

Now, anywhere I need to resolve a relative url in a script, I can do this:

var logoUrl = $Url.resolve("~/Images/logo.png");

Currently rated 4.6 by 5 people

  • Currently 4.6/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Behavior Driven JavaScript Development with JSSpec

An entry about bdd | javascript | tdd Publication date 26. January 2009 12:00

Before Christmas, I begun investigating JavaScript unit testing frameworks in an effort to increase the test coverage for my web projects. I’ve had a busy month since then, moving and changing jobs, but now I’m back on track and ready to try out another framework. So today, I’ll be looking at JSSpec, which is a Behavior Driven Development framework.

Code by Example

In JSSpec, specifications are referred to as containing one or more “examples” (tests) which “describe” (fixtures) the behavior. This wording of the specifications helps enforce the idea that a test should not only verify the behavior of a given piece of code, but also “serve as documentation and at the same time reveal the intent of the code”.

Rewriting the InlineEditor Test Suite with JSSpec

When investigating YUITest, I wrote a simple InlineEditor component complete with a set of tests as an example. Those tests looked like this one:

var testCase0 = new YAHOO.tool.TestCase(
{
    name: "When entering editing mode",
    setUp: function()
    {
        var span = $("<span id='span'>test</span>"); // create the span to edit
        span.appendTo($('#container')); // append it to the test container
        this.editor = new InlineEditor(span);
        YAHOO.util.UserAction.click(span[0]); // click to begin editing
    },
    test_span_is_turned_into_input: function()
    {
        var input = $('#span'); // find the element in the document
        // element should now be replaced with an input element
        YAHOO.util.Assert.isInstanceOf(HTMLInputElement, input[0]);
    },
    test_input_has_same_text_as_span: function()
    {
        var input = $('#span');
        YAHOO.util.Assert.areEqual('test', input.val());
    },
    tearDown: function()
    {
        $('#container').empty(); // clear the container 
        delete this.editor;
    }
});

Migrating the tests to JSSpec wasn't difficult – in part thanks to the fact that I had already written my tests using YUITest with a bdd-style approach. The test above, for example, now looks like this:

describe('When entering editing mode',
{
    before_each: function()
    {
        container = $("<div/>");
        container.appendTo($("body"));
        original_text = 'test';


var span = $("<span id='span'></span>"); // create the span to edit span.text(original_text); span.appendTo(container); // append it to the test container var editor = new InlineEditor(span); editor.beginEdit(); }, after_each: function() { container.remove(); }, 'span should turn into editor': function() { var inputs = $('input').filter('#span'); // find the element in the document value_of(inputs).should_have(1, "length"); }, 'editor should have text of span': function() { var input = $('#span'); value_of(input.val()).should_be(original_text); } });

So what has changed? Where we had setUp and tearDown methods in YUITest, we now have before_each and after_each methods in JSSpec. The test methods are declared with string names in JSSpec, which means I can now use spaces and write more natural names for them (plus no need to prefix their names with ‘test’). The assertion syntax is a bit different too – JSSpec uses a fluent API that again reads a bit more easily.

All in all, I think I prefer the syntax of JSSpec. It also has a very nice test runner UI:

 image

The API is fairly easy to grasp too, and though the documentation is fairly concise (especially compared to YUITest) it covers everything you need to know. All in all, JSSpec is one of those frameworks that you pick up and hit the ground running with, which is awesome.

A Minimalistic Framework

Feature-wise however, YUITest wins hands down. Where JSSpec is strictly offering a testing framework with the minimal set of features you need, YUITest has a much larger bag of tricks. For instance, JSSpec has no helpers for testing asynchronous code (Ajax stuff) or interacting with UI elements (faking mouse/keyboard events etc) – you’ll have to either roll your own or rely on something like jQuery for that stuff. Assertions are a bit sparse too – I really miss things like YUITests data type assertion (isArray/isString/… and the more generic isTypeOf/isInstanceOf).

Continous Integration

Unlike YUITest, JSSpec has no built-in extension API for reporting test results. However, the test runner produces a clean html output which won’t be too hard to parse if you were to for instance fire it up from a custom build task in TFS:

image

Download Example Code

You can download the complete InlineEditor example with a suite of JSSpec tests here.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

JavaScript Unit Test Isolation

An entry about javascript | tdd Publication date 19. December 2008 18:20

A couple of weeks ago, I wrote posted about JavaScript unit testing with the YUI Test framework. In it, I mentioned that I would have liked to be able to reload the page between running each test fixture, to be sure that tests did not interfere with each other. On Twitter, YUI Test creator Nicholas C. Zakas pinged me saying “it's better to use setUp() and tearDown() to reset the environment rather than reloading the page and losing your place.”

He’s of course right – the KISS principle applies. Revisiting my code, what I should have done to ensure total isolation between each test was to use setUp and tearDown to create the context that each request required – in my case, dynamically creating the <span> element that the InlineEditor should work against in the setUp and deleting it in the tearDown. Here’s an updated version of one of the fixtures which uses this approach:

   1: var testCase0 = new YAHOO.tool.TestCase(
   2: {
   3:     name: "When clicking editable span",
   4:  
   5:     setUp: function()
   6:     {
   7:         var span = $("<span id='span'>test</span>"); // create the span to edit
   8:         span.appendTo($('#container')); // append it to the test container
   9:         this.editor = new InlineEditor(span);
  10:  
  11:         YAHOO.util.UserAction.click(span[0]); // click to begin editing
  12:     },
  13:  
  14:     test_span_is_turned_into_input: function()
  15:     {
  16:         var input = $('#span'); // find the element in the document
  17:  
  18:         // element should now be replaced with an input element
  19:         YAHOO.util.Assert.isInstanceOf(HTMLInputElement, input[0]);
  20:     },
  21:  
  22:     test_input_has_same_text_as_span: function()
  23:     {
  24:         var input = $('#span');
  25:         YAHOO.util.Assert.areEqual('test', input.val());
  26:     },
  27:  
  28:     tearDown: function()
  29:     {
  30:         $('#container').empty(); // clear the container
  31:         delete this.editor;
  32:     }
  33: });

I’ve updated all the tests in the sample to follow this pattern; download it here.

Stay tuned for more posts on JavaScript unit testing over christmas as I start investigating other frameworks and start comparing them :)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

JavaScript is Code Too: Test It!

An entry about javascript | tdd Publication date 2. December 2008 16:03

For some reason, it is all too easy not to give the client-side JavaScript code in a web project the same level of care that the “real code” server-side gets. I’m sure I’m not the only who have found myself having to searching through .js files with several hundred (if not thousands) of lines of badly formatted JavaScript code with global variables and functions all over the place, trying to track down some elusive bug. These days, I have a zero-tolerance for code without test coverage – why should I treat my JavaScript any differently? JavaScript is code too…

Unit Test Frameworks for JavaScript

There are quite a few unit test frameworks available for JavaScript - Mark Levison recently posted a nice comparison between a few of them over on his blog. I’ve decided to give a few of the most interesting ones a go, and try to find out which one I like the most, blogging about my experiences along the way. The first framework I’m going to have a look at is YUI Test.

Giving It a Go

To get some real-life experience with unit testing JavaScript using YUI Test, I decided to implement an inline editing component. It should work like this:

  • 1. User clicks on the element to edit (for example a <span/>).
  • 2. The element is converted into an <input type=’text’ /> element.
  • 3. The user edits the text and hits the ENTER key.
  • 4. The <input/> element is converted back to the original element type, containing the edited text.

Using the YUI Test framework, here’s my first test, which will verify that clicking an element which is editable turns it into a text input (step 1-2). Note that I’ve omitted some of the initialization code etc for the framework – you can download a complete working example at the end of this post.

var testCase0 = new YAHOO.tool.TestCase(
{
    name: "When clicking editable span",
 
    setUp: function() 
    {
 
        var span = $('#span1'); // find the span to edit
        this.editor = new InlineEditor(span);
 
        YAHOO.util.UserAction.click(span[0]); // simulate mouse click to begin editing
    },
 
    test_span_is_turned_into_input: function() 
    {
 
        var input = $('#span1'); // find the element in the document
 
        // element should now be replaced with an input element
        YAHOO.util.Assert.isInstanceOf(HTMLInputElement, input[0]); 
    }
});

Note that I’m also using jQuery here (the $ stuff), as I’m not familiar with the rest of the YUI framework (which probably has similar functionality for finding elements etc – I’m just too lazy to look it up :p).

To make this test pass, we need some markup:

<html>
    <body>
        <div>  
            <h1>Test Page</h1>
            <span id="span1">Click to edit me!</span>    
        </div>
    </body>
</html>

Next, we need to implementing enough of the InlineEditor to make the first test pass:

InlineEditor = function(span) {
 
    this.span = $(span);
    var self = this;
    this.span.click(function() { self.beginEdit(); });
}
 
InlineEditor.prototype =
{
    span: null,
    input: null,
 
    beginEdit: function() {
 
        var self = this;
 
        self.input = $("<input type='text' name='title' />");
        self.input.attr('id', self.span.attr('id')); // copy id across
        self.input.val(self.span.text());
        self.span.replaceWith(self.input);
 
        self.input.select();
    }
}

Running the tests, we can see that we’re OK so far:

image

The next requirement is that when the user changes the text and hits ENTER, the original element is shown again, updated with the new text (step 3-4). Lets write a test for that:

var testCase1 = new YAHOO.tool.TestCase(
{
    name: "When hitting enter key while editing",

    setUp
: function()
    {
        var span = $('#span2'); // find the span to edit
        var editor = new InlineEditor(span);
 
        // simulate user clicking the element to begin editing
        YAHOO.util.UserAction.click(span[0]); 
 
        this.expected = 'test';
        editor.input.val(this.expected); // simulate user changing text
 
        // simulate the user hitting enter to end editing
        YAHOO.util.UserAction.keydown(editor.input[0], { keyCode: 13 });  
    },

    test_text_is_updated
: function()
    {
        var elem = $('#span2');
 
        // the document should now contain a span element
        YAHOO.util.Assert.isInstanceOf(HTMLSpanElement, elem[0]);
 
        // span should have the edited value set
        YAHOO.util.Assert.areEqual(this.expected, elem.text());
    }
});

To make this pass, we must complete the implementation of our InlineEditor:

InlineEditor.prototype =
{
    span: null,
    input: null,
 
    beginEdit: function()
    {
        var self = this;
 
        self.input = $("<input type='text' name='title' />");
        self.input.attr('id', self.span.attr('id')); // copy id across
        self.input.val(self.span.text());
        self.span.replaceWith(self.input);
 
        // handle keydown event to watch for user hitting 'enter'
        self.input.keydown(function(e)
        {
            switch (e.keyCode)
            {
                case 13: // enter
                    self.endEdit(); break;
            }
        });
 
        self.input.select();
    },
    endEdit: function()
    {
        var self = this;
 
        // update text
        self.span.text(self.input.val());
        // restore click handler
        self.span.click(function() { self.beginEdit(); });
        // switch elements again
        self.input.replaceWith(self.span);
    }
}

With this in place, we can run our tests again and verify that things are OK:

image

First Impressions of Unit Testing JavaScript (with YUI Test)

I can say right away that it just feels right to also have unit tests for the JavaScript code in a project. These days the client-side logic is becoming more and more complex, and is often just as important to the project as the server-side business logic.

I’ve just barely scratched the surface of the features in YUI Test in this post – check out it’s excellent documentation pages to learn more; the asynchronous test support looks especially cool. Apparently a complimentary mocking library is in the works as well.

Unit testing JavaScript is certainly not without complications. The biggest issue I’ve had so far, is making each test run in isolation. YUI Test doesn’t refresh the browser window between running each test, which means that it is easy for tests to contaminate each others state if you’re not careful (especially true for tests that interact with the DOM, like my example does). This is a feature that I would really like to see, and one I’ll be looking for in other frameworks.

Another obvious obstacle is how to make these tests part of a continuous integration environment. I haven’t looked into it yet, but YUI Test seems to have some great extensibility points for reporting test result either as XML or JSON. I can imagine that writing a custom build task for Team Foundation Server which kicks off the tests and parses the results would be perfectly possible to implement.

Demo Code

If you want to play about with this stuff for yourself, you can download the demo code that I wrote for the examples in this post.

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Partial Rendering with ASP.NET MVC and jQuery

An entry about asp.net mvc | ajax | javascript Publication date 1. June 2008 13:57

Yesterday, I wrote a post detailing how easy we can invoke an ASP.NET MVC controller action from JavaScript using jQuery. Lets up the ante a bit, and see if we can't use the same approach to get some partial rendering going. To accomplish this, we'll use a fact about the RenderViewResult class that hasn't got a lot of attention so far - the fact that its View propetry can be set to point at a UserControl, not just a WebForm. When doing so, only the user control will be rendered - which sounds like exactly what we need for doing partial rendering..

Rendering User Controls

Lets say we're building a simple task list. We might have the following UserControl representing a single task:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Task.ascx.cs" Inherits="MvcApplication2.Views.Tasks.Task" %>
<div class="task">
    <div class="task_name"><%= ViewData.Name %></div>
    <div class="task_due"><%= ViewData.Due %></div>
    <div class="task_desc"><%= ViewData.Description %></div>
</div>

 

Our TaskList view may then use this to render a list of tasks:

<h1>Tasks</h1>
 
<div id="task_list">
    <% foreach(Task task in ViewData) {%>
        <%= Html.RenderUserControl("~/Views/Tasks/Task.ascx", task) %>
    <%} %>
</div>
 
<h1>Add task</h1>
 
Name: <input type="text" id="newtask_name" /><br />
Due: <input type="text" id="newtask_due" /><br />
Description: <input type="text" id="newtask_desc" /><br />
<input type="button" onclick="AddTask()" value="Add" />

 

The TaskList view is rendered by the Index action on our TaskController, which looks like this:

public ActionResult Index()
{
    Task[] tasks = // get tasks from repository
 
    return RenderView("TaskList", tasks);
}

 

Next, we need an action for creating a new task. However, we want to invoke it from JavaScript, and have it only return the markup for the new task item so that we can append it to the task list dynamically, instead of refreshing the entire page. To accomplish this, we implement it using the previously mentioned trick of the RenderViewResult class:

public ActionResult New(string name, string desc, DateTime due)
{
    Task task = new Task { Name = name, Description = desc, Due = due };
    // ...save task to repository
    return RenderView("Task", task);
}

 

Notice here that "Task" points to the Task.ascx user control, not a Web Form. When invoked, this action will render the markup for that control only.

Manipulating the DOM

Like I mentioned in yesterdays post, we can tell jQuery to handle the response from an Ajax call as html. More than that, we can use jQuery to create a DOM element for us from the returned html, and drop it anywhere in our document. The following JavaScript function accomplishes both:

function AddTask()
{
    var name = $("#newtask_name").attr("value");
    var due = $("#newtask_due").attr("value");
    var desc = $("#newtask_desc").attr("value");
 
    $.ajax(
    {
       type: "POST",
       url: "/Tasks/New",
       data: "name=" + name + "&due=" + due + "&desc= "+ desc,
       dataType: "html",
       success: function(result)
       {
            var domElement = $(result); // create element from html
            $("#task_list").append(domElement); // append to end of list        
       }
    });        
}

 

Here, we're making an Ajax call to the New action on the Tasks controller. We then create a new DOM element from the resulting html that it renders, and append it to the task list. And that's it! When the user adds a task, the following POST is sent:

Post

The following response is received from the server, which we append to the task list:

Response

If you want to take a look at a working example, you can download the project I used to demo this here.

Note: For some reason, the Ajax calls are quite slow (~1 sec) when running the demo using Visual Studio's built-in development server. Publish the site to IIS however, and things are blazingly fast (as you can see from the screenshots above) :)

Currently rated 5.0 by 8 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Invoking ASP.NET MVC Actions from JavaScript using jQuery

An entry about asp.net mvc | ajax | javascript Publication date 31. May 2008 11:27

One of the things that the MVC model is really great at, is integrating with Ajax. I blogged about a RenderJsonResult class a few weeks ago that one could use to create controller actions which render their output as Json - since then the last ASP.NET MVC framework drop also includes a JsonResult type of its own. What I didn't talk about in the mentioned post, was how to actually invoke such actions from JavaScript; enter jQuery.

Ajax with JQuery

In the crowd of JavaScript libraries out there, JQuery is probably the most unique - at least for now. Its main features are the abilities to query and manipulate the DOM using syntax that anyone familiar with CSS will feel at home with. But jQuery offers more than this - it also has an Ajax API. With this, we can easily perform a POST or GET on a controller action and handle the result it returns asynchronously.

As an example, lets assume we have a FeedbackController with an action Submit:

public ActionResult Submit(string author, string email, string comment)
{
    // logic for submitting feedback omitted
 
    return new RenderJsonResult {Result = new {success = true, message = "Thank you! We value your feedback."}};
}

 

Invoking this from JavaScript using jQuery is super easy:

$.ajax(
{
   type: "POST",
   url: "/Feedback/Submit",
   data:"author=" + author + "&email=" + authorEmail + "&comment=" + comment,
   success: function(result)
    {
        if(result.success) $("#feedback input").attr("value", ""); // clear all the input fields on success
        $("#feedback_status").slideDown(250).text(result.message); // show status message with animation
    },                
   error : function(req, status, error)
   {
        alert("Sorry! We could not receive your feedback at this time.");   
   }
});

 

We set the type of the call to POST, because we need to send some data with our request, which we use the data option to specify. jQuery allows us to provide a few callback methods that will be invoked on the success or failure of the Ajax call. If our call is successful, the success callback gets invoked, which gets passed an object representing the result of the Ajax call. jQuery is able to handle results formatted in different styles, including xml, html, script and json. You can specify the expected data type using the dataType option. Leaving it out will cause jQuery to choose the type based on the MIME type of the response, which is usually fine. There are other options which can be used; you'll find a complete description of them here. The code in the success callback uses the selectors, attributes, manipulation and effects APIs to reset the input fields and tell the user that the feedback was received.

It's simple, it's elegant - and so, so far removed from the bloated UpdatePanels of ASP.NET.

Surface Scratched

This post merely scratches the surface of what is possible when combining ASP.NET MVC with jQuery (or other Ajax APIs for that matter). Look out for more posts on this topic in the future!

Currently rated 4.2 by 18 people

  • Currently 4.222222/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

NDC 2010

The conference to attend this summer happens June 16th-18th in Oslo, Norway. Are you going? Be sure to catch my talk on AOP while you're there!

 

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS