Wednesday, June 16, 2010

To download modified copy of Selenium RC Server (To handle modal window) click here selenium-server.jar

Tag this link to see more updates on Selenium server.

Thursday, April 22, 2010

Working with Modal Dialogs and Selenium - 2

TABLE OF CONTENTS

1. Introduction To Modal Dialog

2. Properties of a modal dialog

3. Problem with Selenium

4. Unblocking Selenium for Automation

5. Ways of Modal invocation and Challenges

1. Introduction To Modal Dialog

Internet Explorer has provided additional function, showModalDialog, to deal with Modal type windows. When we open a window using showModalDialog the java-script execution gets suspended till the window gets closed. With this feature in place parent window can set itself under wait state expecting return value from the popup window. The popup window before closing itself needs to set its returnValue property, which will be used by the parent window. A sample code is given below.

Main.htm
<script type="text/javascript">
function getUser5()
{
var retValue=window.showModalDialog('popup.htm','',…);
alert(retValue);
}
</script>
<span><a id="'btnModal2'" onclick="return getUser5">Open Popup</a></span>

popup.htm
<script type="text/javascript">
function dosubmit()
{
window.returnValue=document.getElementById("txtName");
window.close();
}
</script>

<form id="'frm'">
Name<input id="txtName" title="Your Google Toolbar can fill this in for you. Select AutoFill" style="BACKGROUND-COLOR: #ffffa0"></input>
<a id="'btnClose'" onclick="return dosubmit();">Submit</a>
</form>

In above example, the parent window opens up a modal window and waits for the modal to return some value. The user enters some text in textbox on the modal window and clicks the link button. The link button calls the javascript to set the returnValue and then closes itself. The return value received by parent is used for further execution.


2. Properties of a Modal dialog


· Modal effect:When modal is opened, accessibility to parent window is blocked.

· Passing of arguments from parent window to modal: Modal window has defined variable called ‘dialogArguments’ which is passed as an argument to showModalDialog function. Modal window can access it with ‘window.dialogArguments’reference.

· Return of value from modal to parent: Modal window has defined variable called ‘returnValue’which needs to be set before window gets closed. This returnValue is passed as return from the modal dialog to the parent window. The value remains even after the modal has been closed.

3. Problem with Selenium

Selenium works on Javascript. It needs to move its handle across windows to perform its operation. When showModalDialog is called the javascript gets suspended for the parent. Selenium whose handle is still pointing the parent window also gets suspended. As a result all the successive commands in Selenium script ultimately get suspended and automation gets blocked.

4. Unblocking Selenium

To retain the normal flow of the selenium the only solution left is to bypass showModalDialog call with normal ‘window.open’ function call. This can be achieved by overriding showModalDialog function as shown below:

window.showModalDialog = function( sURL,vArguments, sFeatures)
{
if(retVal!=null) return retVal;
modalWin = window.open(sURL, 'modal', sFeatures)
}

Above function will open a non-modal dialog when ever showModalDialog function is being called. With this selenium is good to go and do any operations on the popup window. However to get the exact behavior of a modal dialog one need to understand the properties of a modal window and mimic the same behavior over non modal window.

To pass the argument from parent to modal we need to save the arguments in a variable on the parent window. Then we will inject a code on the modal window that will read this value from the parent and save it in ‘window.dialogArguments’ variable.

If we open the window as non-modal, the biggest challenge will be to pass the return value from the modal back to the parent window. This is because when we open a window using ‘window.open’ commands the javascript do not wait for the return value and soon comes out of the function without performing its intended operation after retrieving the return value from the modal. To overcome this we will call the instruction (probably a button click) that calls the showModalDialog function twice. First time when we do a click it will open the non-modal dialog and comes out of the function. On the second click it will use the return value that was saved by the non-modal dialog to perform post-showModalDialog operations. All the operations to be performed on the popup window will go in between the first and the second clicks.

To pass the return value back to the caller function, we need to override the close function or inject onbeforeunload event on the popup window that will save the return value on the parent window. Parent window will use this value during the second click and perform post-showModalDialog operations.

3. Ways of Modal invocation and challenges

Some of the recognized ways of invoking a modal window and the challenges faced are given below:

· Opens a normal modal window

· Intermediate window

o Modal opens another window and closes itself: When a modal window opens another window and then closes itself, selenium will not be able to trace this window.

Solution: To overcome this we can block auto closing of the intermediary window. Once the operation is completed on the target window then the window should be closed.

Ref: CSH Help window in LM.

o Modal calculates and returns a value to parent and closes self. Parent then uses the value to open another modal.

Solution: These scenarios can be handled by allowing the first window to open as modal and the second as non modal.

· Modals invoked at Page onload: Selenium waits till page is completely loaded. Once loaded it then injects javascript codes to handle events on the page. However if the page calls a modal window during onload of the page, selenium will get blocked since showModalDialog is not overridden yet.

Solution: This can be solved by application side patch (HttpModule). The patch will embed the overridden javascript code to each page. Though this opens a non-modal window, the window is still not traceable by selenium. This is because popup at onload do not gets registered to selenium. To overcome this we can call window.open with empty URL and same window id. Doing this will attach the selenium handle to the existing window rather opening a new window.

· Modal with iframe embedded: Modal iframes if calls close function closes the window. But in case of non-modal it just closes the iframe.

Solution: This can be worked out by overloading close function with top.close. It also requires setting the value of ‘window.returnValue’ to ‘window.top.returnValue’.

· Multiple modals (Modal over modal): When a popup opens another popup, it becomes necessary that each window should be identified uniquely in order for selenium to identify them correctly.

Solution: Each modal window should be suffixed with a timestamp string.

Code Reference : Download the attached source code and replace the given function in your selenium-browserbot.js file. More info at Selenium Site


Download modified Selenium RC

Monday, November 30, 2009

Avoid XPath when possible
Using XPath as a location strategy for your elements can be dangerous for long term maintenance for your tests as changes to the markup will undoubtedly break your tests. Likewise, certain browsers (cough cough IE) have poor XPath engines and are considerably slower (IE is about 16x slower).
Strangely enough, following accessibility guidelines also makes for better functional UI testing. So instead of XPath locators, consider:
Use "id" whenever feasible.
selenium.Click("close");
Use "alt" tags for images.
selenium.Click("alt=close window");
Use text inside anchor tags when ids or images are not used.
selenium.Click("link=Home");

Avoid timing code
When working with AJAX or Postback events, page load speed can vary per machine or request. Rather than putting timing code in your NUnit code (ie Thread.Sleep), take advantage of one of the selenium built-in WaitFor... selenese commands.
To use, you place javascript code in the condition where the last statement is treated as a return value.// wait 30 seconds until an element is in the DOM
selenium.WaitForCondition("var element = document.getElementById('element'); element;", "3000");
This approach allows your code to be as fast as the browser rather than set to a fixed speed.

Friday, September 25, 2009

Changing Selenium Jar to globally handle Modal Dialog box

  • Unpack Selenium jar. (User JRE jar.exe utility)
  • Open selenium-browserbot.js file.
  • Search for function 'IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs'"
  • Add following codes at the begning of this function


if(win.showModalDialog)
{
if (typeof(win.top.g_selRetVar) == 'undefined')
win.top.g_selRetVar = null;
win.top._UTL_SetSelRetVar = function (val){
win.top.g_selRetVar = val;
win.top.status = win.top.g_selRetVar + ' is returned from child';
};
win.showModalDialog = function( sURL, vArguments, sFeatures)
{
if ((typeof(win.top.g_selRetVar) != 'undefined') && (win.top.g_selRetVar!=null))
{
var temp = win.top.g_selRetVar;
win.top.g_selRetVar = null;
return temp;
}
try {
win.top.open (sURL, 'modal', sFeatures);
} catch(e){ win.showModalDialog(sURL, vArguments, sFeatures); }
};
}
if(win.close)
{
win.attachEvent('onbeforeunload', function _selhandler()
{
if(win.opener && win.opener._UTL_SetSelRetVar)
win.opener._UTL_SetSelRetVar(win.returnValue);
});
}


  • Pack the jar again. (User JRE jar.exe utility)
If you do not wish to change your jar file you can inject the javascript through your script. This has been mentioned in
Handling Modal windows
Download modified Selenium RC

Tuesday, July 14, 2009

Locator / XPath Generator

Work in Progress. Subscribe yourself to get the release mail.

Basics:
XPath/Locator plays vital role for Selenium automation. XPath for the same control can have various forms. Different people have different way of creating XPath. However best XPath is direct and small. ID is the best option for a locator. Larger the XPath, longer will be the processing time. Same is the case with wild card characters uses. Also 'contains' should be avoided if possible. Its important that XPath should be well constructed. Most of the XPath Generator available generates XPath which is either long or inefficient. This tool will be smart enough to generate best XPath for Selenium automation.

Features to be included:
Direct XPath: Look for Id or the smallest XPath that can identify the element.
Relative XPath: If the element need to be identified relative to another element.

If you have any suggestion or want some feature to be included do share with us.

Friday, June 26, 2009

Check if control is Enabled

public bool IsEnabled(string controlId)
    {
        bool retValue = false;
        try
        {
            string disabled = _selObj.GetEval("selenium.browserbot.getCurrentWindow().getElementById('" + controlId + "').disabled");
            retValue = disabled.ToLower() == "true" ? false : true;
            if (retValue)
                Log.Info(DateTime.Now.ToString() + " : Control " + controlId + " is enabled.");
            else
                Log.Info(DateTime.Now.ToString() + " : Control " + controlId + " is disabled.");
        }
        catch (Exception ex)
        {
            Log.Error(DateTime.Now.ToString() + " : Error occured while determining if control " + controlId + " is enabled");
            throw new Exception("Error occured at SeleniumPage.IsEnabled on '" + controlId + "'", ex);
        }
    return retValue;
}

Avoiding WaitForPageLoad

public bool WaitForBrowserStability(int maxWait)
{
    bool retValue = false;
    DateTime dtStart = DateTime.Now;
    do
    {
        System.Threading.Thread.Sleep(sleepTime);
        if (IsBrowserLoaded())
        {
            retValue = true;
            break;
        }
    } while (((TimeSpan)DateTime.Now.Subtract(dtStart)).TotalMilliseconds < maxWait);
    return retValue;
}
public bool IsBrowserLoaded()
{
    try
    {
        return ("true" == _selObj.GetEval("((\"complete\" == selenium.browserbot.getCurrentWindow().document.readyState) && (null == selenium.browserbot.getCurrentWindow().event))"));
    }
    catch (SeleniumException selExc)
    {
        Log.Warn(DateTime.Now.ToString() + " : Selenium error encountered. " + selExc.Message);
        _selObj.SelectWindow("");
        return false;
    }
    catch (Exception exc)
    {
        throw exc;
    }
}