Skip to content
Jari Bakken edited this page Nov 13, 2010 · 6 revisions

Example script explained

# You have to include/import the Celerity library
require "rubygems"
require "celerity"

# Create a Browser object. Due to Watir compatibility Celerity::IE.new would also work.
# You can specify options, see HTTP://celerity.rubyforge.org/doc/classes/Celerity/IE.html#M000141
browser = Celerity::Browser.new

# Tell the browser object to go to HTTP://www.google.com
browser.goto('HTTP://www.google.com')

# Tell the browser object to find a text field with the name "q" and set the value to "Celerity"
browser.text_field(:name, 'q').value = 'Celerity'

# Tell the browser object to find a button with the name "btnG" and click it
browser.button(:name, 'btnG').click

# Output "yay" if the page we are on includes the text "celerity.rubyforge.org"
puts "yay" if browser.text.include? 'celerity.rubyforge.org'

Setting up the browser

Celerity is all about inspecting and manipulating HTML elements. However, before we can start with the fun stuff, we have to include/import Celerity and create a Browser object.

# You have to include/import the Celerity library
require "rubygems"
require "celerity"

# Create a Browser object
browser = Celerity::Browser.new

# Browse to HTTP://www.google.com
browser.goto('HTTP://www.google.com')

Only four lines of code, and we’re all set! Now, let’s talk about how we can find an element we want to inspect or manipulate.

Finding an element

The general syntax for finding an element is:

browser_object.element_method(how_parameter, what_parameter)

ie. browser.link(:text, "Next") which means the first <a> element with the text “Next”.

Element method

There’s an element method for each supported element or group of elements, i.e. link, div, table, form, text_field, select_list or button. For a full list of supported elements, see List of supported elements.

How Parameter

Note that the how parameter is always a symbol, meaning it has a : in front of it ;)

Search by HTML attributes: :id, :name, :value, :colspan, etc.

The how parameter is generally the HTML attribute you want to search by. To find a <form> element with the id new_user_, you would do something like @browser.form(:id, "newuser"). To find the first frame with _name="main"_ you can do @browser.frame(:name, "main"). To find the first table cell with a column span of 3 (<td colspan="3"></td>), you’d do browser.cell(:colspan, "3").

In addition to search by attributes, Celerity has a few special how parameters.

:text

The :text parameters searches the “inside” of the element. It ignores HTML markup, but includes all text.

<div id="ex1">
  <a id="yo1" href="HTTP://www.google.com">Google</a>
</div>
<div id="ex2">
  <a id="yo2" href="HTTP://www.google.com">Google<img src="google.png" /></a>
</div>
<div id="ex3">
  <a id="yo3" href="HTTP://www.google.com">Google <span>The best search engine</span></a>
</div>
browser.div(:id, "ex1").link(:text, "Google").id # Would return "yo1"
browser.div(:id, "ex2").link(:text, "Google").id # Would return "yo2", the <img> is ignored

# Would not work, because even though the <span> and </span> tags are ignored, the text inside of them is not.
# You can verify this by doing browser.link(:id, "yo3").text which returns "Google The best search engine"
browser.div(:id, "ex3").link(:text, "Google").id

:index

The :index how parameter is very simple and extremely useful. It just allows you to get the nth element. browser.li(:index, 4) means the 4th <li> element on the page.

:xpath

The :xpath how parameter let’s you use XPath (XML Path Language) to find an element.

browser.option(:xpath, "//option[@id='nor']")

The simple (but not very useful) example above would select the first <option> element with the id nor.

What Parameter

The what parameter is what you want to find, and for almost all how parameters it can be both a string or a regular expression, e.g. browser.p(:id, "lead") which means the first <p> element with id lead, or browser.link(:text, /ish/) which means the first <a> element that matches the regular expression /ish/

The exceptions:

  • :xpath must be a string
  • :index must be an integer

Finding an element inside another element

You can nest the element methods to find element(s) inside another element.

<body>
  <div>
    <div id="content">
      <p class="lead">Lead in.</p>
    </div>
  </div>
</body>
browser.div(:index, 1).div(:id, "content").p(:class, "lead").text

The example above would return “Lead in.”.

Finding an element by multiple criteria

Celerity also supports searching by multiple criteria. Just use a hash:

criteria = {:class => "promo", :index => 4}
browser.div(criteria)

# or
browser.div({:class => "promo", :index => 4})

# or
browser.div(:class => "promo", :index => 4)

The example above locates the 4th <div> element with the class of promo.

Finding a bunch of elements

All supported HTML elements has their own collection method, e.g. spans, forms, lis, tables, h1s and divs. For a full list of supported elements, see the List of supported elements.

require "rubygems"
require "celerity"

browser = Celerity::Browser.new
browser.goto('HTTP://www.google.com')

puts browser.buttons

The example above produces something like:

tag:          input
  name:         btnG
  type:         submit
  value:        Google Search
  text:         Google Search
tag:          input
  name:         btnI
  type:         submit
  value:        I'm Feeling Lucky
  text:         I'm Feeling Lucky

Collection methods includes Enumerable, so you can call methods like each, each_with_index, select and collect on them. If you want to loop through all <form> elements, you can do something like this:

browser.forms.each do |form|
      puts form
    end

If you want all <li> elements that has an id that ends with ish, you could do:

li_elements = browser.lis.select { |li| li.id =~ /ish$/ }

Inspecting an element including its attributes

In Celerity you can inspect all HTML attributes just by calling a method with the same name as the attribute you want. If the attribute doesn’t exist, the method will return an empty string, "".

browser.div(:index, 1).id                  # Returns the id attribute of the first <div> element
browser.form(:index, 1).name               # Returns the name attribute of the first <form> element
browser.text_field(:id, "phone").maxlength # Returns the maxlength attribute of the first text field that has id="phone"
browser.div(:index, 1).class               # Won't work as expected. See next paragraph.

However, there are some special cases:

browser.div(:index, 1).class_name  # Since #class is an instance method in the Ruby Object class, we can't use it. You must use #class_name instead.
browser.div(:index, 1).text        # Returns the text inside the element recursively
browser.text_field(:index, 1).type # Return the type attribute, but if the type attribute is missing or invalid, it will return "text" (conforming with the HTML standards)

Manipulating elements

The most common actions are

  • Clicking a link
  • Clicking a button
  • Typing something in a text field
  • Selecting something from a select list

Clicking a link

# Clicks the first link that has a href attribute that matches the regular expression /delete_user\.jsp/
browser.link(:href, /delete_user\.jsp/).click

Clicking a button

# Clicks the first button that has value="Click it"
browser.button(:value, "Click it").click

Typing someting in a text field

# Inputs "smith123" into the first text field that has id="username"
browser.text_field(:id, "username").value = "smith123"

# Types "smith123" into the first text field that has id="username" and triggers Javascript events after each character
browser.text_field(:id, "username").set("smith123")

Fun fact: The first method is more than six times faster than the second method, using a 10 byte string.

Selecting something from a select list

# Selects the Norway option from the first select list with id="country"
browser.select_list(:id, "country").select("Norway")

This is how you handle Ajax

Problem

As each Ajax call runs in it’s own thread, the browser can’t (and shouldn’t) wait for it to finish. However, this sometimes causes problems in automated web testing. This is a typical example:

<form method="post" action="new_user.aspx">
  <fieldset>
    <select name="country" id="country" onchange="javascript:someAjaxCallThatUpdatesCountyList();">
      <option>-- Choose a country</option>
      <option>Norway</option>
      <option>Sweden</option>
    </select>
    <select name="county" id="county">
      <option>-- Choose a county</option>

    </select>
    <input type="submit" name="submit" id="submit" />
  </fieldset>
</form>

When you select a country, an Ajax call is made with the purpose of updating the county list. If you wanted to use Celerity to submit this form you could do something like this:

require "rubygems"
require "celerity"

browser = Celerity::Browser.new
browser.goto('example.htm')
browser.select_list(:id, "country").select("Norway")
browser.select_list(:id, "county").option(:text, "Oslo").select # There are many equivalent ways to select an option, so don't be afraid if this looks unfamiliar
browser.button(:id, "submit").click

However, if you run this script, you might see something like this:

Celerity::Exception::UnknownObjectException in ...
Unable to locate object, using :text and "Oslo"

The Ajax call is not finished, therefore the select list has not been updated with the proper counties.

Solution

See this page

One solution is to wait until it exists, or more precisely; keep checking if it exists until it does – then stop checking and continue.

require "rubygems"
require "celerity"

browser = Celerity::Browser.new
browser.goto('example.htm')
browser.select_list(:id, "country").select("Norway")

sleep(0.1) until browser.select_list(:id, "county").option(:text, "Oslo").exists # Wait until it exists

browser.select_list(:id, "county").option(:text, "Oslo").select
browser.button(:id, "submit").click