Pure dynamic Watir-based page object DSL.
Inspired by page-object and watir-page-helper.
Just like any other gem:
➜ gem install watirsome
Or using bundler:
# Gemfile
gem 'watirsome'
class LoginPage
include Watirsome
text_field :username, label: 'Username'
text_field :password, label: 'Password'
button :submit_login, text: 'Login'
def login(username, password)
self.username = username
self.password = password
submit_login
end
end
browser = Watir::Browser.new
page = LoginPage.new(browser)
page.login('demo', 'demo')
Watirsome provides you with accessors DSL to isolate elements from your methods.
All accessors are just proxied to Watir, thus you free to use all its power in your page objects.
class Page
include Watirsome
# any method defined in Watir::Container are accessible
body :body
section :section, id: 'section_one'
element :svg, tag_name: 'svg'
end
You can use any kind of locators you use with Watir.
class Page
include Watirsome
body :body
section :one, id: 'section_one'
element :svg, tag_name: 'svg'
button :login, class: 'submit', index: 1
end
page = Page.new(@browser)
page.body_body # equals to @browser.body
page.one_section # equals to @browser.section(id: 'section_one')
page.svg_element # equals to @browser.element(tag_name: 'svg')
page.login_button # equals to @browser.button(class: 'submit', index: 1)
Watirsome also provides you with opportunity to locate elements by using any boolean method Watir element (and subelements) supports.
class Page
include Watirsome
div :layer, class: 'layer', visible: true
span :wrapper, exists: false
select_list :country, selected: 'Please select country...'
end
page = Page.new(@browser)
page.layer_div # equals to @browser.divs(class: 'layer').find { |e| e.visible? == true }
page.wrapper_span # equals to @browser.spans.find { |e| e.exists? == false }
page.country_select_list # equals to @browser.select_lists.find { |e| e.selected?('Please select country...') }
You can also use proc/lambda/block to locate element. Block is executed in the context of initialized page, so other accessors can be used.
class Page
include Watirsome
div :layer, class: 'layer'
span :wrapper, -> { layer_div.span(class: 'span') }
end
page = Page.new(@browser)
page.wrapper_span # equals to @browser.div(class: 'layer').span(class: 'span')
Moreover, you can pass arguments to blocks!
class Page
include Watirsome
div :layer, class: 'layer'
a :link do |text|
layer_div.a(text: text)
end
end
page = Page.new(@browser)
page.link_a('Login') # equals to @browser.div(class: 'layer').a(text: 'Login')
For each element, accessor method is defined which returns instance of Watir::Element
(or subtype when applicable).
Element accessor method name is #{element_name}_#{tag_name}
.
class Page
include Watirsome
section :section_one, id: 'section_one'
element :svg, tag_name: 'svg'
end
page = Page.new(@browser)
page.section_one_section #=> #<Watir::HTMLElement:0x201b2f994f32c922 selector={:tag_name=>"section"}>
page.svg_element #=> #<Watir::HTMLElement:0x15288276ab771162 selector={:tag_name=>"svg"}>
For each readable element, accessor method is defined which returns text of that element.
Read accessor method name is element_name
.
Default readable methods are: [:div, :span, :p, :h1, :h2, :h3, :h4, :h5, :h6, :select_list, :text_field, :textarea, :checkbox, :radio]
.
You can make other elements readable by adding tag names to Watirsome.readable
.
# make section readable
Watirsome.readable << :section
class Page
include Watirsome
div :main, id: 'main_div'
section :date, id: 'date'
end
page = Page.new(@browser)
page.main # returns text of main div
page.date # returns text of date section
There is a bit of logic behind text retrieval:
- If element is a text field or textarea, return value
- If element is a select list, return text of first selected option
- Otherwise, return text
For each clickable element, accessor method is defined which performs click on that element.
Click accessor method name is element_name
.
Default clickable methods are: [:a, :link, :button]
.
You can make other elements clickable by adding tag names to Watirsome.clickable
.
# make input clickable
Watirsome.clickable << :input
class Page
include Watirsome
a :login, text: 'Login'
input :submit, ->(type) { @browser.input(type: type) }
end
page = Page.new(@browser)
page.login # clicks on link
page.submit('submit') # clicks on submit input
For each settable element, accessor method is defined which sets value to that element.
Click accessor method name is #{element_name}=
.
Default settable methods are: [:text_field, :file_field, :textarea, :checkbox, :select_list]
.
You can make other elements settable by adding tag names to Watirsome.settable
.
# make input settable
Watirsome.settable << :input
class Page
include Watirsome
text_field :username, label: 'Username'
input :date, type: 'date'
select_list :country, label: 'Country'
end
page = Page.new(@browser)
page.username = 'Username' # sets value of username text field
page.date = '2013-01-01', :return # sends text to element and hits "Enter"
page.country = 'Russia' # selects option with "Russia" text
If found element responds to #set
, accessor calls it. Otherwise, #send_keys
is used.
Watirsome provides you with initializers DSL to dynamically modify your pages/regions behavior.
Each page may define #initialize_page
method which will be used as page constructor.
class Page
include Watirsome
def initialize_page
puts 'Initialized!'
end
end
Page.new(@browser)
#=> 'Initialized!'
Each region you include/extend may define #initialize_region
method which will be called after page constructor.
module HeaderRegion
def initialize_region
puts 'Initialzed header!'
end
end
module FooterRegion
def initialize_region
puts 'Initialzed footer!'
end
end
class Page
include Watirsome
include HeaderRegion
def initialize_page
extend FooterRegion
end
end
Page.new(@browser)
#=> 'Initialized header!'
#=> 'Initialized footer!'
Regions are being cached, so, once initialized, they won't be executed if you call Page#initialize_regions
again.
- Currently tested to work only with
watir-webdriver
. Let me know if it works usingwatir-classic
. - You cannot use
Watir::Browser#select
method as it's overriden byKernel#select
. UseWatir::Browser#select_list
instead. - You cannot use block arguments to locate elements for settable/selectable accessors (it makes no sense). However, you can use block arguments for all other accessors.
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with rakefile, version, or history.
- Send me a pull request. Bonus points for topic branches.
Copyright (c) 2016 Alex Rodionov. See LICENSE.md for details.