-
Notifications
You must be signed in to change notification settings - Fork 8
Widgets
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
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.
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.
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.
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
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