Question

We wrote some Watir that would recursively look through a page's frames until it found the element you were asking for, and it would then return it to you. It doesn't seem like we can do this with Watir-WebDriver.

What we used to do:

So essentially we'd run something like:
findButton(:id, "Login_button")

And we'd loop through all the frames in the page, looking for this button.

def findButton(desc, b = @browser)
# look for object in main page  
    if b.button(desc).exist?  
        obj = b.button(desc)  
        #return and be done.
    else  
        # look for object in frames  
        count = b.document.frames.length  
        (1..count).each do |i|  
            if b.frame(:index,i).button(desc).exist?  
                obj = b.frame(:index,i).button(desc)  
                break  
            end  
        end  
        if obj == nil  
            (1..count).each do |i|  
                obj = find(desc, b.frame(:index,i))  
                if obj != nil  
                    break  
                end  
            end  
        end  
    end  
    if obj == nil && b == @browser  
        raise "Can't find button with descriptor #{desc}"  
    end  
    obj  
end

We'd then use the element object returned:
findButton(:id, "login_button").click, for example.

Why it doesn't seem like we can anymore

So now we're evaluating Watir-WebDriver and the document element doesn't appear to be part of the Watir browser object anymore... but that's okay, right? So then I went and looked through the frames, collection:

browser = Watir::Browser.new :ie
{......}
browser.frames

First off, Browser.frames takes about 2-3 seconds to return any data, even when there's only one frame (Latest watir-webdriver Gem as of today's date, Ruby 193p0, and IE9). Secondly, it doesn't seem like frame object that is returned actually contains the element access I need. browser.frames[1].button(:id, "Login_Button") returns a variety of errors, depending on what I'm looking for.

It almost seems like this exposes a WebDriver limitation that Watir-WebDriver was hoping to work around, in that WebDriver just doesn't resemble a DOM structure, and elements don't always have the proper "Types" that is familiar to Watir, and in fact to the DOM itself.

Some might say it's silly to loop through frames looking for an element, but it's just one of those times where it's best if I have that capability. Our application uses anywhere from 3-5 frames at any given moment, and you can't always predict what frame or what order an element might be in.

Have I missed something? Am I not understanding a fundamental principle of Watir-WebDriver?

Was it helpful?

Solution

There doesn't seem to be a problem with the watir-webdriver API. For example:

require 'watir-webdriver'

def find_element_in_all_frames browser, &block
  element = block.call browser
  return element if element.exists?
  browser.frames.each do |frame|
    result = find_element_in_all_frames frame, &block
    return result if result
  end
  return nil
end

Watir::always_locate = false
browser = Watir::Browser.start 'http://www.w3schools.com/html/tryit.asp?filename=tryhtml_frame_mix'
element = find_element_in_all_frames(browser) { |b| b.h3(:text => 'Frame A') }
puts element.text
browser.close

works completely fine, and returns the correct element. When running with Watir::always_locate set to true, it takes about 5 seconds, with it set to false, about 2 seconds.

You'd use it as:

element = find_element_in_all_frames(browser) { |b| b.button(:id :> "login_button") }
element.click

I don't think this time is unreasonable as the tool is basically switching contexts multiple times to find the element.

If you really want things to work better, it's time to rewrite your app without frames.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top