4. Inspecting an Element

4.1 Text

The text method returns the visible text of an element.

Given the html:

<div>This is the text of the element.</div>

The text of the div element is:

browser.div.text
#=> "This is the text of the element."

Note that the text of an element includes:

  • The text nodes of the element and
  • The text nodes of the element’s descendants.

The following span has its own text node, “ is a Ruby gem.”, as well as a descendant text node, “Watir”.

<span><a href="watir.com">Watir</a> is a Ruby gem.</span>

The text of the span is the concatenation of the text nodes:

browser.span.text
#=> "Watir is a Ruby gem."

Also note that Watir will only include the text that is visible to the user. In the following div, the first span is visible to the user, while the second span is not.

<div>
  <span>visible text</span>
  <span style="display:none;">hidden text</span>
</div>

Therefore, text of the div will only be the first span:

browser.div.text
#=> "visible text"

4.2 Attribute value

Attributes provide additional information or behaviour for an element. They can be found within the element’s start tag.

Watir provides 2 ways to get attribute values:

  1. Using the method that returns the specific attribute’s value.
  2. Using the attribute_value method.

Standard attributes

For attributes that are defined in the HTML specification, there is a corresponding method that returns the attribute value. In most cases, the attribute name and the method name are exactly the same. However, Ruby naming conventions are applied in some situations.

Scenario Naming Rule Example Attribute Corresponding Method
Single word attribute Match attribute name id id
Multi-word attribute Words separated by underscore maxlength max_length
Boolean attribute End with question mark disabled disabled?
Data attribute Dashes replaced by underscores data-field data_field
Aria attribute Dashes replaced by underscores aria-describedby aria_describedby
Class attribute Due to Ruby objects already having a class method, which returns the instance’s class, the “class” attribute is a special case. class class_name

The following HTML has several attributes:

<div data-field="first_name">
  <label id="tp1-label" for="first">First Name:</label>
  <input type="text" id="first" maxlength="50" required="required"
    aria-labelledby="tp1-label" aria-describedby="tp1">
  <div id="tp1" class="tooltip" role="tooltip" hidden="hidden"
    aria-hidden="true">Your first name is a required</div>
</div>

The attribute methods can be used to get the values:

# Retrieve a single word attribute
browser.text_field.id
#=> "first"

# Retrieve a multi-word attribute
browser.text_field.max_length
#=> 50

# Retrieve a boolean attribute
browser.text_field.required?
#=> true
browser.text_field.disabled?
#=> false

# Retrieve a data attribute
browser.div.data_field
#=> "first_name"

# Retrieve an aria attribute
browser.input.aria_labelledby
#=> "tp1-label"

# Retrieve a class attribute
browser.div(id: 'tp1').class_name
#=> "tooltip"

Note that the object type returned by the method will depend on the attribute:

  • Boolean attributes will return a TrueClass or FalseClass object.
  • Numeric attributes will return a Fixnum object.
  • The rest of the attributes will return a String object.

Custom attributes

Attributes that are not defined in the HTML specification will not have an associated method defined. In these cases, the attribute_value method is required.

Given an element with a custom attribute:

<div myCustomAttribute="custom" id="div_id">text</div>

The value of the custom attribute can be obtained by passing the name of the attribute to the attribute_value method:

browser.div.attribute_value('myCustomAttribute')
#=> "custom"

The method can also be useful when dynamically retrieving attribute values. For example, the following collects the attribute values specified in an array.

attrs = ['id', 'myCustomAttribute']
values = attrs.map { |attr| browser.div.attribute_value(attr) }
#=> ["div_id", "custom"]

4.3 Computed style

These days elements are rarely styled with just inline styles. For example, a, inline style can be applied to an error message to make the text red:

<div style="color:red;">Error</div>

Styles are now often moved into classes so that they can be consistently applied across different elements and pages. For example, using a class, the element might become:

<div class="error_message">Error</div>

To check that the error message text is red, it is not sufficient to just check the style attribute. Instead, you need to check the computed style - ie the actual style applied by the browser. This can be achieved by using the Element#style method and specifying a style property:

browser.div.style('color')
#=> "rgba(255, 0, 0, 1)"

Note:

  • The shorthand CSS properties (e.g. background, font, etc.) are not supported. The longhand properties should be used instead - eg background-color.
  • Depending on the browser, the formatting of the property may vary.

4.4 Existence and visibility

Watir has three methods to check whether an elements exists on the page:

  • exists? - Returns true if the element exists (in the DOM).
  • visible? - Returns true if the element is visible to the user.
  • present? - Returns true if the element exists and is visible to the user.

These methods are called similar to:

browser.div.exists?
browser.div.visible?
browser.div.present?

Consider the following page, which has one div tag that is visible (ie displayed to the user) and one that is not.

<body>
  <div id="1" style="block:display;">This text is visible</div>
  <div id="2" style="block:none;">This text is not visible</div>
</body>

When each method (exists?, present?, visible?) is run for the different elements (displayed, not displayed, non-existent), the following results are seen:

Element .exists? .visible? .present?
Displayed (browser.div(:id ⇒ ‘1’)) true true true
Not Displayed (browser.div(:id ⇒ ‘2’)) true false false
Non-Existent (browser.div(:id ⇒ ‘3’)) false exception false

Element#exists? checks if the element is in the DOM (or HTML). Element#visible? checks if the element can be seen by the user, but throws an exception if the element is not in the DOM. Element#present? is the same as Element#visible? except that it returns false, instead of an exception, when the element is not in the DOM.

While the three methods are definitely different, I think, at least from my experience, that you should typically be using Element#present?.

One point to keep in mind, especially for those working with legacy watir libraries/tests, is that .exists? can lead to false positives. Imagine you have a form. When the form is submitted, an error message is displayed for fields that fail validation (ex missing a required field). In some applications this will be implemented by simply changing the style of the element containing the error message from display:none to display:block. Now consider writing a test to check that the validation message is displayed. If you use .exists?, the test will have false positives (ie it will always pass since the element is always in the DOM). In this situation, .visible? or present? should have been used.