Skip to content
bmulholland edited this page Nov 14, 2012 · 2 revisions

Indexed properties allow you to easily define sets of related elements defined by an index. This is most common in cases where there is a table or other defined structure where each "row" contains a set of elements related to a particular object. In the classic case of a shopping cart, you might have a row for each item in the cart with elements identifying the name of the item, a text field to edit the quantity, a button to remove the item from the cart and so forth. Each of these elements exists once for each item in the shopping cart, which means you may have an arbitrary number of elements on the page with which to interact.

An indexed property can be used to define a single property on your page object that represents the combination of the sets of related elements. An index is used to select a particular "row", which returns an object that can be used to interact with the elements on that "row" like any other page object. This requires the elements in a "row" to be identified consistently (e.g. always by name or id) with a pattern that allows for a single substitution to identify a particular element on the page.

Note: In spite of the "table" terminology above, this feature in no way depends on the existence of any HTML table tags. It only requires that the identifiers have a common substitution pattern.

Syntax

You declare an indexed property using the following method call:

indexed_property(:name_of_property, [
  [:element_method, :name_of_element, {:id => 'an_indexed[%s].id'}],
    ... repeated as many times as you want ...
])

The array can contain as many sub-arrays as needed. Each sub-array should contain precisely three entries: a symbol indicating which normal element method should be used and two more parameters representing the normal arguments of the normal element methods. The values in the hash should contain a %s substitution target, which will be used when the indexed property is accessed. Nested indexed_properties are not supported at this time.

One method is generated by this call:

name_of_property

This method returns an object that responds to the [] method. The index passed to the [] method is used as the value to substitute in each hash, and the [] method returns an object that will respond as if all the element methods listed in the property definition were defined with the substituted strings.

Example

As an example, consider the following HTML fragment

<table class="table_class">
    <tr>
        <td><span id="cart[0].title">Item 1</span></td>
        <td><input type="text" id="cart[0].quantity">3</input></td>
        <td><input type="button" id="cart[0].delete" value="Delete" onclick="..."/></td>
    </tr>
    <tr>
        <td><span id="cart[1].title">Item 2</span></td>
        <td><input type="text" id="cart[1].quantity">1</input></td>
        <td><input type="button" id="cart[1].delete" value="Delete" onclick="..."/></td>
    </tr>
    <tr>
        <td><span id="cart[2].title">Item 3</span></td>
        <td><input type="text" id="cart[2].quantity">2</input></td>
        <td><input type="button" id="cart[2].delete" value="Delete" onclick="..."/></td>
    </tr>
</table>

and the following page object:

class ShoppingCartPage
  include PageObject

  indexed_property(:cart_contents, [
    [:span, :title, {:id => 'cart[%s].title'}],
    [:text_field, :quantity, {:id => 'cart[%s].quantity'}],
    [:button, :delete, {:id => 'cart[%s].delete'}]
  ])
end

The page object will have a method named cart_contents that can be used with an index to generate an object that will contain the accessor methods for all of the elements matching a given index. Specifically, after executing the following code:

my_page = ShoppingCartPage.new(@browser)
my_item = my_page.cart_contents[0]

my_item will reference an object that has defined the following methods

title
title_element
quantity
quantity=
quantity_element
delete
delete_element

each of which interacts with the correct elements in the first row of the table.

A test could then easily do something like the following:

expected_titles = ['Item 1', 'Item 2', 'Item 3']
on_page ShoppingCartPage do |page|
  (0..2).each do |i|
    page.cart_contents[i].title.should eql(expected_titles[i])
  end
  page.cart_contents[0].quantity = 7
  page.cart_contents[2].delete
end