I work for appendTo - The jQuery Company

appendToLogoAs you may or may not seen on Twitter the last 3 weeks or so, I accepted an offer to work with appendTo, LLC - The Company Dedicated to jQuery where I will be doing JavaScript Consulting as well as other activities.

I first met the CEO (Mike Hostetler) and President (Jonathan Sharp) of appendTo back in January while I was at the launch of jQuery 1.4 in Washington DC.

After the unfortunate turn of events at my last job (raided by the FBI & IRS), I was forced to look for a new job opportunity.

One of the first places I thought of working for was appendTo. After several weeks of talks everything worked out and now I am an official employee!

Since I’ve started I’ve done some writing, did an architectural review of a client’s code, and most recently I’ve been diving deep in developing a JavaScript and jQuery library.

I am really excited about this opportunity. The group of people that I work alongside are top notch! I find it impressive that appendTo has 5 official jQuery Team members working for them.

It looks like I’ll be able to attend the Boston jQuery Conference this coming October 16-17, 2010. I hope to see you there! Sign up while tickets last.

<blockquote>By the way, thank all of you who sent me job leads, contacting me about working with you, or tweeted on my behalf while I was looking for a job. I appreciate all your efforts!</blockquote>

Read More »

@ElijahManor is Looking for a jQuery &| ASP.NET MVC Job

If you haven’t heard already, I am looking for a new job opportunity… and yes, I realize the title of this blog is speaking in the 3rd person ;)

I am primarily looking for a full-time position where I can either work in a Nashville office or work remotely from Nashville.

I am a Microsoft MVP of ASP.NET and a member of the ASPInsiders group.

I specialize in developing with jQuery and ASP.NET MVC. I tend to focus on the UI only (everything up to the web service call to the middle tier).

I enjoy blogging, writing technical articles, speaking, and playing with the latest and greatest web development tools and libraries.

You can find my resume on my LinkedIn profile.

I look forward to hearing more about your company & the positions that you have available.

Please contact me either through LinkedIn, Twitter (@elijahmanor), or my Contact page.

Read More »

jQuery jqGrid Plugin: Add, Edit, Delete with ASP.NET MVC

 

Introduction

There are a lot of articles out there that show you how to integrate with the jQuery jqGrid Plugin from a listing, paging, sorting approach, but I haven’t seen many that show the integration with the add/edit/delete features that jqGrid offers.

It turns out that it isn’t very difficult to do, but it took quite a bit of digging in the jqGrid API documentation for me to find all the things I needed.

The following article will show how to customize the add/edit/delete modal experience inside of the jqGrid with ASP.NET MVC.

Contact ViewModel

First, you start off with your ViewModel. In this case the following is really bare bones. I haven’t annotated the properties with metadata because I actually do that manually in the jqGrid columns. That isn’t optimal, but it would be nice to have these automatically mapped. That sounds like another blog post ;)

public class ContactViewModel
{
public System.Guid ContactId { get; set; }

public string Name { get; set; }

public string Email { get; set; }

public string PhoneNumber { get; set; }

public DateTime DateOfBirth { get; set; }

public bool IsMarried { get; set; }
}

Contact View

The following code setups up the jqGrid to support add, edit, and delete.

The first function you’ll see is a custom validator that checks to see if the phone number has a length of 14. Yes, it isn’t bullet-proof validation by any stretch of the imagination, but its more of an example of what can be done.

Next you’ll see the updateDialog object literal defining the look and behavior of the add, edit, and delete dialog windows. The main property to define is the URL where the AJAX requests will post the data. You can also control whether the dialog closes immediately, if it’s a modal dialog, etc…

The next important thing to notice is the “key: true” property of the ContactId column. If you don’t set this property then the POST for the delete command  only send the relative ID that jqGrid generates, not the ContactId that you need. So, this is important ;)

Note: You’ll see some code below setting global properties for the jqGrid such as the title of the dialogs, buttons, etc. If you don’t do this then you’ll get generic titles. I figured these customizations made the user experience a little nicer, but things will work just find without them.

function isValidPhone(value, name) {
console.log('isValidPhone');
var errorMessage = name + ': Invalid Format';
var success = value.length === 14;
return [success, success ? '' : errorMessage];
}

$(document).ready(function () {
var updateDialog = {
url: '<%= Url.Action("Update", "Contact") %>'
, closeAfterAdd: true
, closeAfterEdit: true
, afterShowForm: function (formId) {
$("#PhoneNumber").mask("(999) 999-9999");
$("#DateOfBirth").datepicker();
}
, afterclickPgButtons: function (whichbutton, formid, rowid) {
$("#PhoneNumber").mask("(999) 999-9999");
}
, modal: true
, width: "400"
};

$.jgrid.nav.addtext = "Add";
$.jgrid.nav.edittext = "Edit";
$.jgrid.nav.deltext = "Delete";
$.jgrid.edit.addCaption = "Add Contact";
$.jgrid.edit.editCaption = "Edit Contact";
$.jgrid.del.caption = "Delete Contact";
$.jgrid.del.msg = "Delete selected Contact?";

$("#list").jqGrid({
url: '<%= Url.Action("List", "Contact") %>',
datatype: 'json',
mtype: 'GET',
colNames: ['ContactId', 'Name', 'Date of Birth', 'E-mail', 'Phone Number', 'Married'],
colModel: [
{ name: 'ContactId', index: 'ContactId', width: 40, align: 'left', key: true, editable: true, editrules: { edithidden: false }, hidedlg: true, hidden: true },
{ name: 'Name', index: 'Name', width: 300, align: 'left', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'} },
{ name: 'DateOfBirth', index: 'DateOfBirth', width: 200, align: 'left', formatter: 'date', datefmt: 'm/d/Y', editable: true, edittype: 'text', editrules: { required: true, date: true }, formoptions: { elmsuffix: ' *'} },
{ name: 'Email', index: 'Email', width: 200, align: 'left', formatter: 'mail', editable: true, edittype: 'text', editrules: { required: true, email: true }, formoptions: { elmsuffix: ' *'} },
{ name: 'PhoneNumber', index: 'PhoneNumber', width: 200, align: 'left', editable: true, edittype: 'text', editrules: { required: true, custom: true, custom_func: isValidPhone }, formoptions: { elmsuffix: ' *'} },
{ name: 'IsMarried', index: 'IsMarried', width: 200, align: 'left', editable: true, edittype: 'checkbox', editoptions: { value: "True:False" }, editrules: { required: true}}],
pager: $('#listPager'),
rowNum: 1000,
rowList: [1000],
sortname: 'ContactId',
sortorder: "desc",
viewrecords: true,
imgpath: '/Content/Themes/Redmond/Images',
caption: 'Contact List',
autowidth: true,
ondblClickRow: function (rowid, iRow, iCol, e) {
$("#list").editGridRow(rowid, prmGridDialog);
}
}).navGrid('#listPager',
{
edit: true, add: true, del: true, search: false, refresh: true
},
updateDialog,
updateDialog,
updateDialog
);
});

Contact Controller Update Action

The add/update/delete feature takes one URL where you can change the logic based on the operation type. The MVC Modal Binder will map the fields into your ViewModel in most cases. The exception is the “id” that is passed on the delete operation, but there is a way to get around that later in this post ;)

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
var operation = formCollection["oper"];
if (operation.Equals("add") || operation.Equals("edit"))
{
repository.SaveOrUpdate(new ContactViewModel
{
ContactId = viewModel.ContactId,
DateOfBirth = viewModel.DateOfBirth,
Email = viewModel.Email,
IsMarried = viewModel.IsMarried,
Name = viewModel.Name,
PhoneNumber = viewModel.PhoneNumber
});
}
else if (operation.Equals("del"))
{
repository.Delete(new ContactViewModel
{
ContactId = new Guid(formCollection["id"])
});
}

return Content(repository.HasErrors.ToString().ToLower());
}

What About Using Complex Keys?

Instead of having ContactId (“key: true”) as your key to delete, you might have a more complex key to identify which item to delete. As it turns out, you can bind to the onclickSubmit event of the add/edit/delete dialog and change what data is POST’ed to the controller.

A nice side effect of this is that you name your property such that the MVC Modal Binder works.

Updated Dialog Object Literal

var updateDialog = {
url: '<%= Url.Action("Update", "Contact") %>'
, closeAfterAdd: true
, closeAfterEdit: true
, afterShowForm: function (formId) {
$("#PhoneNumber").mask("(999) 999-9999");
$("#DateOfBirth").datepicker();
}
, afterclickPgButtons: function (whichbutton, formid, rowid) {
$("#PhoneNumber").mask("(999) 999-9999");
}
, modal: true
, onclickSubmit: function (params) {
var ajaxData = {};

var list = $("#list");
var selectedRow = list.getGridParam("selrow");
rowData = list.getRowData(selectedRow);
ajaxData = { ContactId: rowData.ContactId };

return ajaxData;
}
, width: "400"
};

Updated Contact Controller Update Action

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
var operation = formCollection["oper"];
if (operation.Equals("add") || operation.Equals("edit"))
{
repository.SaveOrUpdate(new ContactViewModel
{
ContactId = viewModel.ContactId,
DateOfBirth = viewModel.DateOfBirth,
Email = viewModel.Email,
IsMarried = viewModel.IsMarried,
Name = viewModel.Name,
PhoneNumber = viewModel.PhoneNumber
});
}
else if (operation.Equals("del"))
{
repository.Delete(new ContactViewModel
{
ContactId = viewModel.ContactId
});
}

return Content(repository.HasErrors.ToString().ToLower());
}

Conclusion

I hope you found the above article of some use in your everyday coding. The jqGrid also provides an inline editing feature similar to what you might experience in an Excel grid. You might look into that if you are interested.

Please give me your feedback. Thanks!

Download Source Code



Read More »

Filling Address Fields using HTML5 Geolocation and jQuery

I saw an interesting tweet by Remy Sharp (@rem) the other day that sparked my interest.

47b091843184dc342cdd0f26c4943ed0

So, I proceeded to research the Geolocation API and look up various web services that allowed me to utilize JSONP requests from jQuery.

Here is some pseudocode to describe what I came up with…


if (Your browser supports the GeoLocation API) {
if (Geolocation data contains address) {
Use address to populate form (Firefox)
} else {
Use Long/Lat to get address from web service & populate form (Chrome)
}
} else {
Use IP address to get address from web service & populate form (IE)
}

 

cooltext439925164

Note: You may notice depending on the browser not all of the fields are being filled out. This is because of the web services that I am utilizing are lacking one or more fields or vary depending on your location. If I find a better web service then I’ll update the script.

Read More »

BDD-Style QUnit Testing ASP.NET MVC’s jQuery Validation

Client-Side Unit Testing


<div></div>
The goal of this blog post is to show how you can utilize some helpful techniques to easily Unit Test your Web Application. In this post I will focus on Unit Testing the Client-Side validation rules that ASP.NET MVC generates. You can apply the following techniques to pretty much any scenario, but since this is something I do, I thought I’d share.

Our sample applications is a Contact Manager. At this point we only have a toolbar with a “New Contact” button. When the button is clicked a “New Contact” dialog will appear with several input fields and a “Save” and “Cancel” button. All of the fields are required, so if the user clicks the “Save” button client-validation should verify that all fields have a value.

<h4>ASP.NET MVC Contact ViewModel</h4>
First lets take a look at our ViewModel which will drive the rules of our client-side validation.

<pre>using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace jQueryUnitTestingFormValidation.Models
{
public class ContactViewModel
{
[HiddenInput(DisplayValue = false)]
public System.Guid Id { get; set; }

[Required(ErrorMessage = “Name Required”)]
[DisplayName(“Name”)]
[StringLength(50, ErrorMessage = “Name must be less than or equal to 50 characters”)]
public string Name { get; set; }

[Required(ErrorMessage = “Email Required”)]
[DisplayName(“E-mail”)]
[StringLength(50, ErrorMessage = “Email must be less than or equal to 50 characters”)]
[DataType(DataType.EmailAddress)]
[RegularExpression(@”^([a-z0-9!#$%&’+/=?^_{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_{|}~-]+)@(?:a-z0-9?.)+a-z0-9?\s;?\s)+$”, ErrorMessage = “Email must be valid”)]
public string Email { get; set; }

[Required(ErrorMessage = “Phone Number Required”)]
[DisplayName(“Phone Number”)]
[StringLength(50, ErrorMessage = “Phone must be less than or equal to 50 characters”)]
public string PhoneNumber { get; set; }

[Required(ErrorMessage = “Date of Birth Required”)]
[DisplayName(“Date of Birth”)]
public DateTime? DateOfBirth { get; set; }

[Required(ErrorMessage = “Required”)]
[DisplayName(“Is Married”)]
public bool IsMarried { get; set; }
}
}</pre>
<h4>ASP.NET MVC Contact View</h4>
Our View is pretty simple. I decided for this sample to not modify the MasterPages that the Templated Helpers use. You’ll see that I’m call the LabelFor, EditorFor, and ValidationMessageFor and organizing them as I wish. If you are interested in a cleaner way to do this you can check out a previous blog entry I wrote entitled Opinionated ASP.NET MVC 2 Template Helpers.

<pre><div id="createDialog" title="New Contact" style="display: none;">
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm(“Create”, “Contact”, FormMethod.Post, new {@id = “createPost”})) { %>
<dl>
<dt><%= Html.LabelFor(m => m.Name) %></dt>
<dd>
<%= Html.EditorFor(m => m.Name) %>
<%= Html.ValidationMessageFor(m => m.Name) %>
</dd>
<dt><%= Html.LabelFor(m => m.Email) %></dt>
<dd>
<%= Html.EditorFor(m => m.Email) %>
<%= Html.ValidationMessageFor(m => m.Email)%>
</dd>
<dt><%= Html.LabelFor(m => m.PhoneNumber) %></dt>
<dd>
<%= Html.EditorFor(m => m.PhoneNumber) %>
<%= Html.ValidationMessageFor(m => m.PhoneNumber)%>
</dd>
<dt><%= Html.LabelFor(m => m.DateOfBirth) %></dt>
<dd>
<%= Html.EditorFor(m => m.DateOfBirth) %>
<%= Html.ValidationMessageFor(m => m.DateOfBirth)%>
</dd>
<dt><%= Html.LabelFor(m => m.IsMarried) %></dt>
<dd>
<%= Html.EditorFor(m => m.IsMarried) %>
<%= Html.ValidationMessageFor(m => m.IsMarried)%>
</dd>
</dl>
<% } %>
</div></pre>
<h4>JavaScript Contact Revealing Module</h4>
The Contact Revealing Module contains the logic to initialize the button and dialog events, post the form to the Controller, etc…

Note: I am utilizing the Revealing Module Pattern for those of you who might not be aware of it. It is very helpful in splitting up your JavaScript code into testable and reusable modules.

<pre>var contactCreateModule = (function () {
var public = {},
dialogWidth = 800;

public.createDialog;
public.createPost;

public.init = function () {
public.createDialog = $(“#createDialog”);
public.createPost = $(“#createPost”);

public.initEventHandlers();
};

public.initEventHandlers = function () {
public.initToolbar();

public.initDialog();
};

public.initToolbar = function () {
$(“#toolbar button”).button();

$(“#createContact”).click(function () {
public.displayCreate();
});
};

public.initDialog = function () {
$(“.datePicker”).datepicker();

public.createDialog.dialog({
autoOpen: false
, width: dialogWidth
, modal: true
, open: validationModule.clearValidationMessages
, buttons: {
“Cancel”: function () {
public.createDialog.dialog(“close”);
},
“Save”: function () {
if (public.createPost.valid()) {
public.createDialog.dialog(“close”);
public.postContact();
}
}
}
});
};

public.displayCreate = function () {
public.createDialog
.dialog(“open”)
.find(“input:first”)
.focus();
};

public.postContact = function (callback) {
$.ajaxSettings.traditional = true;
$.ajax({
url: public.createPost.attr(“action”),
data: public.createPost.serialize(),
type: “POST”,
success: function (data, textStatus, xhr) {
public.postContactSuccess(data, textStatus, xhr);
callback(data && data.Success);
},
error: public.postContactError
});
};

public.postContactSuccess = function (data, textStatus, xhr) {
if (data && data.Success) {
notificationModule.displayMessage(true, “Your contact has been created!”);
} else {
notificationModule.displayMessages(data.Success, data.Messages);
}
};

public.postContactError = function (xhr, textStatus, error) {
var errorMessage = exception ? exception : xhr.statusText;
notificationModule.displayMessage(false, “There was an error creating your contact: “ + errorMessage);
};

return public;
} ());</pre>
<h4>Classic QUnit Style Tests</h4>
I initially started this blog post using standard QUnit syntax, but there was something about it that didn’t stand well with me. In particular, I didn’t like how I had a bunch of asserts all lumped together. Later in this article I switch out the classic QUnit-Style with a BDD-Style syntax.

The following are some screenshots from the Unit Tests…

<div></div>
The following is a slightly zoomed in view of the above image. You can see that all the asserts for one particular test are hidden beneath it. You can expand &| collapse the test to see each individual assert.

<div></div>
You can view the unit tests below that generated the above screen shots.
Since I don’t want the $.ajax call to actually occur in my Unit Tests, I swap out the default functionality with a stub function instead in the module setup (which is called before each test). In the module teardown (which is called after each test) I restore the default functionality in case any future test needs it.

<pre>var contactWasPosted = false;
module(“Contact: Create”, {
setup: function () {
contactWasPosted = false;

contactCreateModule.postContactBackup = contactCreateModule.postContact;
contactCreateModule.postContact = function (callback) {
contactWasPosted = true;
};
},
teardown: function () {
contactCreateModule.postContact = contactCreateModule.postContact;
contactCreateModule.createDialog.dialog(‘close’);
}
});

test(“When New Contact Button Clicked”, function () {
//Arrange

//Act
$(“#createContact”).click();

//Assert
ok($(“#createDialog:visible”).length, “Dialog Should Display”);
ok($(“#Name_validationMessage:not(:visible)”).length, “Name Validation Should Not Display”);
ok($(“#Email_validationMessage:not(:visible)”).length, “Email Validation Should Not Display”);
ok($(“#PhoneNumber_validationMessage:not(:visible)”).length, “PhoneNumber Validation Should Not Display”);
ok($(“#DateOfBirth_validationMessage:not(:visible)”).length, “DateOfBirth Validation Should Not Display”);
ok($(“#IsMarried_validationMessage:not(:visible)”).length, “IsMarried Validation Should Not Display”);
});

test(“When Click Save On an Empty Form”, function () {
//Arrange
$(“#createContact”).click();

//Act
$(“.ui-button-text:contains(‘Save’)”).parent().click();

//Assert
ok($(“#Name_validationMessage:visible”).length, “Name Validation Should Display”);
ok($(“#Email_validationMessage:visible”).length, “Email Validation Should Display”);
ok($(“#PhoneNumber_validationMessage:visible”).length, “PhoneNumber Validation Should Display”);
ok($(“#DateOfBirth_validationMessage:visible”).length, “DateOfBirth Validation Should Display”);
ok($(“#IsMarried_validationMessage:visible”).length, “IsMarried Validation Should Display”);
ok($(“#createDialog:visible”).length, “Dialog Should Remain Displayed”);
});

test(“When Click Save On an Complete Form”, function () {
//Arrange
$(“#createContact”).click();

$(“#Name”).val(“xNamex”);
$(“#Email”).val(“tasty@bacon.com”);
$(“#PhoneNumber”).val(“xPhoneNumberx”);
$(“#DateOfBirth”).val(“xDateOfBirthx”);
$(“#IsMarried”).attr(“checked”, true);

//Act
$(“.ui-button-text:contains(‘Save’)”).parent().click();

//Assert
ok($(“#Name_validationMessage:not(:visible)”).length, “Name Validation Should Not Display”);
ok($(“#Email_validationMessage:not(:visible)”).length, “Email Validation Should Not Display”);
ok($(“#PhoneNumber_validationMessage:not(:visible)”).length, “PhoneNumber Validation Should Not Display”);
ok($(“#DateOfBirth_validationMessage:not(:visible)”).length, “DateOfBirth Validation Should Not Display”);
ok($(“#IsMarried_validationMessage:not(:visible)”).length, “IsMarried Validation Should Not Display”);
ok(contactWasPosted, “Contact Should Post”);
ok($(“#createDialog:not(:visible)”).length, “Dialog Should Be Closed”);
});</pre>
<h4>BDD Style QUnit Tests </h4>
After talking more with Dan Mohl (@dmohl) I decided I wanted to try to find a Behavior Driven style of writing QUnit tests. I know there are several other BDD Client-Side Unit Test frameworks out there, but I wanted to keep to the QUnit runner for now.

So, during my research the author of Pavlov, Michael Monteleone (@namelessmike), let me know about his project, which ended up to be exactly what I was looking for.

The following is the output of my tests using QUnit and Pavlov…

<div></div>
Here is a slightly zoomed in view of the QUnit Pavlov test output…

<div></div>
The structure of the Unit Tests is dramatically different from the above classic Unit Tests.

The first thing you’ll notice is that I am extending the Assertion definitions to clean up some of the assert code that I had in my previous Unit Tests.

I still have the same logic in from the above Unit Tests that was in the setup and teardown methods, but now you can find those in the before and after methods.

The syntax of Pavlov is very readable from an English standpoint. You basically describe some scenario in words, and then split it out into code. It was very refreshing once I put it all together.

<pre>QUnit.specify.extendAssertions({
isNotDisplayed: function(actual, expected, message) {
ok(actual.is(“:hidden”) || actual.text().length == 0, message || “okay: isNotDisplayed”);
},
isDisplayed: function (actual, expected, message) {
ok(actual.is(“:visible”) || actual.text().length > 0, message || “okay: isDisplayed”);
}
});

QUnit.init({ moduleTestDelimeter: “, it “ });
QUnit.specify.globalApi = true;
QUnit.specify(“Contact”, function () {

describe(“Create”, function () {

var contactWasPosted;

before(function () {
contactWasPosted = false;

contactCreateModule.postContactBackup = contactCreateModule.postContact;
contactCreateModule.postContact = function (callback) {
contactWasPosted = true;
};

$(“#createContact”).click();
});

after(function () {
contactCreateModule.postContact = contactCreateModule.postContact;
contactCreateModule.createDialog.dialog(‘close’);
});

describe(“When the contact button is clicked”, function () {
it(“should display the dialog”, function () {
assert($(“#createDialog:visible”).length).isEqualTo(1);
});

it(“should not display name validation”, function () {
assert($(“#Name_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display email validation”, function () {
assert($(“#Email_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display PhoneNumber validation”, function () {
assert($(“#PhoneNumber_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display DateOfBirth validation”, function () {
assert($(“#DateOfBirth_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display IsMarried validation”, function () {
assert($(“#IsMarried_validationMessage:visible”)).isNotDisplayed();
});
});

describe(“When clicking save on an empty form”, function () {
before(function () {
$(“.ui-button-text:contains(‘Save’)”).parent().click();
});

it(“should keep the dialog displayed”, function () {
assert($(“#createDialog:visible”).length).isEqualTo(1);
});

it(“should display Name validation”, function () {
assert($(“#Name_validationMessage:visible”)).isDisplayed();
});

it(“should display Email validation”, function () {
assert($(“#Email_validationMessage:visible”)).isDisplayed();
});

it(“should display PhoneNumber validation”, function () {
assert($(“#PhoneNumber_validationMessage:visible”)).isDisplayed();
});

it(“should display DateOfBirth validation”, function () {
assert($(“#DateOfBirth_validationMessage:visible”)).isDisplayed();
});

it(“should display IsMarried validation”, function () {
assert($(“#IsMarried_validationMessage:visible”)).isDisplayed();
});
});

describe(“When clicking save on a completed form”, function () {
before(function () {
$(“#Name”).val(“xNamex”);
$(“#Email”).val(“tasty@bacon.com”);
$(“#PhoneNumber”).val(“xPhoneNumberx”);
$(“#DateOfBirth”).val(“xDateOfBirthx”);
$(“#IsMarried”).attr(“checked”, true);

$(“.ui-button-text:contains(‘Save’)”).parent().click();
});

it(“should not display name validation”, function () {
assert($(“#Name_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display email validation”, function () {
assert($(“#Email_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display PhoneNumber validation”, function () {
assert($(“#PhoneNumber_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display DateOfBirth validation”, function () {
assert($(“#DateOfBirth_validationMessage:visible”)).isNotDisplayed();
});

it(“should not display IsMarried validation”, function () {
assert($(“#IsMarried_validationMessage:visible”)).isNotDisplayed();
});

it(“should post contact”, function () {
assert(contactWasPosted).isTrue();
});

it(“should close the dialog”, function () {
assert($(“#createDialog:visible”).length).isEqualTo(0);
});
});

});

});</pre>
<h4>Conclusion</h4>
The more and more I find myself creating highly dynamic websites, the more I find the need to Unit Test the browser interaction.

I hope you found the above example helpful. I would be interested to hear what tools you use to help Unit Test your client-side code. Please share… it makes us all better :)
You can find some other helpful client-side Unit Testing tools in the Script Junkie article I wrote entitled jQuery Test-Driven Development. In addition I wrote several other jQuery related articles that you can find on Script Junkie.

Note: It was not my intention to exhaustively Unit Test everything in the above example. There are other things I would Unit Test, but to make this example easy to understand in a bite-sized chuck, I limited myself to some simple examples.

Download Source Code


Read More »