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

Locate elements whose id attribute matches targetValue.

name=targetValue

Locate elements whose name attribute matches targetValue.

css=cssSelectorSyntax

Locate elements using CSS selector syntax.

partial_link_text=textFragment

Locate A elements whose text includes textFragment.

class=className and class_name=className

Locate elements whose class attribute matches className.

link=exactText and link_text=exactText

Locate A elements whose text is exactly exactText.

tag_name=targetValue

Locate elements whose tag name matches targetValue.

xpath=xpathExpression

Locate elements using XPATH expressions.

Selector Methods

↑ top

Public Instance Methods

attr(arg, &block) click to toggle source

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
frame(arg, &block) click to toggle source

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
json(arg) { |element| ... } click to toggle source

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
sid(arg, &block) click to toggle source

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
smart(arg) { |target| ... } click to toggle source

Select an element by applying different Selector strategies.

This selector tries to find elements by using the following strategies:

  1. default (id suffix)

  2. by name

  3. by class_name

  4. by id fragment (the id contains the text specified)

  5. by placeholder attribute

  6. by text content (unless the element is a label)

  7. by radio/checkbox/button/submit value

  8. by referenced element (find a label by its text, then find the element pointed by the label's for attribute)

  9. 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
window(arg, &block) click to toggle source

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