donderdag 31 januari 2008

Using WatiN to test select lists in an update panel

UPDATE:  This looks like another great way to handle automating selectlists in AJAX scenarios http://pushpontech.blogspot.com/2008/04/ajax-issues-with-watin.html

In our web application we make use of update panels to give the user a nicer UI experience. On one of our web pages a SELECT element contains a list of countries. When the user selects a country from this list, a post back occurs and another SELECT element is populated with states for the selected country. A very basic scenario.

When automating this scenario with WatiN the following test would pass if the select elements are not wrapped in an update panel.

[Test]
public void ShouldOnlySeeStatesFromSelectedCountry()
{
// Open the web page with country and state selection lists
IE ie = new IE("http://localhost/dropdownwithwatintoken/MyDetailsPage.aspx");

// Select a country from the list
ie.SelectList("countries").Option("Netherlands").Select();

// Do the asserts
Assert.That(ie.SelectList("states").Options.Length, Is.EqualTo(12));
Assert.That(ie.SelectList("states").Option("Utrecht").Exists);
}


But after the countries and states lists were wrapped in an update panel, this test started to fail. WatiN doesn't recognize an AJAX call so it will not wait until it is finished. So what happens: After the select is made an async postback is initiated. Then the next line in the test is executed but this is before the response from the server is received and rendered. So no values are in the states list and the test will fail.



Although each element in WatiN has a WaitUntil(attributeconstraint) method, for this test we couldn't figure out what to wait for in the UI to know for sure the async postback was finished (received and rendered). So we decided to add a Thread.Sleep(someSeconds) for the time being. Although this worked, the test was waiting longer than necessary. If there only was an attribute on the SELECT element that would change after the async postback.... Using the WaitUntil method, we could wait for the change of the attribute value and then proceed.



The solution was to create a new custom web control that inherits from DropDownList and overrides the AddAttributesToRender method.



public class DropDownWithWatiNToken : System.Web.UI.WebControls.DropDownList
{
protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
{
base.AddAttributesToRender (writer);
if (AutoPostBack)
{
writer.AddAttribute("watintoken", DateTime.Now.Ticks.ToString());
}
}
}


Each time the HTML for this control gets rendered an attribute "watintoken" will be added with the current date and time represented in ticks. So now we have the needed attribute to check for!



To use this we need to replace our asp:DropDownList usages in our web page(s). To do so you should register this custom control in your aspx web page in which you are going to use the DropDownWithWatiNToken control.



<%@ Register TagPrefix="watin" Namespace="CustomWebControls" Assembly="DropDownWithWatiNToken" %>


Then replace asp:DropDownList with watin:DropDownWithWatiNToken. It should look something like this:



<watin:DropDownWithWatiNToken ID="countries" Runat="server" AutoPostBack="True" />


Now that we have changed the controls on the web page, we can use the "watintoken" attribute in our test and wait until it is changed. After the attribute value has changed, we can proceed the test.



[Test]
public void ShouldOnlySeeStatesFromSelectedCountry()
{
// Open the web page with country and state selection lists
IE ie = new IE("http://localhost/dropdownwithwatintoken/MyDetailsPage.aspx");

// Remember the value of the watintoken attribute
string watintoken = ie.SelectList("countries").GetAttributeValue("watintoken");
// Select a country from the list
ie.SelectList("countries").Option("Netherlands").Select();
// Wait until the value of the watintoken attribute is changed
ie.SelectList("countries").WaitUntil(!Find.By("watintoken",watintoken));

// Do the asserts
Assert.That(ie.SelectList("states").Options.Length, Is.EqualTo(12));
Assert.That(ie.SelectList("states").Option("Utrecht").Exists);
}


And all tests are green again!



Tags van Technorati: