Tellurium 0.6.0 User Guide

The Tellurium Automated Testing Framework (Tellurium) is a UI module-based automated testing framework for web applications and it is robust, expressive, flexible, and reusable. This is Tellurium 0.6.0 User Guide.

5.
1 Introduction
1.1 Motivations
Automated web testing has always been one of the hottest and most important topics in the software testing
arena when it comes to the rising popularity of Rich Internet applications (RIA) and Ajax-based web
applications. With the advent of new web techniques such as RIA and Ajax, automated web testing tools must
keep up with these changes in technology and be able to address the following challenges:
• JavaScript Events: JavaScript is everywhere on the web today. Many web applications are
JavaScript heavy. To test JavaScript, the automated testing framework should be able to trigger
JavaScript events in a very convenient way.
• Ajax for Dynamic Web Content: Web applications have many benefits over desktop applications;
for example they have no installation and application updates are instantaneous and easier to support.
Ajax is a convenient way to update a part of the web page without refreshing the whole page. AJAX
makes web applications richer and more user-friendly. The web context for an Ajax application is
usually dynamic. For example, in a data grid, the data and number of rows keep changing at runtime.
• Robust/Responsive to Changes: A good automated web testing tool should be able to address the
changes in the web context to some degree so that users do not need to keep updating the test code.
• Easy to Maintain: In an agile world, software development is based on iterations, and new features
are added on in each sprint. The functional tests or user acceptance tests must be refactored and
updated for the new features. The testing framework should provide the flexibility for users to
maintain the test code very easily.
• Re-usability: Many web applications use the same UI module for different parts of the application.
The adoption of JavaScript frameworks such as Dojo and ExtJS increases the chance of using the
same UI module for different web applications. A good testing framework should also be able to
provide the re-usability of test modules.
• Expressiveness: The testing framework should be able to provide users without much coding
experience the ability to easily write test code or scripts in a familiar way, like by using a domain
specific language (DSL).
The Tellurium Automated Testing Framework (Tellurium) is designed with the following motivations:
• Robust/responsive to changes; allow changes to be localized
• Address dynamic web contexts such as JavaScript events and Ajax
• Easy to refactor and maintain
• Modular; test modules are reusable
• Expressive and easy to use
1.2 Why Tellurium is a New Approach for Web Testing
The Tellurium Automated Testing Framework (Tellurium) is an open source automated testing framework for
web applications that addresses the challenges and problems in web testing.
Most existing web testing tools/frameworks focus on individual UI elements such as links and buttons.
Tellurium takes a new approach for automated web testing using the concept of the UI module. The UI
module is a collection of UI elements grouped together. Usually, the UI module represents a composite UI
object in the format of nested basic UI elements. For example, the Google search UI module can be expressed
as follows:
1

6.
ui.Container(uid: "GoogleSearchModule", clocator: [tag: "td"], group: "true"){
InputBox(uid: "Input", clocator: [title: "Google Search"])
SubmitButton(uid: "Search", clocator: [name: "btnG", value: "Google Search"])
SubmitButton(uid: "ImFeelingLucky", clocator: [value: "I'm Feeling Lucky"])
}
Tellurium is built on the foundation of the UI module. The UI module makes it possible to build locators for
UI elements at runtime. First, this makes Tellurium robust and responsive to changes from internal UI
elements. Second, the UI module makes Tellurium expressive. A UI element can be referred to simply by
appending the names (uid) along the path to the specific element. This also enables Tellurium's Group
Locating feature, making composite objects reusable, and addressing dynamic web pages.
Tellurium is implemented in Groovy and Java. The test cases can be written in Java, Groovy, or pure Domain
Specific Language (DSL) scripts. Tellurium evolved out of Selenium, but the UI testing approach is
completely different. For example, Tellurium is not a "record and replay" style framework, and it enforces the
separation of UI modules from test code, making refactoring easy. For example, once you defined the Google
Search UI module shown above, you can write your test code as follows:
type "GoogleSearchModule.Input", "Tellurium test"
click "GoogleSearchModule.Search"
Tellurium does Object to Locator Mapping (OLM) automatically at run time so that UI objects can be defined
simply by their attributes using Composite Locators. Tellurium uses the Group Locating Concept (GLC) to
exploit information inside a collection of UI components so that locators can find their elements. It also
defines a set of DSLs for web testing. Furthermore, Tellurium uses UI templates to define sets of dynamic UI
elements at runtime. As a result, Tellurium is robust, expressive, flexible, reusable, and easy to maintain. The
main features of Tellurium include:
• Abstract UI objects to encapsulate web UI elements
• UI module for structured test code and re-usability
• DSL for UI definition, actions, and testing
• Composite Locator to use a set of attributes to describe a UI element
• Group locating to exploit information inside a collection of UI components
• Dynamically generate runtime locators to localize changes
• UI templates for dynamic web content
• XPath support
• jQuery selector support to improve test speed in IE
• Locator caching to improve speed
• Javascript event support
• Use Tellurium Firefox plugin, Trump, to automatically generate UI modules
• Dojo and ExtJS widget extensions
• Data driven test support
• Selenium Grid support
• JUnit and TestNG support
• Ant and Maven support
1.3 How Challenges and Problems are addressed in Tellurium
First of all, Tellurium does not use "record and replay." Instead, it uses the Tellurium Firefox plugin Trump to
generate the UI module (not test code) for you. Then you need to create your test code based on the UI
module. In this way, the UI and the test code are decoupled. The structured test code in Tellurium makes it
2

7.
much easier to refactor and maintain the code.
The composite locator uses UI element attributes to define the UI, and the actual locator (e.g. xpath or jQuery
selector) will be generated at runtime. Any updates to the composite locator will lead to different runtime
locators, and the changes inside the UI module are localized. The Group locating is used to remove the
dependency of the UI objects from external UI elements (i.e. external UI changes will not affect the current UI
module for most cases) so that your test code is robust and responsive to changes up to a certain level.
Tellurium uses the respond attribute in a UI object for you to specify JavaScript events, and the rest will be
handled automatically by the framework itself. UI templates are a powerful feature in Tellurium used to
represent many identical UI elements or a dynamic size of different UI elements at runtime, which are
extremely useful in testing dynamic web contexts such as a data grid. The Option UI object is designed to
automatically address dynamic web contexts with multiple possible UI patterns.
Re-usability is achieved by the UI module when working within one application and by Tellurium Widgets
when working across different web applications. With the Domain Specific Language (DSL) in Tellurium you
can define UI modules and write test code in a very expressive way. Tellurium also provides you the
flexibility to write test code in Java, Groovy, or pure DSL scripts.
1.4 Tellurium Architecture
The Tellurium framework architecture is shown as follows.
3

8.
The DSL parser consists of the DSL Object Parser, Object Builders, and the Object Registry.
Thanks to Groovy builder pattern, we can define UI objects expressively and in a nested fashion. The DSL
object parser will parse the DSL object definition recursively and use object builders to build the objects on
the fly. An object builder registry is designed to hold all predefined UI object builders in the Tellurium
framework, and the DSL object parser will look at the builder registry to find the appropriate builders. Since
the registry is a hash map, you can override a builder with a new one using the same UI name. Users can also
add their customer builders into the builder registry. The DSL object definition always comes first with a
container type object. An object registry (a hash map) is used to store all top level UI Objects. As a result, for
each DSL object definition, the top object ids must be unique in the DslContext. The object registry will be
used by the framework to search objects by their ids and fetch objects for different actions.
The Object Locator Mapping (OLM) is the core of the Tellurium framework and it includes UI ID mapping,
XPath builder, jQuery selector builder, and Group Locating.
The UI ID supports nested objects. For example, "menu.wiki" stands for a URL Link "wiki" inside a container
called "menu". The UI ID also supports one-dimensional and two-dimensional indices for table and list. For
example, "main.table[2][3]" stands for the UI object of the 2nd row and the 3rd column of a table
4

9.
inside the container "main".
XPath builder can build the XPath from the composite locator, i.e., a set of attributes. Starting with version
0.6.0, Tellurium supports jQuery selectors to address the problem of poor performance of XPath in Internet
Explorer. jQuery selector builders are used to automatically generate jQuery selectors instead of XPath with
the following advantages:
• Faster performance in IE.
• Leverage the power of jQuery to retrieve bulk data from the web by testing with one method call.
• New features provided by jQuery attribute selectors.
The Group Locating Concept (GLC) exploits the group information inside a collection of UI objects to help us
find the locator of the collection of UI objects.
The Eventhandler will handle all events like "click", "type", "select", and so on. The Data Accessor is used to
fetch data or UI status from the DOM. The dispatcher will delegate all calls it receives from the Eventhandler
and the data accessor to the connector, which connects to the Tellurium engine. The dispatcher is designed to
decouple the rest of the Tellurium framework from the base test driving engine so that we can switch to a
different test driving engine by simply changing the dispatcher logic.
1.5 HOW Tellurium Works
Basically, there are two parts for the Tellurium framework. The first part is to define UI objects and the
second part is working on the UI objects like firing events and getting data or status from the DOM.
The defineUI operation can be demonstrated in the following sequence diagram:
When the Test code calls defineUI(), the DslContext calls the Dsl Object Parser to parse the UI definition.
The Parser looks at each node and call the appropriate builders to build UI objects. The top level object is
stored in the UI Object registry so that we can search for the UI object by uid.
5

10.
The processing of actions such as clicking on an UI object is illustrated in the following sequence diagram:
The action processing includes following two parts.
First, the DslContext will create a WorkflowContext so that we can pass meta data such the relative
locator inside it. Then, we start to look at the UI object registry by calling the walkTo(uid) method.
Remember, the UI object registry hold all the top level UI objects. If we can find the top level UI object, we
can recursively call the walkTo(uid) method on the next UI object until we find the UI Object matching
the uid. During the walkTo method calls, we start to aggregate relative xpaths or jQuery selector into the
reference locator and pass it on to the next UI object. In this way, the runtime locator is built.
If the UI Object is found, we call the action such as "click" on the UI object and the call is passed on to the
EventHandler, where additional JavaScript events may be fired before and/or after the click action as shown
in the above sequence diagram. The action and JavaScript events are passed all the way down from the
dispatcher and connector to the Tellurium Engine, which is embedded in the Selenium server at the current
stage.
6

11.
2 Concepts and Features
2.1 UI Object
Tellurium provides a set of predefined UI objects, which users can use directly.
The basic UI object is an abstract class. Users cannot instantiate it directly. The basic UI Object works as the
base class for all UI objects and it includes the following attributes:
1. uid: UI object's identifier
2. namespace: can be used for XHTML
3. locator: the locator of the UI object, could be a base locator or a composite locator
4. group: this attribute is used for Group Locating and it only applies to a collection type of UI object
such as Container, Table, List, Form.
5. respond: the JavaScript events the UI object can respond to. The value is a list.
All UI Objects will inherit the above attributes and methods. But be aware that you usually do not call these
methods directly and you should use DSL syntax instead. For example, use:
click "GoogleSearchModule.Search"
In this way, Tellurium will first map the UIID "GoogleSearchModule.Search" to the actual UI object and then
call the click method on it. If that Ui object does not have the click method defined, you will get an error.
2.2 Composite Locator
Tellurium supports two types of locators: base locator and composite locator. The base locator is a relative
XPath. The composite locator, denoted by "clocator", specifies a set of attributes for the UI object and the
actual locator will be derived automatically by Tellurium at runtime.
The Composite Locator is defined as follows:
class CompositeLocator {
String header
String tag
String text
String trailer
def position
boolean direct
Map<String, String> attributes = [:]
}
To use the composite locator, you need to use "clocator" with a map as its value. For example:
clocator: [key1: value1, key2: value2, ...]
The default attributes include "header", "tag", "text", "trailer", "position", and "direct". They are all optional.
The "direct" attribute specifies whether this UI object is a direct child of its parent UI, and the default value is
"false".
If you have additional attributes, you can define them in the same way as the default attributes, for example:
7

12.
clocator: [tag: "div", value: "Tellurium home"]
Most Tellurium objects come with default values for certain attributes, for example, the tag attribute. If these
attributes are not specified, the default attribute values will be used. In other words, if you know the default
attribute values of a Tellurium UI object, you can omit them in clocator. Take the RadioButton object as an
example, its default tag is "input," and its default type is "radio." You can omit them and write the clocator as
follows:
clocator: [:]
which is equivalent to
clocator: [tag: "input", type: "radio"]
2.3 UI Module
The UI Module is the heart of Tellurium. The UI module is a collection of UI elements grouped together.
Usually, the UI module represents a composite UI object in the format of nested basic UI elements. For
example, the download search module in Tellurium's project site is defined as follows:
ui.Form(uid: "downloadSearch", clocator: [action: "list", method: "get"], group: "true") {
Selector(uid: "downloadType", clocator: [name: "can", id: "can"])
InputBox(uid: "searchBox", clocator: [name: "q"])
SubmitButton(uid: "searchButton", clocator: [value: "Search"])
}
Tellurium is built on the foundation of the UI module. The UI module makes it possible to build locators for
UI elements at runtime. First, this makes Tellurium robust and responsive to changes from internal UI
elements. Second, the UI module makes Tellurium expressive. A UI element can be referred to simply by
appending the names (uids) along the path to the specific element. This also enables Tellurium's "Group
Locating" feature, making composite objects reusable and addressing dynamic web pages.
This frees up the testers to write better tests rather than spend precious testing time on identifying and
resolving test failures due to XPath changes.
2.4 UiID
In Tellurium, the UI object is referred by its UiID. For nested Ui objects, the UiID of the UI Object is a
concatenated of the UI objects' uids along its path to the UI Object. For example, in the following nested UI
Module,
ui.Container(uid: "parent_ui"){
InputBox(uid: "inputbox1", locator: "...")
Button(uid: "button1", locator: "...")
Container(uid: "child_ui){
Selector(uid: "selector1", locator: "...")
...
Container(uid: "grand_child"){
TextBox(uid: "textbox1", locator: "...")
...
}
}
}
8

13.
the TextBox is referred as "parent_ui.child_ui.grand_child.textbox1". The exceptions are tables and lists,
which use [x][y] or [x] to reference its elements inside. For example, labels_table[2][1] and
GoogleBooksList.subcategory[2]. Table header can be referred in the format of
issueResult.header[2].
More general case can be shown by the following diagram,
For example, the UiID of the List E in the above diagram is A.C.E and the InputButton in the List E is
referred by its index n, i.e., A.C.E[n].
2.5 Group Locating
In the Tellurium UI module, you often see the "group" attribute. For example:
ui.Container(uid: "google_start_page", clocator: [tag: "td"], group: "true"){
InputBox(uid: "searchbox", clocator: [title: "Google Search"])
SubmitButton(uid: "googlesearch", clocator: [name: "btnG", value: "Google Search"])
SubmitButton(uid: "Imfeelinglucky", clocator: [value: "I'm Feeling Lucky"])
}
What does the attribute group mean? The group attribute is a flag for the Group Locating Concept. Usually,
the XPath generated by Selenium IDE, XPather, or other tools is a single path to the target node such as:
//div/table[@id='something']/div[2]/div[3]/div[1]/div[6]
No sibling node's information is used here. What is wrong with this? The XPath depends too much on
information from nodes far away from the target node. In Tellurium, we try to localize the information and
reduce this dependency by using sibling information or local information. For example, in the above google
UI module, the group locating concept will try to find the "td" tag with its children as "InputBox",
"googlesearch" button, and "Imfeelinglucky" button. In this way, we can reduce the dependencies of the UI
elements inside a UI module on external UI elements to make the UI definition more robust.
9

14.
2.6 UI Templates
Tellurium UI templates are used for two purposes:
1. When there are many identical UI elements, you can use one template to represent them all
2. When here are a variable/dynamic size of UI elements at runtime, you know the patterns, but not the
size.
More specifically, Table and List are two Tellurium objects that can define UI templates. Table defines two
dimensional UI templates, and List is for one dimensional. The Template has special UIDs such as "2", "all",
or "row: 1, column: 2".
Let us look at use case (1), we have the following HTML source
<ul class="a">
<li>
<A HREF="site?tcid=a"
class="b">AA
</A>
</li>
<li>
<A HREF="site?tcid=b"
class="b">BB
</A>
</li>
<li>
<A HREF="site?;tcid=c"
class="b">CC
</A>
</li>
<li>
<A HREF="site?tcid=d"
class="b">DD
</A>
</li>
<li>
<A HREF="site?tcid=e"
class="b">EE
</A>
</li>
<li>
<A HREF="site?tcid=f"
class="b">FF
</A>
</li>
</ul>
You have six links there. Without templates, you have to put six UrlLink objects in the UI module. Look at
how simple by using the template
ui.List(uid: "list", clocator: [tag: "ul", class: "a"], separator:"li")
{
UrlLink(uid: "all", clocator: [class: "b"])
}
10

15.
For use case (2), a common application is the data grid. Look at the "issueResult" data grid on our Tellurium
Issues page:
ui.Table(uid: "issueResult", clocator: [id: "resultstable", class: "results"],
group: "true")
{
TextBox(uid: "header: 1", clocator: [:])
UrlLink(uid: "header: 2", clocator: [text: "%%ID"])
UrlLink(uid: "header: 3", clocator: [text: "%%Type"])
UrlLink(uid: "header: 4", clocator: [text: "%%Status"])
UrlLink(uid: "header: 5", clocator: [text: "%%Priority"])
UrlLink(uid: "header: 6", clocator: [text: "%%Milestone"])
UrlLink(uid: "header: 7", clocator: [text: "%%Owner"])
UrlLink(uid: "header: 9", clocator: [text: "%%Summary + Labels"])
UrlLink(uid: "header: 10", clocator: [text: "%%..."])
//define table elements
//for the border column
TextBox(uid: "row: *, column: 1", clocator: [:])
//For the rest, just UrlLink
UrlLink(uid: "all", clocator: [:])
}
Aren't the definitions very simple and cool?
You may wonder how to use the templates if you have multiple templates such as the "issueResult" table
shown above. The rule to apply the templates is: "specific one first, general one later".
2.7 Javascript Events
Most web applications include Javascript, and thus the web testing framework must be able to handle
Javascript events. What we really care about is firing the appropriate events to trigger the event handlers.
Selenium has already provided methods to generate events such as:
fireEvent(locator, "blur")
fireEvent(locator, "focus")
mouseOut(locator)
mouseOver(locator)
Tellurium was born with Javascript events in mind since it was initially designed to test applications written
using the DOJO JavaScript framework. For example, we have the following radio button:
<input type='radio' name='mas_address_key' value='5779' onClick='SetAddress_5779()'>
Although we can define the radio button as follows:
RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779'])
The above code will not respond to the click event since the Tellurium RadioButton only supports the "check"
and "uncheck" actions, which is enough for the normal case. As a result, no "click" event/action will be
generated during the testing. To address this problem, Tellurium added the "respond" attribute to Tellurium
UI objects. The "respond" attribute could be used to define whatever events you want the UI object to respond
to. The Radio Button can be redefined as:
11

16.
ui.Container(uid: "form", clocator: [whatever]){
RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779'],
respond: ["click"])
}
That is to say, you can issue the following command:
click "form.billing"
Even if the RadioButton does not have the click method defined by default, it is still able to dynamically add
the click method at runtime and call it.
A more general example is as follows:
InputBox(uid: "searchbox", clocator: [title: "Google Search"],
respond: ["click", "focus", "mouseOver", "mouseOut", "blur"])
Except for the "click" event, all of the "focus", "mouseOver", "mouseOut", and "blur" events will be
automatically fired by Tellurium during testing. Do not worry about the event order for the respond attribute,
Tellurium will automatically re-order the events and then process them appropriately for you.
2.8 Logical Container
The Container object in Tellurium is used to hold child objects that are in the same subtree in the DOM
object. However, there are always exceptions. For example, logical Container (Or virtual Container, but I
think Logical Container is better) can violate this rule.
What is a logic Container? It is a Container with empty locator, for instance,
Container(uid: "logical"){
......
}
But empty != nothing, there are some scenarios that the logical container can play an important role. The
reason is the Container includes a uid for you to refer and it can logically group UI element together.
Look at the following example,
<div class="block_content">
<ul>
<li>
<h5>
<a href="" title="">xxx</a>
</h5>
<p class="product_desc">
<a href=".." title="More">...</a>
</p>
<a href="..." title=".." class="product_image">
<img src="..." alt="..." height="129" width="129"/>
</a>
<p>
<a class="button" href="..." title="View">View</a>
<a title="Add to cart">Add to cart</a>
</p>
</li>
12

17.
<li>
similar UI
</li>
<li>
similar UI
</li>
</ul>
</div>
As you see, the UI elements under the tag li are different and how to write the UI template for them? The
good news is that the logical Container comes to play. For example, the UI module could be written as
ui.Container(uid: "content", clocator: [tag: "div", class: "block_content"]){
List(uid: "list", clocator: [tag: "ul"], separator:"li") {
Container("all"){
UrlLink(uid: "title", clocator: [title: "View"])
......
other elements inside the li tag
}
}
}
Another usage of the logical Container is to convert the test case recorded by Selenium IDE to Tellurium test
cases. Let us take the search UI on Tellurium download page as an example.
First, I recorded the following Selenium test case using Selenium IDE,
import com.thoughtworks.selenium.SeleneseTestCase;
public class SeleniumTestCase extends SeleneseTestCase {
public void setUp() throws Exception {
setUp("http://code.google.com/", "*chrome");
}
public void testNew() throws Exception {
selenium.open("/p/aost/downloads/list");
selenium.select("can", "label=regexp:sAll Downloads");
selenium.type("q", "TrUMP");
selenium.click("//input[@value='Search']");
selenium.waitForPageToLoad("30000");
}
}
Don't be confused by the locator "can" and "q", they are just UI element ids and can be easily expressed in
XPath. The "label=regexp:sAll Downloads" part just tells you that Selenium uses regular express to match
the String and the "s" stands for a space. As a result, we can write the UI module based on the above code.
public class TelluriumDownloadPage extends DslContext {
public void defineUi() {
ui.Container(uid: "downloads") {
Selector(uid: "downloadType", locator: "//*[@id='can']")
InputBox(uid: "input", locator: "//*[@id='q']")
SubmitButton(uid: "search", locator: "//input[@value='Search']")
}
}
public void searchDownload(String downloadType, String searchKeyWords) {
selectByLabel "downloads.downloadType", downloadType
13

18.
keyType "downloads.input", searchKeyWords
click "downloads.search"
waitForPageToLoad 30000
}
}
And we can create the Tellurium test case accordingly,
public class TelluriumDownloadPageTestCase extends TelluriumJavaTestCase {
protected static TelluriumDownloadPage ngsp;
@BeforeClass
public static void initUi() {
ngsp = new TelluriumDownloadPage();
ngsp.defineUi();
}
@Test
public void testSearchDownload(){
connectUrl("http://code.google.com/p/aost/downloads/list");
ngsp.searchDownload(" All Downloads", "TrUMP");
}
}
2.9 jQuery Selector
The motivation behind the jQuery Selector is to improve the test speed in IE since IE lacks of native XPath
support and the XPath is very slow. Tellurium exploits jQuery selector capability to improve the test speed
dramatically.
Tellurium supports both XPath and jQuery selector and still uses XPath as the default locator. To use jQuery
selector, you need to explicitly tell Tellurium to use jQuery selector.
To enable jQuery selector call the following method.
• useJQuerySelector(): Use jQuery selector.
To disable jQuery selector call the following method, it also switches back to the default locator. i.e. XPath
• disableJQuerySelector(): Switch back to XPath locator.
The above can be better illustrated the following diagram.
14

19.
Some of the jQuery functions are provided by the custom selenium server, which is created by our Tellurium
Engine project. Be aware that jQuery selector only works for composite locator, i.e. clocator. You cannot have
base locator, i.e., XPath, in your UI module.
How does the jQuery selector work? The basic idea is to customize Selenium Core to load the jQuery library
at startup time. You can add jquery.js in to the TestRunner.html and RemoteRunner.html. Another way is
dump all jquery.js into user-extensions.js. Since our Engine prototype customizes Selenium core anyway, we
used the former method.
After that, we register a custom locate strategy "jquery", using the following Selenium API call:
addLocationStrategy ( strategyName,functionDefinition )
This defines a new function for Selenium to locate elements on the page. For example, if you define the
strategy "foo", and someone runs click("foo=blah"), we'll run your function, passing you the string "blah", and
click on the element that your function returns, or throw an "Element not found" error if your function returns
null. Selenium passed three arguments to the location strategy function:
• locator: the string the user passed in
• inWindow: the currently selected window
• inDocument: the currently selected document
The function must return null if the element can't be found.
Arguments:
15

20.
• strategyName: the name of the strategy to define; this should use only letters a-zA-Z with no spaces
or other punctuation.
• functionDefinition: a string defining the body of a function in JavaScript. For example: return
inDocument.getElementById(locator);
Accordingly, we define the custom locate strategy as follows,
addLocationStrategy("jquery", '''
var found = $(inDocument).find(locator);
if(found.length == 1 ){
return found[0];
}else if(found.length > 1){
return found.get();
}else{
return null;
}
''')
The code is pretty straightforward. When we find one element, return its DOM reference (Note: Selenium
does not accept returning an array with only one element) and if we find multiple elements, we use jQuery
get() method to return an array of DOM references. Otherwise, return null.
The actual code is a bit more complicated because we need to consider our custom attribute locator. We use
the same format of attribute locator as the XPath one, i.e.,
locator@attribute
Apart from that, we need to create a set of custom Selenium methods and add the appropriate Selenium RC
Java methods. For example, we created the following Selenium method
Selenium.prototype.getAllText = function(locator){
var $e = $(this.browserbot.findElement(locator));
var out = [];
$e.each(function(){
out.push($(this).text());
});
return JSON.stringify(out);
};
Then, created the corresponding Java method by extending Selenium
class CustomSelenium extends DefaultSelenium {
def String getAllText(String locator){
String[] arr = [locator];
String st = commandProcessor.doCommand("getAllText", arr);
return st;
}
}
jQuery Selector provides the following additional Selenium methods, which are utilized in DslContext to form
a set of new DSL methods.
16

21.
• String getAllText(String locator): Get all text from the set of elements corresponding
to the jQuery Selector.
• String getCSS(String locator, String cssName) : Get the CSS properties for the
set of elements corresponding to the jQuery Selector.
• Number getJQuerySelectorCount(String locator) : Get the number of elements
matching the corresponding jQuery Selector
jQuery also supports the following attribute selectors.
• attribute: have the specified attribute.
• attribute=value: have the specified attribute with a certain value.
• attribute!=value: either don't have the specified attribute or do have the specified attribute but
not with a certain value.
• attribute^=value: have the specified attribute and it starts with a certain value.
• attribute$=value: have the specified attribute and it ends with a certain value.
• attribute*=value: have the specified attribute and it contains a certain value.
Apart from the above, Tellurium provides a set of locator agnostic methods, i.e., the method will
automatically decide to use XPath or jQuery dependent on the exploreJQuerySelector flag, which can be turn
on and off by the following two methods:
public void useJQuerySelector()
public void disableJQuerySelector()
In the meanwhile, Tellurium also provides the corresponding XPath specific and jQuery selector specific
methods for your convenience. However, we recommend you to use the locator agnostic methods until you
have a good reason not to.
The new methods are listed here:
• Get the Generated locator from the UI module
Locator agnostic:
♦ String getLocator(String uid)
JQuery selector specific:
♦ String getSelector(String uid)
XPath specific:
♦ String getXPath(String uid)
17

24.
The are something you should be aware of for jQuery Selector:
• If you have a duplicate "id" attribute on the page, jQuery selector always returns the first DOM
reference, ignoring other DOM references with the same "id" attribute.
• Some attribute may not working in jQuery, for example, the "action" attribute in a form. Tellurium
has a black list to automatically filter out the attributes that are not honored by jQuery selector.
• Seems the "src" attribute in Image has to be full URL such as http://www.google.com. One
workaround is to put '*' before the URL.
2.10 jQuery Selector Cache Option
jQuery cache is a mechanism to further improve the speed by reusing the found DOM reference for a given
jQuery selector. Our benchmark results show that the jQuery cache could improve the speed by up to 14%
over the regular jQuery selector and over 27% for some extreme cases. But the improvement of jQuery cache
over regular jQuery selector has upper bound, which is the portion of jQuery locating time out of the round
trip time from Tellurium core to Selenium core. jQuery cache is still considered to be experimental at the
current stage. Use it at your own discretion.
To make the cache option configurable in tellurium UI module, Tellurium introduces an attribute cacheable to
UI object, which is set to be true by default. For a Container type object, it has an extra attribute
noCacheForChildren to control whether to cache its children.
One example UI module is defined as follows,
ui.Table(uid: "issueResultWithCache", cacheable: "true", noCacheForChildren: "true",
clocator: [id: "resultstable", class: "results"], group: "true") {
......
//define table elements
//for the border column
TextBox(uid: "row: *, column: 1", clocator: [:])
TextBox(uid: "row: *, column: 8", clocator: [:])
TextBox(uid: "row: *, column: 10", clocator: [:])
//For the rest, just UrlLink
UrlLink(uid: "all", clocator: [:])
}
Where the cacheable can overwrite the default value in the UI object and noCacheForChildren in the above
example will force Tellurium to not cache its children.
When you choose to use jQuery Cache mechanism, Tellurium core will pass the jQuery selector and a meta
command to Tellurium Engine, which is embedded inside Selenium core at the current stage. The format is as
follows taking the Tellurium issue searching UI as an example,
jquerycache={
"locator":"form[method=get]:has(#can, span:contains(for), input[type=text][name=q],
input[value=Search][type=submit]) #can",
"optimized":"#can",
"uid":"issueSearch.issueType",
"cacheable":true,
"unique":true
}
20

25.
where locator is the regular jQuery selector and optimized is the optimized jQuery selector by the jQuery
selector optimizer in Tellurium core. The locator is used for child UI element to derive a partial jQuery
selector from its parent. The optimized will be used for actual DOM searching.
The rest three parameters are meta commands. uid is the UID for the corresponding jQuery selector and
cacheable tells the Engine whether to cache the selector or not. The reason is that some UI element on the
web is dynamic, for instance, the UI element inside a data grid. As a result, it may not be useful to cache the
jQuery selector.
The last one, unique, tells the Engine whether the jQuery selector expects multiple elements or not. This is
very useful to handle the case that jQuery selector is expected to return one element, but actually returns
multiple ones. In such a case, a Selenium Error will be thrown.
On the Engine side the cache is defined as
//global flag to decide whether to cache jQuery selectors
this.cacheSelector = false;
//cache for jQuery selectors
this.sCache = new Hashtable();
this.maxCacheSize = 50;
this.cachePolicy = discardOldCachePolicy;
The cache system includes a global flag to decide whether to use the cache capability, a hash table to store the
cached data, a cache size limit, and a cache eviction policy once the cache is filled up.
The cached data structure is defined as
//Cached Data, use uid as the key to reference it
function CacheData(){
//jQuery selector associated with the DOM reference, which is a whole selector
//without optimization so that it is easier to find the reminding selector
//for its children
this.selector = null;
//optimized selector for actual DOM search
this.optimized = null;
//jQuery object for DOM reference
this.reference = null;
//number of reuse
this.count = 0;
//last use time
this.timestamp = Number(new Date());
};
Tellurium Engine provides the following cache eviction policies:
• DiscardNewPolicy: discard new jQuery selector.
• DiscardOldPolicy: discard the oldest jQuery selector measured by the last update time.
• DiscardLeastUsedPolicy: discard the least used jQuery selector.
• DiscardInvalidPolicy: discard the invalid jQuery selector first.
It is important to know when the cached data become invalid. There are three mechanisms we can utilize here:
21

26.
1. Listen for page refresh event and invalidate the cache data accordingly
2. Intercept Ajax response to decide when the web is update and if the cache needs to be updated.
3. Validate the cached jQuery selector before using it.
Right now, Tellurium Engine uses the 3rd mechanism to check if the cached data is valid or not. The first two
mechanisms are still under development.
Whenever Tellurium Core passes a locator to the Engine, the Engine will first look at the meta command
cacheable. If this flag is true, it will first try to look up the DOM reference from the cache. If no cached DOM
reference is available, do a fresh jQuery search and cache the DOM reference, otherwise, validate the cached
DOM reference and use it directly. If the cacheable flag is false, the Engine will look for the UI element's
ancestor by its UID and do a jQuery search starting from its ancestor if possible.
Tellurium Core provides the following methods for jQuery Selector cache control,
• public void enableSelectorCache(): Enable jQuery selector cache
• public boolean disableSelectorCache(): Disable jQuery selector cache
• public boolean cleanSelectorCache(): Cleanup the whole jQuery selector cache
• public boolean getSelectorCacheState(): Test if the cache is enabled or not in Engine
• public void setCacheMaxSize(int size): Set the cache maximum size
• public int getCacheSize(): Get the cache current size
• public int getCacheMaxSize(): Get the cache maximum size
• public Map<String, Long> getCacheUsage(): Get the cache usage
• public void useDiscardNewCachePolicy(): Use DiscardNewCachePolicy
• public void useDiscardOldCachePolicy(): Use DiscardOldCachePolicy
• public void useDiscardLeastUsedCachePolicy(): Use
DiscardLeastUsedCachePolicy
• public void useDiscardInvalidCachePolicy(): Use DiscardInvalidCachePolicy
• public String getCurrentCachePolicy(): Get the current cache policy
2.11 "Include" Frequently Used Sets of elements in UI Modules
Sometimes, you have a frequently used set of elements, which you do not want to redefined them over and
over again in your UI module. Now, you can use the "Include" syntax to re-use pre-defined UI elements,
Include(uid: UID, ref: REFERRED_UID)
22

29.
customUiCall "Google.Input", typeRepeated, "Tellurium Groovy"
The customUiCall method handles all the UI to locator mapping for users. Tellurium also provides the
following method for users to make direct calls to Selenium server.
customDirectCall(String method, Object[] args)
2.14 Data Driven Testing
Data Driven Testing is a different way to write tests, i.e, separate test data from the test scripts and the test
flow is not controlled by the test scripts, but by the input file instead. In the input file, users can specify which
tests to run, what are input parameters, and what are expected results. Data driven testing in Tellurium can be
illustrated by the following system diagram.
The tellurium data driven test consists of three main parts, i.e., Data Provider, TelluriumDataDrivenModule,
and TelluriumDataDrivenTest. The data provider is responsible for reading data from input stream and
converting data to Java variables. Tellurium provides the following data provider methods:
1. loadData file_name, load input data from a file
2. useData String_name, load input data from a String in the test script
3. bind(field_name), bind a variable to a field in a field set
4. closeData, close the input data stream and report the test results
5. cacheVariable(name, variable), put variable into cache
6. getCachedVariable(name, variable), get variable from cache
where the file_name should include the file path, for example,
loadData "src/test/example/test/ddt/GoogleBookListCodeHostInput.txt"
25

30.
Right now, Tellurium supports pipe format and CSV formatinput file. To change the file reader for different
formats, please change the following settings in the configuration file TelluriumConfig.groovy:
datadriven{
dataprovider{
//specify which data reader you like the data provider to use
//the valid options include "PipeFileReader", "CSVFileReader" at this point
reader = "PipeFileReader"
}
}
Sometimes, you may like to specify test data in the test scripts directly, useData is designed for this purpose
and it loads input from a String. The String is usually defined in Groovy style using triple quota, for example:
protected String data = """
google_search | true | 865-692-6000 | tellurium
google_search | false| 865-123-4444 | tellurium selenium test
google_search | true | 755-452-4444 | tellurium groovy
google_search | false| 666-784-1233 | tellurium user group
google_search | true | 865-123-5555 | tellurium data driven
"""
...
useData data
bind is the command to bind a variable to input Field Set field at runtime. FieldSet is the format of a line of
data and it is defined in the next section. For example,
def row = bind("GCHLabel.row")
is used to bind the row variable to the "row" field in the FieldSet "GCHLabel". Tellurium does not explicitly
differentiate input parameters from the expected results in the input data. You only need to bind variables to
the input data and then you can use any of them as the expected results for result comparison.
cacheVariable and getCachedVariable are used to pass intermediate variables among tests. cacheVariable is
used to put variable into a cache and getCachedVariable is used to get back the variable. For example,
int headernum = getTableHeaderNum()
cacheVariable("headernum", headernum)
...
int headernum = getCachedVariable("headernum")
...
When you are done with your testing, please use "closeData" to close the input data stream. In the meantime,
the result reporter will output the test results in the format you specified in the configuration file, for example,
XML file as shown in the TelluriumConfig.groovy file:
test{
result{
//specify what result reporter used for the test result
//valid options include "SimpleResultReporter", "XMLResultReporter",
//and "StreamXMLResultReporter"
reporter = "XMLResultReporter"
//the output of the result
26

31.
//valid options include "Console", "File" at this point
//if the option is "File", you need to specify the file name,
//other wise it will use the default
//file name "TestResults.output"
output = "Console"
//test result output file name
filename = "TestResult.output"
}
}
TelluriumDataDrivenModule is used to define modules, where users can define UI Modules, FieldSets, and
tests as shown in the following sequence diagram. Users should extend this class to define their own test
modules.
TelluriumDataDrivenModule provides one method "defineModule" for users to implement. Since it extends
the DslContext class, users can define UI modules just like in regular Tellurium UI Module. For example:
ui.Table(uid: "labels_table", clocator: [:], group: "true"){
TextBox(uid: "row: 1, column: 1", clocator: [tag: "div",
text: "Example project labels:"])
Table(uid: "row: 2, column: 1", clocator: [header: "/div[@id="popular"]"]){
UrlLink(uid: "all", locator: "/a")
}
}
FieldSet is used to define the format of one line of input data and FieldSet consists of fields, i.e., columns, in
the input data. There is a special field "test", which users can specify what test this line of data apply to. For
example,
fs.FieldSet(name: "GCHStatus", description: "Google Code Hosting input") {
Test(value: "getGCHStatus")
27

32.
Field(name: "label")
Field(name: "rowNum", type: "int")
Field(name: "columNum", type: "int")
}
The above FieldSet defines the input data format for testing google code hosting web page. Note, the Test
field must be the first column of the input data. The default name of the test field is "test" and does not need to
be specified. If the value attribute of the test field is not specified, it implies this same format, i.e., FieldSet,
can used for different tests.
For a regular field, it includes the following attributes:
class Field {
//Field name
private String name
//Field type, default is String
private String type = "String"
//optional description of the Field
private String description
//If the value can be null, default is true
private boolean nullable = true
//optional null value if the value is null or not specified
private String nullValue
//If the length is not specified, it is -1
private int length = -1
//optional String pattern for the value
//if specified, we must use it for String validation
private String pattern
}
Tellurium can automatically handle Java primitive types. Another flexibility Tellurium provides is that users
can define their own custom type handlers to deal with more complicated data types by using "typeHandler",
for example,
//define custom data type and its type handler
typeHandler "phoneNumber", "org.tellurium.test.PhoneNumberTypeHandler"
//define file data format
fs.FieldSet(name: "fs4googlesearch", description: "example field set for google search"){
Field(name: "regularSearch", type: "boolean",
description: "whether we should use regular search or use I'm feeling lucky")
Field(name: "phoneNumber", type: "phoneNumber", description: "Phone number")
Field(name: "input", description: "input variable")
}
The above script defined a custom type "PhoneNumber" and the Tellurium will automatically call this type
handler to convert the input data to the "PhoneNumber" Java type.
The "defineTest" method is used to define a test in the TelluriumDataDrivenModule, for example, the
following script defines the "clickGCHLabel" test:
28

33.
defineTest("clickGCHLabel"){
def row = bind("GCHLabel.row")
def column = bind("GCHLabel.column")
openUrl("http://code.google.com/hosting/")
click "labels_table[2][1].[${row}][${column}]"
waitForPageToLoad 30000
}
Note, the bind command is used to bind variables row, column to the fields "row" and "column" in the
FieldSet "GCHLabel".
Tellurium also provide the command "compareResult" for users to compare the actual result with the expected
result. For example, the following script compares the expected label, row number, and column number with
the acutal ones at runtime:
defineTest("getGCHStatus"){
def expectedLabel = bind("GCHStatus.label")
def expectedRowNum = bind("GCHStatus.rowNum")
def expectedColumnNum = bind("GCHStatus.columNum")
openUrl("http://code.google.com/hosting/")
def label = getText("labels_table[1][1]")
def rownum = getTableMaxRowNum("labels_table[2][1]")
def columnum = getTableMaxColumnNum("labels_table[2][1]")
compareResult(expectedLabel, label)
compareResult(expectedRowNum, rownum)
compareResult(expectedColumnNum, columnum)
pause 1000
}
Sometimes, users may need custom "compareResult" to handle more complicated situation, for example, users
want to compare two lists. In such a case, users can override the default "compareResult" behaviour by
specifying custom code in the closure:
compareResult(list1, list2){
assertTrue(list1.size() == list2.size())
for(int i=0; i<list1.size();i++){
//put your custom comparison code here
}
}
If users want to check a variable in the test and the "checkResult" method should be used, which comes with a
closure where users can define the actually assertions inside.
checkResult(issueTypeLabel) {
assertTrue(issueTypeLabel != null)
}
Like "compareResult", "checkResult" will capture all assertion errors. The test will resume even the assertions
fail and the result will be reported in the output.
In addition to that the "logMessage" can be used for users to log any messages in the output.
29

34.
logMessage "Found ${actual.size()} ${issueTypeLabel} for owner " + issueOwner
TelluriumDataDrivenTest is the class users should extend to run the actual data driven testing and it is more
like a data driven testing engine. There is only one method, "testDataDriven", which users need to implement.
The sequence diagram for the testing process is shown as follows.
Usually, users need to do the following steps:
1. use "includeModule" to load defined Modules
2. use "loadData" or "useData" to load input data stream
3. use "stepToEnd" to read the input data line by line and pick up the specified test and run it, until
reaches the end of the data stream
4. use "closeData" to close the data stream and output the test results
What the "includeModule" does is to merge in all Ui modules, FieldSets, and tests defined in that module file
to the global registry. "stepToEnd" will look at each input line, first to find the test name and pass in all input
parameters to it, and then run the test. The whole process can be illustrated in the following example:
class GoogleBookListCodeHostTest extends TelluriumDataDrivenTest{
public void testDataDriven() {
includeModule example.google.GoogleBookListModule.class
includeModule example.google.GoogleCodeHostingModule.class
//load file
loadData "src/test/example/test/ddt/GoogleBookListCodeHostInput.txt"
//read each line and run the test script until the end of the file
stepToEnd()
30

35.
//close file
closeData()
}
}
The input data for this example are as follows,
##TEST should be always be the first column
##Data for test "checkBookList"
##TEST | CATEGORY | SIZE
checkBookList|Fiction|8
checkBookList|Fiction|3
##Data for test "getGCHStatus"
##TEST | LABEL | Row Number | Column Number
getGCHStatus |Example project labels:| 3 | 6
getGCHStatus |Example project| 3 | 6
##Data for test "clickGCHLabel"
##TEST | row | column
clickGCHLabel | 1 | 1
clickGCHLabel | 2 | 2
clickGCHLabel | 3 | 3
Note that line starting with "##" is comment line and empty line will be ignored.
If some times, users want to control the testing execution flow by themselves, Tellurium also provides this
capability even though it is not recommended. Tellurium provides two additional commands, i.e., "step" and
"stepOver". "step" is used to read one line of input data and run it, and "stepOver" is used to skip one line of
input data. In this meanwhile, Tellurium also allows you to specify additional test script using closure. For
example,
step{
//bind variables
boolean regularSearch = bind("regularSearch")
def phoneNumber = bind("fs4googlesearch.phoneNumber")
String input = bind("input")
openUrl "http://www.google.com"
type "google_start_page.searchbox", input
pause 500
click "google_start_page.googlesearch"
waitForPageToLoad 30000
}
But this usually implies that the input data format is unique or the test script knows about what format the
current input data are using.
2.15 The Dump Method
Tellurium Core added a dump method to print out the UI object's and its descendants' runtime locators that
Tellurium Core generates for you.
public void dump(String uid)
31

36.
where the uid is the UI object id you want to dump. The cool feature of the dump() method is that it does not
require you to run the actual tests, that is to say, you do not need to run selenium server and do not need to
launch the web browser. Just simply define the UI module and then call the dump() method.
For example, we can define the UI module for Tellurium Issue Search module as follows,
public class TelluriumIssueModule extends DslContext {
public void defineUi() {
//define UI module of a form include issue type selector and issue search
ui.Form(uid: "issueSearch", clocator: [action: "list", method: "get"], group: "true"){
Selector(uid: "issueType", clocator: [name: "can", id: "can"])
TextBox(uid: "searchLabel", clocator: [tag: "span", text: "*for"])
InputBox(uid: "searchBox", clocator: [type: "text", name: "q"])
SubmitButton(uid: "searchButton", clocator: [value: "Search"])
}
}
}
Then you can use the dump method in the following way,
TelluriumIssueModule tisp = new TelluriumIssueModule();
tisp.defineUi();
tisp.dump("issueSearch");
The above code will print out the runtime XPath locators
Dump locator information for issueSearch
-------------------------------------------------------
issueSearch: //descendant-or-self::form[@action="list" and @method="get"]
issueSearch.issueType: //descendant-or-self::form[descendant::select[@name="can" and
@id="can"] and descendant::span[contains(text(),"for")] and
descendant::input[@type="text" and @name="q"] and
descendant::input[@value="Search" and @type="submit"] and
@action="list" and @method="get"]/descendant-or-self::select
[@name="can" and @id="can"]
issueSearch.searchLabel: //descendant-or-self::form[descendant::select[@name="can"
and @id="can"] and descendant::span[contains(text(),"for")] and
descendant::input[@type="text" and @name="q"] and descendant::input[
@value="Search" and @type="submit"] and @action="list" and @method="get"]
/descendant-or-self::span[contains(text(),"for")]
issueSearch.searchBox: //descendant-or-self::form[descendant::select[@name="can"
and @id="can"] and descendant::span[contains(text(),"for")] and
descendant::input[@type="text" and @name="q"] and descendant::input[
@value="Search" and @type="submit"] and @action="list" and @method="get"]
/descendant-or-self::input[@type="text" and @name="q"]
issueSearch.searchButton: //descendant-or-self::form[descendant::select[@name="can"
and @id="can"] and descendant::span[contains(text(),"for")] and
descendant::input[@type="text" and @name="q"] and descendant::input[
@value="Search" and @type="submit"] and @action="list" and @method="get"]
/descendant-or-self::input[@value="Search" and @type="submit"]
-------------------------------------------------------
32

37.
2.16 Selenium Grid Support
Selenium Grid transparently distributes your tests on multiple machines so that you can run your tests in
parallel. We recently added support for Selenium Grid to the Tellurium. Now you can run the Tellurium tests
against different browsers using Selenium Grid. Tellurium core is updated to support Selenium Grid sessions.
Assume we have 3 machines set up to run the Tellurium tests on Selenium Grid. You can do all these steps on
your local box. To do this locally remove the machine names with localhost. Each machine in this set up has a
defined role as described below.
dev1.tellurium.com Tellurium test development machine.
hub.tellurium.com Selenium Grid hub machine that will drive the tests.
rc.tellurium.com Multiple Selenium RC server running and registered to the Selenium Grid HUB. The actual
test execution will be done on this machine. You can register as many Selenium RC servers as you wish. You
need to be realistic about the hardware specification though.
Download Selenium Grid from the following URL and extract the contents of the folder on each of these
machines.
We use Selenium Grid 1.0.3 which is the current released version.
http://selenium-grid.seleniumhq.org/download.html
Following is the illustration of the environment.
33

38.
The first step would be to launch the selenium grid hub on the hub machine. Open up a terminal on the HUB
machine hub.tellurium.com and go to the download directory of Selenium Grid.
> cd /Tools/selenium-grid-1.0.3
> ant launch-hub
You will notice that it has launched the Selenium HUB on the machine with different browsers. To ensure that
the HUB is working properly go to the following location.
http://hub.tellurium.com:4444/console
You can see a web page with 3 distinct columns as Configured Environments, Available Remote Controls,
Active Remote Controls
You will have a list of browsers that are configured by default to run the tests while the list for Available
Remote Controls and Avtive Remote Controls will be empty.
The next step would be to launch the Selenium RC servers and register them with the selenium HUB. Open
up a terminal on rc.tellurium.com and go to the selenium grid download directory.
> cd /Tools/selenium-grid-1.0.3
> ant -Dport=5555 -Dhost=rc.tellurium.com -DhubURL=http://hub.tellurium.com:4444
-Denvironment="Firefox on Windows" launch-remote-control
This command will start a Selenium RC server on this machine and register it with the Selenium Grid hub
machine as specified by the hubURL.
To register another Selenium RC server on this machine for internet explorer do the same on a different port.
> cd /Tools/selenium-grid-1.0.3
> ant -Dport=5556 -Dhost=rc.tellurium.com -DhubURL=http://hub.tellurium.com:4444 -Denvironment=
• port will that the remote control will be listening at. Must be unique on the machine the remote
control runs on.
• hostname Hostname or IP address of the machine the remote control runs on. Must be visible from the
Hub machine.
• hub url Which hub the remote control should register/unregister to. as the hub is running on hostname
hub.tellurium.com, this URL will be http://hub.tellurium.com:4444
Once you are successful in replicating a setup similar to the one described above, point your browser to the
Hub console (http://hub.tellurium.com:4444/console). Check that all the remote controls did register properly.
Available remote controls list should be updated and have these 2 selenium servers available to run the tests.
Now you have started the selenium hub and the selenium rc servers on the Grid environment, the next step
would be to run the Tellurium tests against different browsers.
Go to the Tellurium test development machine which in our case is dev1.tellurium.com.
Open up the TelluriumConfig.groovy and change the values of the selenium server and port to make sure that
tellurium requests for the new sessions from Selenium HUB and then Selenium HUB can point tellurium tests
34

39.
to be run on rc.tellurium.com based on the browser of choice.
You need to change the values for the following properties.
• runInternally: ensures that you do not launch the Selenium Server on the local machine.
• serverHost: the selenium grid hub machine that has the information about the available selenium rc
servers.
• port: port that Selenium HUB is running on, by default this port is 4444 but you can change that in the
grid_configuraton.yml file if this port is not available on your HUB machine.
• browser: the browser that comes under the configured environments list on the selenium HUB
machine, you can change these values to your own choice in the grid_configuration.yml file.
tellurium{
//embedded selenium server configuration
embeddedserver {
//port number
port = "4444"
//whether to use multiple windows
useMultiWindows = false
//whether to run the embedded selenium server.
//If false, you need to manually set up a selenium server
runInternally = false
//profile location
profile = ""
//user-extension.js file
userExtension = "target/classes/extension/user-extensions.js"
}
//event handler
eventhandler{
//whether we should check if the UI element is presented
checkElement = false
//wether we add additional events like "mouse over"
extraEvent = true
}
//data accessor
accessor{
//whether we should check if the UI element is presented
checkElement = true
}
//the configuration for the connector that connects the selenium client
//to the selenium server
connector{
//selenium server host
//please change the host if you run the Selenium server remotely
serverHost = "hub.tellurium.com"
//server port number the client needs to connect
port = "4444"
//base URL
baseUrl = "http://localhost:8080"
//Browser setting, valid options are
// *firefox [absolute path]
// *iexplore [absolute path]
// *chrome
// *iehta
browser = "Firefox on Windows"
//user's class to hold custom selenium methods associated with user-extensions.js
//should in full class name, for instance, "com.mycom.CustomSelenium"
customClass = "org.tellurium.test.MyCommand"
}
35