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

32 comments:

  1. Very well explained. Thanks Amit.

    ReplyDelete
  2. Hi Amit,

    Thanks for the valuable comments on Show-Modal Dialog Box using selenium, We do have several show modal dialog box in our application but note that, some show modal dialog boxes are not opening the perfect application window (Non modal window) with the code changes in browserbot.js, It's showing undefined in the window (non modal window) and the actual functionality of the window is missing (e.g. if the modal pop up is for confirmation for delete something with the confirmation message and Yes no Button then it shows the message as undefined with yes no button, also on clicking on yes button it's just closing the window without performing the actual delete), It's not setting the return value for the new non modal window, Please let me know if you have any pointer for it.

    ReplyDelete
  3. Amit, also note that, the code you suggested not setting up the dialog.argument for the new opened non modal window so it's shows as undefined, is there any way to set the dialog argument to the non modal window

    ReplyDelete
  4. you can check your code against following:

    http://msdn.microsoft.com/en-us/library/ms533723(VS.85).aspx

    Click on Show mw button from that page

    ReplyDelete
  5. Modified Selenium Server has been added in the post.

    ReplyDelete
  6. Hi,

    I try to used the Modified Selenium Server you have posted but it doesn't work for me.
    I still have problem with popup opened by showModalDialog.
    I work with Firefox 3. Is there a problem with this browser ?

    ReplyDelete
  7. showModalDialog is a function only in IE. You need not use any alternatives when you are using Firefox for testing.

    ReplyDelete
  8. Hi Amit,

    I have check the new selenium server file, but still getting the same error "Undefined".

    URL to test:
    http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/dialogArgumentsCallerEX1.htm

    Click on Button "Launch The Window".

    Check the result, It shows undefined.

    Regards,
    Ashish

    ReplyDelete
  9. Hi Amit,
    I was trying out the example..
    The "main.html" that you have provided in the example does not seem to work(on clicking the open popup nothing comes up).anythng extra need to be added?
    Also noticed that spanid html tag used is the closure tag for the starting tag

    ReplyDelete
  10. I traced an tag issue in the Main.html code. Rectified the same. Please try it again. Sorry for your inconvenience.

    ReplyDelete
  11. Hi Amit,

    Did you get any resolution/pointer.

    I am unable to iget that main.html

    Regards

    ReplyDelete
  12. Hi Amit,

    Greetings!

    Did you find any solution for seeting up the ShowModal Dialogbox Arguments on non modal window?

    Regards.

    ReplyDelete
  13. Ashish!

    Sorry for late replying. I didnt get muct time to go through your first query. I will see this tonight. For your second query, the attached selenium server already has implemented the setting up the dialog arguments. There is one specific scenario where the current logic will fail. The scenario is when a function opens up a modal window, and then process the return from the modal. With current logic we are bound to make two clicks. If the first click (which passes on null value to later part of the function) lands the logic into error state, the whole logic under consideration will fail. However this is very specific case. If you find any resolution to this let me know.

    ReplyDelete
  14. Hi Amit,

    Same with Anonymous (June 17, 2010 12:46 AM), I tested with Firefox 3 and the modal dialog made the selenium wait indefinitely.
    Maybe it does not work well with other browsers as well?

    ReplyDelete
  15. Hi Amit,

    sample html code provided is not working and have some javascript issue.

    ReplyDelete
  16. got it... it's small javascript syntex issue in Main.html and popup.html...

    after correcting that it's work fine for me...

    ReplyDelete
  17. Hi Amit,

    i understand for what scenario it is and the explanation behind to use HttpModule. But can you let us know what exactly you are referring to by "application side patch (HttpModule)" and where I can get all the details regarding that:-

    "application side patch (HttpModule)"?

    ReplyDelete
  18. HttpModule is an assembly that is placed with web application and alters every request of the pages as per the requirement. In the current context we nbeed to write an httpModule that should inject javascript code (overridden showModalDialog). Doing this will impact all pages so get in touch with application developer to know all the impacted areas with the module. Further if you have any common js page that is reference in all your web pages, it would be good idea to place the code there. This will not require to write HttpModule. Both thiese approaches changes the application behavior which many doesnt like however this will not require any selenium server changes for showModalDialog. For more information on HttpModule refer : http://msdn.microsoft.com/en-us/library/zec9k340(v=VS.71).aspx or http://www.15seconds.com/issue/020417.htm

    ReplyDelete
  19. Hi Amit,

    I tried to use your workaround for modal dialogs in our application following the instructions in http://seleniumdeal.blogspot.com/2010/04/working-with-modal-dialogs-and-selenium.html - the popup dialog was opened but with "false" value and with javascript error: "Message: 'dialogArguments' is undefined"
    Could you please advise ?

    Thanks,
    Yaffit

    ReplyDelete
  20. Hi amit,
    Thx for your explanations..
    Still i am not able to do popup with your modified JAR file..
    I tried below code and not working...Actually seems to be different
    prob.
    1) selenium.Click(“css=input[id$='_LookupFooterImageButton']“);
    2) String feedWinId = selenium.GetEval(“{var windowId; for(var x in selenium.browserbot.openedWindows ) {windowId=x;} }”);
    3) selenium.SelectWindow(feedWinId);
    4) selenium.WindowFocus();
    5) selenium.Type(“//form[@id='form1']/table//*[@id='SearchTextBox']“, “p”);
    Here while executing, selenium RemoteRunner executes line 1 which is going to open popup window and after that no response for long time..
    So I closed that opened popup window manually while executing, Immediately lines 2,3 and 4 are executing….
    Line 5 is the command to give input on popup window

    Selenium Remote Runner - Command History shows only upto command
    selenium.Click("css=input[id$='_LookupFooterImageButton']");. After no
    response. Here whatever coded available after this line is not
    executing.
    But when i close popup window manually, immediately further lines are
    executing....This is my exact problem...
    Hope u got my prob..

    Can u help me?

    ReplyDelete
  21. Hi Amit.
    I also have a problem with modal windows in Firefox3. Modal window makes the selenium to wait until I manually close it. Do you have any idea how to handle this windows?

    ReplyDelete
  22. The work around here targets to handle modal in IE as IE has defined different function 'showModalDialog' to open a modal window. There is no such concept in Firefox as yet. However the developers generally achieve modal behavior in Firefox through Javascript code. To handle this you may need to look towards the devs code. The whole logic goes like:
    1. You need to trace the new popup window.
    2. Try moving pointer on the popup window.
    3. Perform operation.
    Take your devs assistance to know how a popup is getting open as a modal window.

    ReplyDelete
  23. We use Firefox 3 and call showModalDialog, I got this to work by using the approach in this page: http://seleniumdeal.blogspot.com/2009/09/changing-selenium-jar-to-globally.html, but instead of modifying IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs, I've modified the global: BrowserBot.prototype.modifyWindowToRecordPopUpDialogs...Also found that win.attachEvent doesn't work in firefox but instead use addEventListener, this does use an extra parameter so i pass it true...i now can get modal dialogs to work in Firefox, but not IE, go figure

    ReplyDelete
  24. Thanks for all this info. I have modal dialogs working in both IE and Firefox 3 but am stuck. We open up iframes in the modal dialogs and when i submit the form within the iframe the parent modal window stays open. I see you mention that you have to modify the .close function to close the top window. Can you explain that in more detail. the win.close function in the attached jar already checks for return values from this.top but the only window that is closes is this., I'm having trouble with order of closing the windows.

    ReplyDelete
  25. Probably this could be the environment issue. I faced the same issue on Vista, IE8. Please give a try with PIExplore rather iexplore while initializing the selenium server. Hope this helps.

    ReplyDelete
  26. I tried it with PIExplore but same problem. I'm using IE8 on Win7 64-bit...Any other ideas?

    ReplyDelete
  27. Bob, can you please let me know if you only have to change the win.attachEvent method for Firefox?(As I've changed this to addEvenListener but I'm still not getting the modal window to work)

    ReplyDelete
  28. How correctly to refactor the script in the new version of selenium?
    What rules should be followed?

    ReplyDelete
  29. Hi Amit,

    I'm new to webdriver and I have scenario where a modal dialog appears asking the user to reset the password, i'm unable to identify the element on the modal dialog box, it always returns element not found. I'm using XPATH to identify the elements.

    i have tried many ways but hard luck. could you just help me on this coz we have lot of Modal Dialogue boxes in our application

    Thanks
    Anil veluru

    ReplyDelete
  30. This comment has been removed by the author.

    ReplyDelete
  31. Hi Amit ,

    Can you please help me resolve the issue in ie9

    I have replaced the required code to handle modialogs in the selenium-browserbot.js file in latest selenium 2.38 jar, the tool handles and works fine for all the browsers , but doesnot work on IE9

    the .close function that is " Overriding close function to save the return value before actual close" is blocking the tool from initiating any command and finally after long wait the test case fails

    if(windowToModify.close)
    {
    // We will retain the original behaviour of close to be used when needed.
    windowToModify._closeEvent = windowToModify.close;
    // Overriding close function to save the return value before actual close
    windowToModify._close = function()
    {
    try
    {
    var _opener;

    if(this.opener)
    _opener = this.opener;
    else _opener = this.top.opener;
    var retValue = 'N.A.';
    if(_opener && _opener._UTL_SetSelRetVar)
    {
    if ((typeof(this.returnValue) != 'undefined') && (this.returnValue!=null))
    retValue = this.returnValue;
    else if ((typeof(this.top.returnValue) != 'undefined') && (this.top.returnValue!=null))
    retValue = this.top.returnValue;
    _opener._UTL_SetSelRetVar(retValue);
    }

    _opener=this;
    this.close = this._closeEvent;
    this.name=new Date().getTime();
    window.open("", this.name, "");
    this.close();
    }
    catch(e){}
    };

    ReplyDelete
  32. Please provide other link to download code as google drive is restricted in my working environment

    ReplyDelete