Finding the next empty text field

Problem

As I continued to investigate making Watir-Classic tests compatible with Watir-Webdriver, I noticed one test that failed to input the next empty row of a text field grid. The grid could start empty or it could start with rows pre-populated with saved values:

<html>
  <body>
    <table>
      <tr>
        <td><input type="text" id="name_01" value="existing name"></td>
        <td><input type="text" id="value_01" value="existing value"></td>
      </tr>
      <tr>
        <td><input type="text" id="name_02"></td>
        <td><input type="text" id="value_02"></td>
      </tr>
      <tr>
        <td><input type="text" id="name_03"></td>
        <td><input type="text" id="value_03"></td>
      </tr>      
    </table>
  </body>
</html>

The test looked for the first row with an empty field and inputted it, repeating if multiple rows of data were needed. The Watir-Classic code boiled down to:

next_field = browser.text_field(id: /name/, value: '')
p next_field.id
#=> "name_02"

next_field.set('some text')
next_field = browser.text_field(id: /name/, value: '')
p next_field.id
#=> "name_03"

Sadly, the same code failed with Watir (previously named Watir-Webdriver):

next_field = browser.text_field(id: /name/, value: '')
p next_field.id
#=> unable to locate element, using {:id=>/name/, :value=>"", :tag_name=>"input or textarea", :type=>"(any text type)"} (Watir::Exception::UnknownObjectException)

Solution

The problem is that Watir’s XPath builder will look for an element that has the value attribute with an empty string value. Unfortunately, that is not the same as our element, which does not have any value attribute. We could address this by writing our own XPath locator that checks for the absence of the value attribute (or wait for the functionality to be added to Watir in Issue 345):

next_field = browser.text_field(xpath: './/input[@type="text"][not(@value)]')
p next_field.id
#=> "name_02"

next_field.set('some text')
next_field = browser.text_field(xpath: './/input[@type="text"][not(@value)]')
p next_field.id
#=> "name_02"

This only works for the first row after the pre-populated rows. As seen by the attempt to input the second blank row, Watir grabs the same row, which is no longer empty. I recently learned on Stack Overflow from Tom Walpole, that this is due to the value property being set by the set method, but not the value attribute. Selenium-WebDriver only checks the attribute, which as we have not changed, results in the same row being returned each time.

Another option would be to exploit the fact that Watir will check the value property when it cannot build an XPath expression. Locators using non-trivial regular expressions, such as that for our empty string /^$/, will trigger this behaviour.

next_field = browser.text_field(id: /name/, value: /^$/)
p next_field.id
#=> "name_02"

next_field.set('some text')
next_field = browser.text_field(id: /name/, value: /^$/)
p next_field.id
#=> "name_03"

As you can see, we got the desired result. Note that this may break in future releases. Finding ways to convert regular expressions into XPath provides an improvement boost in locators. While Watir currently only optimizes a couple of scenarios, there is the possibility that more will be done. Those changes could result in the above code once again checking the value property. If you want to avoid that risk, you may want to explicitly check the property:

next_field = browser.text_fields(id: /name/).find { |t| t.value.empty? }
p next_field.id
#=> "name_02"

next_field.set('some text')
next_field = browser.text_fields(id: /name/).find { |t| t.value.empty? }
p next_field.id
#=> "name_03"
Advertisements
This entry was posted in Watir, Watir Migration, Watir-Webdriver and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s