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