Update: The javascript on this blog has been suppressed by the blogger's parser. Consequently, some of the functionality may not work. Inconvenience is regretted.

Thursday, April 3, 2008

AJAX Issues With WATIN

Recently I started Automating the UI test cases for our AJAX enabled asp.net website with Watin. It was not long before that I ran into problems as Watin seems to be ajax postback agnostic.

I had a master dropdownlist and a child dropdownlist (this is disabled until something is selected in the master DDL). On the selection of an item in the master DDL there is an AJAX postback that would get data for the child DDL and then an item in the child DDL will be selected. To automate this scenario I wrote the following code:


IE ie = new IE("http://localhost/MySite/Default.aspx");
SelectList ddlMaster = ie.SelectList("ddlMaster");
ddlMaster.Option(Find.ByValue("India")).Select();
SelectList ddlChild = ie.SelectList("ddlChild");
ddlChild.Option(Find.ByValue("Hyderabad")).Select();


The Problem: The first DDL gets selected perfectly and an asynchronous postback happens but I get an exception in the fourth line (which tries to select a value in the child DDL) saying "Could not find OPTION element tag matching criteria: Attribute 'value' with value 'Hyderabad'". The problem was clear - we are trying to select a value in the child DDL before asynchronous postback is over and the child DDL is populated with values.

The first thought that came into my mind was to put a Thread.Sleep but I know it wasn't a good solution since I can never be sure of how long the asynchronous request is going to take. So I looked for something that could tell me if page is still in async postback. Enter the PageRequestManager Class in asp.net ajax. It has a property called isInAsyncPostback that you can query to know if page is in async postback. So all I needed was a way to run some javascript from the code and thank god, there is Eval in Watin to do just that.

The Solution:

1)Updated the aspx page with a method to return true if page is in async postback as below:


<asp:ScriptManager ID="scrptMgr" runat="server" EnablePartialRendering="true">
</asp:ScriptManager> <!-- script manager shown for completeness -->
<script type="text/javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();
function IsPageInAsyncPostback(){
return prm.get_isInAsyncPostBack();
}

</script>

2) Wrote a helper method to wait until async postback is complete:


private static void WaitForAsyncPostBackToComplete(IE ie)
{
bool isInPostback = true;
while (isInPostback)
{
isInPostback = Convert.ToBoolean(ie.Eval("IsPageInAsyncPostback();"));
if (isInPostback)
Thread.Sleep(200); //sleep for 200ms and query again
}
}


3) Updated the test scenario code as below:


IE ie = new IE("http://localhost/MySite/Default.aspx");
SelectList ddlMaster = ie.SelectList("ddlMaster");
ddlMaster.Option(Find.ByValue("India")).Select();
WaitForAsyncPostBackToComplete(ie);
SelectList ddlChild = ie.SelectList("ddlChild");
ddlChild.Option(Find.ByValue("Hyderabad")).Select();


and all was merry!!!

Hope this is helpful.

pushp

8 comments:

Jeroen said...

Hi,

Just saw your comment on my blog and then I read this great article! I will update my post about testing with selectlists and AJAX and include a link to your article.

Happy testing with WatiN,
Jeroen

Randy Klingelheber said...

Thanks for the post. I used your idea along with a couple mods:

1. Added the call to get the PageRequestManager to the Watin Eval statement so that I didn't have to change the page under test.

2. Created an extension method to add the method 'WaitUntilAsyncPostBackComplete' to Watin's IE class.

Anonymous said...

Hi Randy,

Can you please explain how you "added the call to get the PageRequestManager to the Watin Eval statement so that you didn't have to change the page under test."?

Randy Klingelheber said...

public static class IEExtension
{
private const int WaitTimeInMilliseconds = 200;

public static void WaitUntilAsyncPostBackComplete(this IE ie)
{
while (IsInAsyncPostBack(ie))
{
Thread.Sleep(WaitTimeInMilliseconds);
}
}

public static bool IsInAsyncPostBack(this IE ie)
{
const string isAsyncPostBackScript = "Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()";
return bool.Parse(ie.Eval(isAsyncPostBackScript));
}
}

Anonymous said...

I love you.

Daniel said...

Randy's solution looks great. Sadly, I'm testing a ColdFusion website.

Does anyone know of some javascript I could run that would tell me the async postback state of IE7 running a ColdFusion page?

Thanks in advance...

Anonymous said...

Nice! It resolved my frustration!

Anonymous said...

Very Interesting Blog! Thank You For Thi Blog!