Tuesday, April 3, 2012

Frames and WebDriver

When dealing with iframes and WebDriver things can quickly get confusing. Especially if you add popup windows to the mix.

When you have an iframe, it is a separate DOM. You can look at it as a separate web page inside the current web page. Lets take an example diagram:


If we look at the source code for this it might be something like:
<html>
    <body>
    <iframe src="frame1.html" style="border: red;">
        <html>
        ...
        </html>
    </iframe>
    <iframe id="2" src="frame2.html" style="border: green;">
        <iframe id="2-1" src="frame2-1.html">...</iframe>
        <iframe id="2-2" src="frame2-2.html">...</iframe>
        <iframe id="2-3" src="frame2-3.html">...</iframe>
        <iframe id="2-4" src="frame2-4.html">....</iframe>
        <iframe id="2-5" src="frame2-5.html">....</iframe>
        <iframe id="2-6" src="frame2-6.html">...</iframe>
        <iframe id="2-7" src="frame2-7.html">...</iframe>
        <iframe id="2-8" src="frame2-8.html">...</iframe>
        <iframe id="2-9" src="frame2-9.html">...</iframe>
    ...
    </iframe>
    <iframe id="3" src="frame3.html" style="border: brown;">
        <iframe id="3-1" src="frame3-1.html">...</iframe>
        <iframe id="3-2" src="frame3-2.html">...</iframe>
    ...
    </iframe>
    <iframe id="4" src="frame4.html" style="border: blue;">
        <iframe id="4-1" src="frame4-1.html">...</iframe>
        <iframe id="4-2" src="frame4-2.html">...</iframe>
        <iframe id="4-3" src="frame4-3.html">...</iframe>
        <iframe id="4-4" src="frame4-4.html">...</iframe>
        <iframe id="4-5" src="frame4-5.html">...</iframe>
        <iframe id="4-6" src="frame4-6.html">...</iframe>
        <iframe id="4-7" src="frame4-7.html">...</iframe>
        <iframe id="4-8" src="frame4-8.html">...</iframe>
        <iframe id="4-9" src="frame4-9.html">...</iframe>
    ...
    </iframe>
    </body>
</html>
All the rectangles in the diagram are iframes. The iframe with the red border would be the first iframe in the HTML code. Inside each iframe will be a full HTML page. It will have the <html></html> and everything which goes inside an HTML page.

So in WebDriver you have a switchTo method. The switchTo method returns a WebDriver.TargetLocator interface. If we look at the WebDriver.TargetLocator interface we see the following methods:

  • frame(int index)
  • frame(String nameOrId)
  • frame(WebElement frameElement)
  • defaultContent()
We can use the index to find the frame. If you have one main page and it contains a set of iframes, this is fine. However, if you have frames within frames it can get a little difficult to follow. Even if you can figure it out today, you will have to go through the whole exercise again if they change the layout by moving, adding or deleting a frame.

The best way to find a frame is with the id attribute or find the frame element using findElement() then use the third version listed above.

Now here is the most important thing to remember: you cannot jump in two or more frames. So if you are at the main content page and want an element in frame3-1.html you have to switch to frame3.html then to frame3.1.html. Assuming we are at the main page, the code for this might look like:


    driver.switchTo().frame("3");
    driver.switchTo().frame("3-1");


Additionally, if I have switched to frame4-7.html and I want to go to frame2-1.html, I have to go back to the top, then to frame2.html, then to frame2-1.html.

So how do you get to the top? If you are dealing with iframes then the defaultContent() method will take you to the main page, above all the iframes. If you are dealing with frames, defaultContent() will take you to the first frame.

So you can either leave focus were every you last left it then every action in a frame assumes you are at some unknown focus, call driver.switchTo().defaultContent() to get to the top, then go down to the frame you want OR you can start at the top, go to the frame you want then back to the top when you are done. The first way you go to the top as needed. The second way you ensure you are always at the top. Both ways work. It is just a matter of convention.

Sometimes drawing the layout of the page is a little harder then the diagram above. Additionally, if the layout changes, it can be difficult to alter the picture, depending on how you drew it. What I like to do is draw the relationships like a tree. For example, the diagram above might be draw as:


From this tree the rules are that you can go down a branch (switchTo().frame() from the parent) or you can get to the root of the tree (defaultContent() for iframes). You cannot jump across nodes or up levels.

9 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Good article. Thanks for the insight. Found this out the hard way myself while porting RC test to WebDriver.

    Want to point out one thing though, you may encounter this issue while porting RC tests since Selenium RC abstracted away the frame & iframe DOM navigation concept of WebDriver automatically taking care of it behind the scenes for you.

    For those who automated tests where Selenium RC took care of the iframes for you and you didn't have to explicit know/find the iframe, this will come as a surprise when you find element with Firebug related tools and (don't spend too much time looking around contextually whether it's nested inside some iframe) as WebDriver will then fail to find element that you assumed in RC was on the main page.

    ReplyDelete
  3. Very Informative,

    How do you replace selenium api's WaitForFrameToLoad, since I still haven't found a effective way to replace it, since some times you click something on the right frame and the left frame loads but when u switch to it it fails. Any feedback on this is much appreciated.

    ReplyDelete
  4. Anonymous, in manual testing I wait for an element to be able to click. Rather than wait for a page to load I wait until the element I want to click on is available.

    With a frame it would be more loop until the frame is available to switch to then loop until the element within the frame is available to click.

    If you want more details please ask at the WebDriver Google Group. It is a better place to have an interactive conversation. I check that group daily.

    ReplyDelete
  5. I was trying to switch to an inner iframe in one step - like in the selenium documentation:

    { It’s possible to access subframes by separating the path with a dot, and you can specify the frame by its index too. That is:

    driver.switchTo().frame("frameName.0.child");

    }

    but it doesn't work.
    any idea why?

    ReplyDelete
  6. Lotem hiki, the frame() method takes a frameName, an index or 'child'. The dot separator the Selenium documentation refers to is not the input to frame() but chaining multiple calls, i.e.

    driver.switchTo().frame("frameName").switchTo().frame(0).switchTo("child");

    This is really just short hand for:

    driver = driver.switchTo().frame("frameName");
    driver = driver.switchTo().frame(0);
    driver = driver.switchTo().frame("child");

    ReplyDelete
  7. Wow!!! This is what I was looking for...thanks for a great post!!!Continue the good work!!!

    ReplyDelete
  8. This is a great stuff, well written and clear. When I switch to the frame listed below, I am asked to enter name, card# and zip code then press OK.
    iframe src="https://www.somesite.com/framesite.aspx id="id1" name="name1" width="350" height="110" frameborder="0" scrolling="no"
    /iframe>
    If name, card# and zipcode are correct, it displays a thank you page.
    How do you switch to the thank you page to verify that your test passed or failed?

    Thanks,
    Hamid Assous

    ReplyDelete
  9. Hamid,

    If the thank you page is on the default iframe then you need to switch back to the default page using driver.switchTo().defaultContent().

    If the thank you page is a different window then you will need to use http://darrellgrainger.blogspot.ca/2012/04/how-to-find-popup-window-if-it-does-not.html to open the pop up window.

    If it is a different iframe then you will need to switch to the new iframe before dealing with the tahnka you page.

    ReplyDelete

Note: Only a member of this blog may post a comment.