x-treme
x-treme

Reputation: 1636

pageobject - when_visible for all elements

I am using a combination of cucumber and pageobject to test my web application. Sometimes, the script tries to click an element even before the page that contains the element starts loading. (I confirmed this by capturing the screenshots of failing scenarios)

This inconsistency is not wide-spread and it happens repeatedly only for a few elements. Instead of directly accessing those elements, if I do example_element.when_visible.click, the test suite always passes.

As of now, I click a link using link_name (generated by pageobject module on calling link(:name, identifier: {index: 0}, &block)

I would like to not edit the above mentioned snippet, but act as if i called link_name_element.when_visible.click. The reason is, the test suite is pretty large and it would be tedious to change all the occurences and I also believe that the functionality is already present and somehow I don't see it anywhere. Can anybody help me out?!

Upvotes: 1

Views: 1343

Answers (1)

Justin Ko
Justin Ko

Reputation: 46836

This seems solution seems quite hacky and may not be considering some edge cases. However, I will share it since there are no other answers yet.

You can add the following monkey patch assuming that you are using watir-webdriver. This would be added after you require page-object.

require 'watir-webdriver'
require 'page-object'

module PageObject
  module Platforms
    module WatirWebDriver
      class PageObject
        def find_watir_element(the_call, type, identifier, tag_name=nil)
          identifier, frame_identifiers, wait = parse_identifiers(identifier, type, tag_name)
          the_call, identifier = move_element_to_css_selector(the_call, identifier)

          if wait
            element = @browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}.when_present"
          else
            element = @browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}"
          end
          switch_to_default_content(frame_identifiers)
          type.new(element, :platform => :watir_webdriver)
        end        

        def process_watir_call(the_call, type, identifier, value=nil, tag_name=nil)
          identifier, frame_identifiers, wait = parse_identifiers(identifier, type, tag_name)
          the_call, identifier = move_element_to_css_selector(the_call, identifier)

          if wait
            modified_call = the_call.dup.insert(the_call.rindex('.'), '.when_present')
            value = @browser.instance_eval "#{nested_frames(frame_identifiers)}#{modified_call}"
          else
            value = @browser.instance_eval "#{nested_frames(frame_identifiers)}#{the_call}"
          end

          switch_to_default_content(frame_identifiers)
          value
        end

        def parse_identifiers(identifier, element, tag_name=nil)
          wait = identifier.has_key?(:wait) ? false : true
          identifier.delete(:wait)

          frame_identifiers = identifier.delete(:frame)
          identifier = add_tagname_if_needed identifier, tag_name if tag_name
          identifier = element.watir_identifier_for identifier
          return identifier, frame_identifiers, wait
        end        
      end
    end
  end
end

Basically, the intent of this patch is that the Watir when_present method is always called. For example, your page object call will get translated to Watir as browser.link.when_present.click. In theory, it should get called for any method called on a page object element.

Unfortunately, there is a catch. There are some situations where you probably do not want to wait for the element to become present. For example, when doing page.link_element.when_not_visible, you would not want to wait for the element to appear before checking that it does not appear. In these cases, you can force the standard behaviour of not waiting by including :wait => false in the element locator:

page.link_element(:wait => false).when_not_visible

Upvotes: 2

Related Questions