Lapis Lazuli:Selecting an element
Contents
This page is under construction
Lapis Lazuli is an addition to Watir. This page explains how to select an element using Lapis Lazuli. Then, once you have the element selected, you can start interacting with the element. See Interacting with an element
Finding the element
There are 8 ways to find an element:
browser.find
finds a single element matching the specifications passed to it.browser.wait
does the same as find, but then it waits until it's successful to find the matching specifications.browser.find_all
finds all elements matching the specifications passed to it.browser.wait_all
waits until it finds at least one matching element, but will return all of them if more were found.browser.multi_find
finds a single element matching one of multiple specifications passed to it. Specifications are handled in order and only the first match is returned.browser.multi_wait
does the same as multi_find, but then waits until it's successful to find an element.browser.multi_find_all
finds all elements matching one of multiple specifications passed to it. Specifications are handled in order and only elements belonging to the first match are returned.browser.multi_wait_all
does the same as multi_find_all, but again, it waits for at least 1 result to match the specifications.
Element selection examples
Find and wait
browser.goto 'http://training-page.testautomation.info/' # Selecting the username input field using :objects elm1 = browser.find(:input => {:id => 'login-username'}) # Does the same thing, also 'strings' are allowed elm2 = browser.find('input' => {'id' => 'login-username'}) # Selecting it using a /regular expression/ (see http://rubular.com/ ) elm3 = browser.find(:input => {:id => /gin-usern/}) # Doing the same thing, but now we wait until it's present. So if it takes 5 seconds for it to show, elm1, 2 and 3 would give an error, but this one will succeed. elm4 = browser.wait(:input => {:id => 'login-username'}) # Setting the timeout to 3 seconds, so it would throw an error after 3 seconds. elm5 = browser.wait(:input => {:id => 'login-username'}, :timeout => 3) # Selecting an element by providing 2 attributes elm6 = browser.find(:input => {:id => 'login-username', :class => /form-control/})
Like selector
Like selectors are shorthands for more complex XPath-based selection. A like selector is either of:
- A symbol, e.g.
:a
, in which case an XPath query is constructed for findinga
elements. - An array of three elements, in which case an XPath query is constructed with the following components:
- The first is interpreted as the element name (as above),
- The second is interpreted as an attribute to match, and
- The third is interpreted as an attribute value to match.
browser.goto 'http://training-page.testautomation.info/' # Full notation elm1 = browser.find(:like => [ :element => :input, :attribute => :id, :include => 'login-username' ]) # Short notation elm2 = browser.find(:like => [:input, :id, 'login-username']) # The 3rd value is "include", so you don't need to use the full ID, just a part of the text will work too. elm3 = browser.find(:like => [:input, :id, 'gin-usern']) # Will select the first :input field it finds present on the page elm4 = browser.find(:like => :input) # Also works fine with wait, or any other find alternative. elm5 = browser.wait(:like => [:input, :id, 'login-username'])
Find all
browser.goto 'http://training-page.testautomation.info/' # Find all input elements that are present on this page elm1 = browser.find_all(:like => :input) # Check how many elements were found elm1.length => 2 # Select the first element [0] (zero is the first in arrays), and show the html elm1[0].html => <input id="login-username" class="form-control ng-pristine ng-untouched ng-valid" ng-model="Users.login_data.name" size="60" placeholder="Username" type="text"> # Find all input elements that exist on the page (so also the ones in the background) elm2 = browser.find_all(:like => :input, :filter_by => :exists?) elm2.length => 9
Multi find
Multi find supports the use of multiple selectors and tries to find a match with any of these.
browser.goto 'http://training-page.testautomation.info/' # Find both the username and password input field elms = browser.multi_find_all( :selectors => [ {:like => [:input, :id, 'login-username']}, {:like => [:input, :id, 'login-password']} ] ) elms.length => 2 elms[0].html => <input id="login-username" class="form-control ng-pristine ng-untouched ng-valid" ng-model="Users.login_data.name" size="60" placeholder="Username" type="text"> elms[1].html => <input ng-model="Users.login_data.password" class="form-control ng-pristine ng-untouched ng-valid" id="login-password" placeholder="Password" type="password"> # Find all input elements AND <a> elements that exist on the page elms = browser.multi_find_all( :selectors => [ { :like => :input, :filter_by => :exists? }, { :like => :a, :filter_by => :exists? } ] ) elms.length => 17 # This will print out the ID of all the elements you found. elms.each do |e| puts e.id end
Selection options
- Selector options, these options are part of the selector.
:throw
-true
orfalse
weather an error should be thrown if the element can't be found.:context
- give a found element to search within.:filter_by
- the expected value is a symbol that the element responds to, e.g.:present?
. If provided, only present elements (in the example) will be matched.
- General options, these options are for the whole find function
:message
- when failing, raise a custom message.:timout
- Browser.wait option only, time to wait to find your selector.:selectors
- a list of selectors by which to find elements. Implicit when no other options are provided (see above).:pick
- one of the possible first parameter values to thepick_one
function. Note that forfind
and:multi_find
, the default is to pick the first element found.:mode
- one ofmatch_one
ormatch_all
- determines whether the multi find functions return after finding a single element matching any _one_ of the provided selectors, or only when _all_ selectors given match at least one element.
For the default values of all the options, see Lapis Lazuli:Default values for find / multi find / find all / multi find all and wait options
Selector specific v.s. general options
In most cases, you will use both type of options in a list without separating them. But it's important to understand the difference, in case you're going to use multi_find. In multi_find you use multiple selectors, and so the notation is different.
Example of an invalid selection
browser.multi_find( :selectors => [ {:like => :div}, {:like => :input} ], :filter_by => :exists? # <-- This is the wrong place to put this, because now it's not part of the selector. )
A correct example, where 1 selector is filtered on :exists?
and another searched within a :context
.
browser.multi_find( :selectors => [ {:like => :div, :filter_by => :exists?}, {:like => :input, :context => form} ] )
:throw
:throw
decides weather an error should be thrown when it fails to find your specifications.
browser.goto 'http://training-page.testautomation.info/' # An error will be thrown (default is :throw => true) browser.find(:like => [:div, :class, 'doesnotexist']) # No error will be thrown and elm1 will result in being nil elm1 = browser.find(:like => [:div, :class, 'doesnotexist'], :throw => false) if elm1 == nil # Do something else if the element did not exist end
:context
:context
is an element you've already found, to look inside for something else.
browser.goto 'http://training-page.testautomation.info/' # First wait for the login form container form = browser.wait(:like => [:form, :id, 'form-login']) # Then select the input fields inside the form inputs = browser.find_all( :like => :input, :context => form, :filter_by => :exists? ) inputs.length => 2 # To prove the difference without using a context inputs = browser.find_all( :like => :input, :filter_by => :exists? ) inputs.length => 9
Note that when a find/wait function become too long (2 or more options), we're putting it on multiple lines. This is to keep the code look clean and easy to read. Also a best practise to do yourself. </blockequote>
:filter_by
Possibility between
:present?
and:exists?
browser.goto 'http://training-page.testautomation.info/' # Then select the input fields inside the form inputs = browser.find_all( :like => :input, :context => form, :filter_by => :exists? ) inputs.length => 2 # To prove the difference without using a context inputs = browser.find_all( :like => :input, :filter_by => :exists? ) inputs.length => 9In addition, a helper function is exposed that allows you to pick an element from a collection (array) of elements.
Picking an Element
The
pick_one
helper function is easily explained in isolation, but it should be noted that it also underlies thefind
andmulti_find
functions:browser.pick_one(:first, collection) # returns the first item from the collection browser.pick_one(:last, collection) # returns the last item from the collection browser.pick_one(:random, collection) # returns a random item from the collection browser.pick_one(12, collection) # returns the 12th item from the collectionYou should not usually need to use this function directly, but instead use one of the find functions.
Find Function Syntax
All find functions accept much the same parameters; the general syntax is this:
browser.find(selector1) browser.find(:some_option => value, :selectors => [selector1]) # browser.find_all same as browser.find browser.multi_find(selector1, selector2, ...) browser.multi_find(:some_option => value, :selectors => [selector1, selector2, ...]) # browser.multi_find_all same as browser.multi_findThis syntax affords the flexibility of providing options to the functions, without forcing you to provide any.
Find Options
The following options are interpreted by all find functions:
:selectors
- a list of selectors by which to find elements. Implicit when no other options are provided (see above).:pick
- one of the possible first parameter values to thepick_one
function. Note that forfind
and:multi_find
, the default is to pick the first element found.:mode
- one ofmatch_one
ormatch_all
- determines whether the multi find functions return after finding a single element matching any _one_ of the provided selectors, or only when _all_ selectors given match at least one element.:filter_by
- the expected value is a symbol that the element responds to, e.g.:present?
. If provided, only present elements (in the example) will be matched.:context
- give a found element to search within.:message
- when failing, raise a custom message.Selectors
Selectors can be the kind of selectors that would be passed to the Watir browser's functions, but LapisLazuli affors far greater flexibility here.
- If a selector is a string, e.g.
"a"
, then elements with the symbol name are found, using Watir's built-in functions.- If a selector is a symbol, e.g.
:a
, then elements with the symbol name are found, but using XPath instead.- If a selector is a hash, it is considered to be a regular Watir selector, e.g.
:id => /some-id/
.
- If this hash contains a
:like
key, the value of this key is further interpreted before passing the entire hash on to Watir (see below).- If this hash contains a
:context
key, the value of this key is expected to be a Watir element, and a search will be performed relative to this element. If the context is not provided, the search starts at the document root.Like Selectors
Like selectors are shorthands for more complex XPath-based selection. A like selector is either of:
- A symbol, e.g.
:a
, in which case an XPath query is constructed for findinga
elements.- An array of three elements, in which case an XPath query is constructed with the following components:
- The first is interpreted as the element name (as above),
- The second is interpreted as an attribute to match, and
- The third is interpreted as an attribute value to match.
Note that like selectors **add** to a regular Watir selector. It's perfectly legitimate to use regular selectors and like selectors in combination, but the result may be unexpected.
Examples
# Find any link. same as browser.a browser.find(:a) browser.find({:like => :a}) # xpath # same as browser.a(:href => /test/) browser.find(:a => {:href => /test/}) # More complicated version: browser.find( {:a => {:class => "loginButton"}}, {:a => {:href => /login/}}, {:button => {:name => "login"}} ) # Find element where an attribute contains something. # Uses XPath instead of the slower regexes: # browser.a(:href => /account\/login/) browser.find(:like => { :element => :a, :attribute => :href, :include => "account/login" }) # A shorthand browser.find(:like => [:a, :href, "account/login"]) # Finding a single class is also possible if you add spaces around it browser.find(:like => [:a, :class, " login "]) # And also support for XPath text browser.find(:like => { :element => :a, :attribute => :text, :include => "Login" }) # Finding based on name, id or text browser.find("login") # Finding an element within another element form = browser.find("register_form") firstname_field = browser.find( :input => {:name => 'firstname'}, :context => form )