class Bauxite::Selector
Selector class.
Selectors represent different strategies for finding elements. Selenium provides a list of standard selectors (e.g. by id, by css expression, etc).
Additional selectors can be specified by defining custom methods in the Selector class.
Each custom selector is defined in a separate file in the
'selectors/' directory. The name of the file must match the name of
the selector. These files should avoid adding public methods other than the
selector method itself. Also, no attr_accessors
should be
added.
Selector methods can use the ctx
attribute to refer to the current test Context. The protected method selenium_find can
also be used to locate elements using standard Selenium selectors.
Selector methods should always take a block and forward that block to a call to either find or selenium_find.
For example (new selector template):
# === selectors/data.rb ======= # class Selector # :category: Selector Methods def data(arg, &block) # selector code goes here, for example: selenium_find(:css, "[data='#{arg}']", &block) end end # === end selectors/data.rb === # Context::selectors.include? 'data' # => true
To avoid name clashing with Ruby reserved words, the '_selector' suffix can be included in the selector method name (this suffix will not be considered part of the selector name).
For example (_selector suffix):
# === selectors/end.rb ======= # class Selector # :category: Selector Methods def end_selector # do something end end # === end selector/end.rb === # Context::selectors.include? 'end' # => true
Standard Selenium Selectors¶ ↑
- id=
targetValue
- name=
targetValue
- css=
cssSelectorSyntax
- partial_link_text=
textFragment
- class=
className
and class_name=className
- link=
exactText
and link_text=exactText
- tag_name=
targetValue
- xpath=
xpathExpression
Selector Methods
↑ topPublic Instance Methods
Select an element by attribute value.
The attribute selector syntax is:
attr=name:value
For example:
# assuming <div custom="true">foo</div> assert attr=custom:true "foo" # => matches the element above.
# File lib/bauxite/selectors/attr.rb, line 35 def attr(arg, &block) data = arg.split(':', 2) selenium_find(:css, "[#{data[0]}='#{data[1]}']", &block) end
Change the selector scope to the given frame and finds an element in that frame.
This is a composite selector. The frame selector syntax is:
frame=|frame_selector|child_selector
Where frame_selector
is any selector available that matches
the target frame, child_selector
is any selector available
that matches the target element inside the target frame.
Note that the '|' character can be replaced with any single-character delimiter.
Also note that frame selectors can be embedded to select a frame inside a
frame. To accompilish this, child_selector
can be a frame
selector.
For example:
# assuming <iframe class="myframe"> # <div id="child">foo</div> # </iframe> assert "frame=|css=.myframe|child" "foo" # => matches the 'child' element above.
# File lib/bauxite/selectors/frame.rb, line 48 def frame(arg, &block) delimiter = arg[0] items = arg[1..-1].split(delimiter, 2) find(items[0]) do |f| begin @ctx.driver.switch_to.frame f find(items[1], &block) ensure @ctx.driver.switch_to.default_content end end end
Select a field in a JSON document.
The JSON selector syntax is:
# For objects: json=key # {"key":1} => 1 json=key.subkey # {"key":{"subkey":2}} => 2 json=key[0] # {"key": [3]} => 3 json=key[1].subkey # {"key": [{"subkey":4}]} => 4 json=key.length # {"key": [5]} => 1 # For arrays: json=[0] # [1] => 1 json=[1].value # [{"value": 2}] => 2 json=length # [3] => 1
For example:
# assuming {"key": [{"subkey":4},{"val":"42"}]} assert json=key[0].subkey 4 assert json=key[1].val 42 assert json=key.length 2 # => these assertions would pass
# File lib/bauxite/selectors/json.rb, line 49 def json(arg, &block) source = JSON::parse(@ctx.driver.page_source.sub(/^<html[^>]*>.*<pre>/, '').sub(/<\/pre>.*<\/html>$/, '')) element = _json_find(source, arg) raise Selenium::WebDriver::Error::NoSuchElementError, "Cannot find JSON element: #{arg}" unless element element = element.to_s def element.text; self; end def element.tag_name; 'json'; end yield element if block_given? element end
Select an element by id suffix.
This is the default selector. Any selector strings that do not contain an equal sign (i.e. '=') will use this selector.
For example:
# assuming <div id="strange_uuid_like_stuff_myDiv">foo</div> assert sid=myDiv "foo" # => matches the element above.
# File lib/bauxite/selectors/sid.rb, line 35 def sid(arg, &block) selenium_find(:css, "[id$='#{arg.gsub("'", "\\'")}']", &block) end
Select an element by applying different Selector strategies.
This selector tries to find elements by using the following strategies:
-
default (id suffix)
-
by
name
-
by
class_name
-
by
id
fragment (theid
contains the text specified) -
by
placeholder
attribute -
by text content (unless the element is a label)
-
by radio/checkbox/button/submit value
-
by referenced element (find a label by its text, then find the element pointed by the label's
for
attribute) -
by child element (find a label by its text, then find the first element child control element, including input, select, textarea and button).
For example:
# assuming <label>By label parent<input type="text" value="By label parent"/></label> assert "smart=By label parent" "By label parent" # => the assertion succeeds.
# File lib/bauxite/selectors/smart.rb, line 46 def smart(arg, &block) b = lambda { |e| e } target = _smart_try_find { sid(arg, &b) } target ||= _smart_try_find { selenium_find(:name, arg) } target ||= _smart_try_find { selenium_find(:class_name, arg) } target ||= _smart_try_find { attr("id*:"+arg, &b) } target ||= _smart_try_find { attr("placeholder:"+arg, &b) } quoted_arg = "'"+arg.gsub("'", "\\'")+"'" target ||= _smart_try_find do selenium_find(:css, ["input[type='checkbox'][value=#{quoted_arg}]", "input[type='radio'][value=#{quoted_arg}]", "input[type='button'][value=#{quoted_arg}]", "input[type='submit'][value=#{quoted_arg}]" ].join(',') ) end return yield target if target target = selenium_find(:xpath, "//*[contains(text(), '#{arg.gsub("'", "\\'")}')]") return yield target unless target.tag_name.downcase == 'label' label = target id = label['for'] return yield selenium_find(:id, id) if id target = _smart_try_find { label.find_element(:css, "input, select, textarea, button") } return yield target if target end
Change the selector scope to the given window and finds an element in that window.
This is a composite selector. The window selector syntax is:
window=|window_name|child_selector
Where window_name
is the name or url pattern of the target
window, child_selector
is any selector available that matches
the target element inside the target window.
Note that the '|' character can be replaced with any single-character delimiter.
For example:
# assuming <div id='label'>hello!</div> in popup.html js "window.w = window.open('popup.html', 'mypopup')" assert "window=|mypopup|label" "hello!" # => this assertion would pass assert "window=|popup.html|label" "hello!" # => this assertion would pass
# File lib/bauxite/selectors/window.rb, line 47 def window(arg, &block) current = @ctx.driver.window_handle delimiter = arg[0] name,child = arg[1..-1].split(delimiter, 2) pattern = /#{name}/ if name =~ /^\/.*\/[imxo]*$/ pattern = eval(name) end match = @ctx.driver.window_handles.find do |h| @ctx.driver.switch_to.window h @ctx.driver.current_url =~ pattern end name = match if match begin @ctx.driver.switch_to.window name rescue StandardError => e @ctx.driver.switch_to.window current raise Bauxite::Errors::AssertionError, "Cannot find a window matching '#{name}' (either by name exact match or by url regex)." + e.message end begin find(child, &block) ensure @ctx.driver.switch_to.window current end end