Skip to content
This repository has been archived by the owner on Feb 13, 2020. It is now read-only.

Widgets

MattiSG edited this page May 14, 2012 · 8 revisions

Tests are defined in terms of actions and properties on widgets. A widget is a piece of functionality present on one more more pages of your application.

A single page can have many widgets, and multiple copies of a widget may appear on the same page, provided that there is some way to identify each instance independently.

Widgets go in MY_APP/test/right/widgets/, one per source file. A widget defines its elements in terms of standard Selenium 2 selectors and actions in terms of those elements. For example,

class LoginWidget < Test::Right::Widget
  field :username, :id => 'username'
  button :login, :xpath => "//input[@type='submit']"

  action :login do |username, password|
    fill_in :username, username
    click :login
  end
end

Actions

Tightly coupling your tests to specific selectors is a recipe for pain. Widgets provide a level of abstraction so that you can write your tests in terms of actions. You can change the behavior of actions and the selectors they use to find elements while preserving the name, allowing all tests that depend on them to remain the same.

If a widget has a validated_by_presence_of element specified, then the widget's presence will be checked before the action is executed.

Properties

Actions are how tests use widgets to effect changes on pages. Properties are how tests can query widgets about the state of the browser. A property is defined on a widget using the @property@ command:

class CartWidget < Test::Right::Widget
  element :item_count, :id => 'item_count'

  property :number_of_items do
    get_element(:item_count).text
  end
end

Once a property is defined on a widget, tests can make assertions about that property. For example:

class CartFeature < Test::Right::Feature
  def test_adding_item
    with ItemWidget["shoes"] do |w|
      w.add_to_cart
    end

    with CartWidget do |w|
      assert_equal 1, w.number_of_items
    end
  end
end

Assertions on properties automatically retry, so you don't have to worry about asynchronous behavior or server round-trips breaking your tests.

Roots

To make finding sub-elements inside of a widget simpler, you can specify a root selector for a widget that all sub-element queries will start at. For example,

class MyWidget < Test::Right::Widget
  rooted_at :class => 'mywidget'
  element :foo, :xpath => '/div[1]/span/a'
end
<html>
  <body>
    <div class="mywidget">
       <div>
         <span>
            <a href="http://www.google.com">I am foo!</a>
         </span>
       </div>
    </div>
  </body>
</html>

Performing an operation on the foo element will automatically search within the 'mywidget' div.

Presence

If you specify a validated_by_presence_of value on a widget, Test::Right will automatically ensure that element is present before performing operations on a widget. It can also be used to wait for widgets to be present.

class MyWidget < Test::Right::Widget
  rooted_at :class => 'mywidget'
  element :foo, :xpath => '/div[1]/span/a'

  validated_by_presence_of :foo
end
class MyFeature < Test::Right::Feature
  def test_foo
    wait_for MyWidget
  end
end

Plural Widgets

If you have a widget that appears multiple times on a page, for example rows in a table, you can configure your widget class to be named_by a particular child of the widget, and then use that name to find specific instances of the widget on a page.

class MyWidget < Test::Right::Widget
  rooted_at :class => 'mywidget'
  element :foo, :xpath => '/div[1]/span/a'

  named_by :foo

  validated_by_presence_of :foo
end
class MyFeature < Test::Right::Feature
  def test_foo
    with MyWidget["a name"] do |w|
      w.do_something
    end
  end
end
Clone this wiki locally