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;
    }
}

Monday, June 22, 2009

Selecting Popup window without Id

public bool SelectTopWindow()
{
    try
    {
        string[] arr = GetAllWindowNames();
        if (arr.Length != 0)
        {
            System.Threading.Thread.Sleep(1000);
            _selObj.SelectWindow(arr.GetValue(arr.Length - 1).ToString());
            return true;
        }
    }
    catch (Exception exc)
    {
        return false;
    }
}

Saturday, May 30, 2009

Uploading Files using Selenium RC in C#

After googling all around to overcome the Selenium incapability of handling File Upload control finally I could write a simple function in C# which will do my job. In the function given below _selObj is the object of DefaultSelenium class.

public bool TypeIntoFileUpload(string controlId, string filePath)
{
    try
    {
        string newFilePath = filePath.Replace('\\', '/');
         _selObj.WindowFocus();
         _selObj.Focus(controlId);
        string jscript="";
        jscript += "if(selenium.browserbot.getCurrentWindow().clipboardData){window.clipboardData.setData('Text','" + newFilePath + "');}";
         _selObj.GetEval(jscript);
        byte VK_CONTROL = 0x11;
        byte VK_V = 0x56;
        _selObj.KeyDownNative(Convert.ToString(VK_CONTROL));
        _selObj.KeyPressNative(Convert.ToString(VK_V));
        _selObj.KeyUpNative(Convert.ToString(VK_CONTROL));

        return true;
    }
    catch (Exception exc)
    {
        return false;
    }
}

Thursday, January 22, 2009

Handling Modal window with Selenium

Aim:
To unblock Selenium when Modal Window appears on the screen.

Basics:
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.

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




popup.htm

<script>
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 the closes itself. The return value received by parent is used for further execution.

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.

Possible workaround:
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 defining a global function as shown below:

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

However return value from these popup could not be tracked as these windows do not have returnValue parameter. This can be overcome by writing extra line of code using selenium script to get the required value from the popup window.
Let’s consider a simple javascript function that opens up a modal window into three sections:



function processModal ()
{
Prescript
Var returnValue = Open Modal window
Postscript
}



Opening a window with this function unblocks the Selenium. However the Postscript gets executed too early. Assuming Postscript doesn’t do any function with null set to returnValue, we can invoke the processModal twice. The first time it will open the popup and fetch the value from it. The next time it will bypass any window open executing just the Postscript based on the return value captured during the first run.



Implementation:
Based on above consideration I have written three functions:

  • ClickAndSelectModalDialog: This function accepts the control id on clicking which the modal dialog opens. It bypasses the modal call to open non-Modal window. It also moves the handle to the popup.




  • public bool ClickAndSelectModalDialog(string controlId)
    {
    try
    {
    if (ClickForModalDialog(controlId))
    {
    SelectTopWindow();
    string jscript = "";
    {
    jscript += "if(selenium.browserbot.getCurrentWindow().close){";
    jscript += "window.attachEvent(\"onbeforeunload\", function _selhandler(){";
    jscript += "if(window.opener && window.opener._UTL_SetSelRetVar){";
    jscript += "window.opener._UTL_SetSelRetVar(window.returnValue);";
    jscript += "}});}";
    }
    _selObj.GetEval(jscript);
    return true;
    }
    else throw new Exception("Error occured while opening a modal window.");
    }
    catch (Exception exc)
    {
    throw new Exception("Error occured while Clicking and selecting a modal window.", exc);
    }
    }




    public bool ClickForModalDialog(string controlId)
    {
    try
    {
    string jscript = "";
    jscript = "typeof(window.g_selRetVar) != 'undefined' ? window.g_selRetVar : 'undefined'";
    string modalValue = _selObj.GetEval(jscript);
    Log.Info("Modal Return value is " + modalValue);
    { //Bypass showModalDialog call
    jscript = "if(selenium.browserbot.getCurrentWindow().showModalDialog){";
    { // Define variable g_selRetVar
    jscript += "if (typeof(selenium.browserbot.getCurrentWindow().g_selRetVar) == 'undefined') selenium.browserbot.getCurrentWindow().g_selRetVar = null;";
    }
    { //Define function _UTL_SetSelRetVar
    jscript += "selenium.browserbot.getCurrentWindow()._UTL_SetSelRetVar = function (val){ window.g_selRetVar = val; window.status = window.g_selRetVar + ' is returned from child';};";
    }
    jscript += "selenium.browserbot.getCurrentWindow().showModalDialog = function( sURL, vArguments, sFeatures)";
    jscript += "{if ((typeof(window.g_selRetVar) != 'undefined') && (window.g_selRetVar!=null)) {var temp = window.g_selRetVar; window.g_selRetVar = null; return temp;}";
    jscript += "selenium.browserbot.getCurrentWindow().open(sURL, 'modal', sFeatures);";
    jscript += "};}";
    }
    _selObj.GetEval(jscript);

    _modalControl = controlId;
    this.Click(_modalControl);
    if (modalValue == null || modalValue == "" || modalValue == "undefined")
    WaitForPopUp("modal", "60000");

    return true;
    }
    catch (Exception exc)
    {
    Log.Error(exc);
    throw new Exception("Unable to open modal window.", exc);
    }
    }




    public bool AcceptModalValue()
    {
    this.ClickForModalDialog(_modalControl);
    _modalControl = "";
    Log.Info(DateTime.Now.ToString() + " : Parent window accepted value from Modal window.");
    return true;
    }


    Example:

    Consider Page to be the object of Class that defines above method and other Selenium methods.(Wrapper class)



    //Open/Select Modal Window
    Page.ClickAndSelectModalDialog("//a[@title='Change domain']");
    //Perform on Modal Wiondow
    Page.SelectFrame("//IFRAME");
    string xpath = "//INPUT[contains(@value,'Global')]";
    Page.Click(xpath);
    //Perform on Modal Wiondow Continued...
    Page.Click("idtask_Next");
    //Select Parent Window
    Page.SelectWindow(null);
    //Invoke parent to accept Madal value
    Page.AcceptModalValue();


    Note: If you want to handle this globally you need to add this code in your selenium jar file. The steps are given under Changing Selenium Jar to globally handle Modal Dialog box
    Download modified Selenium RC