Search code examples
rubyseleniumxpathwatirpage-object-gem

How can I get Watir to make a fresh reference on a non-stale element?


A portion of some tests I am writing calls for checking if an option gets removed from a select list once that option has been used. I am inconsistently getting this error: timed out after 60 seconds, waiting for {:xpath=>"//select[@id = 'newIdentifierType']//option", :index=>31} to be located (Watir::Exception::UnknownObjectException)

It causes my test to fail maybe 2-3 times out of 10 runs and seems kind of random. I think Watir is looking for the "old" select list with this ID since it caches the element and may also include that it had 32 items, but it times out since a select list with this ID and 32 items no longer exists. The new select list has the same ID but only 31 items.

Is there a way to always get a new reference on this element even though it's not technically going stale? Am I experiencing this problem due to a different issue?

My current code for getting the options in the select list:
@browser.elements(:xpath => "//select[@id = 'newIdentifierType']//option")

I am using Ruby/Cucumber with Selenium and Watir Webdriver level. I first tried defining the element as a select_list in a page-object but moved it to the step definitions using @browser.element to see if that would stop the timeout. I thought it may ignore Watir's cached elements and get the most current one with the ID, but that does not appear to be the case.


Solution

  • Please avoid using XPath with Watir. Everything you can do with XPath, Watir has a much more readable API to handle.

    To check for a specific option not being there, you should avoid collections and locate directly:

    el = browser.select_list(id: "newIdentifierType").option(value: "31"))
    # or 
    el = browser.select_list(id: "newIdentifierType").option(text: "This one"))
    

    Then to see if it has gone away:

    el.stale?
    # or
    el.wait_until(:stale?)
    

    That won't test the right thing if the entire DOM has changed, though, so you might need to just relocate:

    browser.select_list(id: "newIdentifierType").option(text: "This one")).present?
    

    If you are intent on using a collection, the correct way to get the list of options is:

    options = @browser.select(id: 'newIdentifierType').options
    el = options.find { |o| o.text == 'This one' }
    # Do things
    el.stale?