|
UserGuide070TelluriumBasics
(A PDF version of the user guide is available here)
Tellurium BasicsUiID AttributeIn Tellurium, the UI object is referred to by its UiID, i.e., the UI object identifier. For nested Ui objects, the UiID of the UI Object is a concatenated UI objects' uids along its path to the UI Object. For example, in the following nested UI Module shown below, the TextBox is referred to as the "parent_ui.child_ui.grand_child.textbox1". 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: "...")
...
}
}
}The exceptions are tables and lists, which use [x][y] or [x] to reference the elements inside. For example, labels_table[2][1] and GoogleBooksList.subcategory[2]. The Table header can be referred in the format of issueResult.header[2]. More general cases are shown in Figure 2-3 General Cases Search Example: ui.Form(uid: "A", clocator: [:]){
InputBox(uid: "B", clocator: [:])
Container(uid: "C", clocator: [tag: "div"]){
Selector(uid: "D", clocator: [:])
List(uid: "E", clocator: [tag: "ul"], separator: "li"){
UrlLink(uid: "{1} as Link", clocator: [:])
InputBox(uid: "{all}", clocator: [:])
}
}
}Figure 2-3: General Cases Search Example
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. For example: A.C.En. Locator AttributesTellurium supports two types of UI Object locators:
The Base locator is a relative XPath. The Composite locator, denoted by "clocator", specifies a set of attributes for the UI object. The actual locator is 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, 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 there are additional attributes, they are defined in the same way as the default attributes. For example: 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 are used. In other words, if the default attribute values of a Tellurium UI object are known, omit them in clocator. For example, if the RadioButton Object’s default tag is "input", and the default type is "radio", omit them and write the clocator as follows: clocator: [:] which is equivalent to: clocator: [tag: "input", type: "radio"] Group Attribute: Group LocatorIn the Tellurium UI module, the "group" attribute is seen often. 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"])
}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] Sibling node information is not used in this example as the XPath depends too much on information from nodes far away from the target node. In Tellurium, every effort is made to localize the information and reduce this dependency by using sibling information or local information. For example, in the above google UI module example, the group locator concept searches for the location of the "td" tag with its children as "InputBox", "googlesearch" button, and "Imfeelinglucky" button. In this way, the dependencies of the UI elements inside a UI module on external UI elements are reduced, making the UI definition more robust. self attributeSome times, elements inside a Table usually are inside its parent tag, for instance, we have the following HTML source. <div id="table">
<div>
<div id="name">
<div>Data</div>
<div>
<img/>
</div>
</div>
<div id="shortname">
<div>Bezeichnung</div>
<div>
<img/>
</div>
</div>
<div id="type">
<div>Typ</div>
<div>
<img/>
</div>
</div>
</div>
<div id="client-area">
<div>
<div>Bildsystem</div>
<div>Bildsystem</div>
<div>Bildserver</div>
</div>
<div>
<div>Partner</div>
<div>Partner</div>
<div>Bestandssystem</div>
</div>
<div>
<div>MS</div>
<div>MS</div>
<div>MS</div>
</div>
</div>
</div>where the "Data" element and many others are inside its parent tag "div". To module this, we added a self attribute to the UiObject class and the default is "false". To describe the above html, we can define the following UI module. ui.StandardTable(uid: "Table", clocator: [id: "table"], bt: "div", brt: "div", bct: "div"){
TextBox(uid: "{tbody: 1, row: all, column: 1}", clocator: [tag: "div"], self: "true")
Image(uid: "{tbody: 1, row: all, column: 2}", clocator: [:])
TextBox(uid: "{tbody: 2, row: all, column: 1}", clocator: [tag: "div"], self: "true")
TextBox(uid: "{tbody: 2, row: all, column: 2}", clocator: [tag: "div"], self: "true")
TextBox(uid: "{tbody: 2, row: all, column: 3}", clocator: [tag: "div"], self: "true")
}To test the UI module, you can simply call the following api. getHTMLSource("Table");Be aware that the self can be "true" ONLY for UI elements inside a List, a Table, or a StandardTable Object. Respond Attribute: JavaScript EventsTellurium provides a "respond" attribute used to define any event requiring the UI object to respond. Most web applications include Javascript, and thus the web testing framework must be able to handle Javascript events. What is important 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()'> Alternately one can define the radio button as follows: RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779']) The above code does not respond to the Click event as the Tellurium RadioButton only supports the "check" and "uncheck" actions. This is enough for the normal case. As a result, no "click" event/action is generated during testing. To address this problem, Tellurium added the "respond" attribute to Tellurium UI objects. The "respond" attribute is used to define any event requiring the UI object to respond. The Radio Button is redefined as: ui.Container(uid: "form", clocator: [whatever]){
RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779'],
respond: ["click"])
}Then 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. Be aware, you have to explicitly call the click method and Event Handler will not automatically trigger the click event. A more general example is: 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 are automatically fired by Tellurium during testing. Do not worry about the event order for the respond attribute as Tellurium automatically re-orders the events and then processes them appropriately. That is to say, the Event Handler will automatically trigger these events. CSS SelectorUse the CSS Selector to improve the test speed in IE as IE lacks native XPath support and the XPath is slow. Tellurium exploits CSS selector capability to improve test speed dramatically. Tellurium supports both XPath and CSS selector and still uses CSS selector as the default locator. New DSL MethodsCSS Selector provides the following additional Selenium methods, utilized in DslContext to form a set of new DSL methods: String getAllText(String locator): 2. Get the CSS properties for the set of elements corresponding to the CSS Selector. Number getJQuerySelectorCount(String locator) : 3. Get the number of elements matching the corresponding CSS Selector. Additional CSS Attribute SelectorsjQuery also supports the following attribute selectors: attribute: have the specified attribute. attribute=value: have the specified attribute with a certain value. attribute!=value: either do not have the specified attribute or 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. Locator Agnostic MethodsApart from the above, Tellurium provides a set of locator agnostic methods. For example, the method automatically decides to use XPath or jQuery dependent on the exploreJQuerySelector flag, which can be turned on and off by the following two methods:
Tellurium also provides the corresponding XPath specific and CSS selector specific methods for your convenience. However, it is recommended that you use the locator agnostic methods until there is a good reason not to. The new XPath and CSS Selector specific methods are as follows: 1. Get the Generated locator from the UI module Locator agnostic: String getLocator(String uid) CSS selector specific: String getSelector(String uid) XPath specific: String getXPath(String uid) 2. Get the Number of Elements matching the Locator Locator agnostic: Number getLocatorCount(String locator) CSS selector specific: Number getJQuerySelectorCount(String jQuerySelector) XPath specific: Number getXpathCount(String xpath) 3. Table Methods Locator agnostic: int getTableHeaderColumnNum(String uid) int getTableFootColumnNum(String uid) int getTableMaxRowNum(String uid) int getTableMaxColumnNum(String uid) int getTableMaxRowNumForTbody(String uid, int ntbody) int getTableMaxColumnNumForTbody(String uid, int ntbody) int getTableMaxTbodyNum(String uid) CSS selector specific: int getTableHeaderColumnNumBySelector(String uid) int getTableFootColumnNumBySelector(String uid) int getTableMaxRowNumBySelector(String uid) int getTableMaxColumnNumBySelector(String uid) int getTableMaxRowNumForTbodyBySelector(String uid, int ntbody) int getTableMaxColumnNumForTbodyBySelector(String uid, int ntbody) int getTableMaxTbodyNumBySelector(String uid) XPath specific: int getTableHeaderColumnNumByXPath(String uid) int getTableFootColumnNumByXPath(String uid) int getTableMaxRowNumByXPath(String uid) int getTableMaxColumnNumByXPath(String uid) int getTableMaxRowNumForTbodyByXPath(String uid, int ntbody) int getTableMaxColumnNumForTbodyByXPath(String uid, int ntbody) int getTableMaxTbodyNumByXPath(String uid) 4. Verify if an Element is Disabled Locator agnostic: boolean isDisabled(String uid) CSS selector specific: boolean isDisabledBySelector(String uid) XPath specific: boolean isDisabledByXPath(String uid) 5. Get the Attribute Locator agnostic: def getAttribute(String uid, String attribute) 6. Check the CSS Class Locator agnostic def hasCssClass(String uid, String cssClass) 7. Get CSS Properties CSS selector specific: String[] getCSS(String uid, String cssName) 8. Get All Data from a Table CSS selector specific: String[] getAllTableCellText(String uid) String[] getAllTableCellTextForTbody(String uid, int index) 9. Get List Size Locator agnostic: int getListSize(String uid) CSS selector specific: getListSizeBySelector(String uid) XPath specific: getListSizeByXPath(String uid) There are issues to be aware of with CSS Selector:
UI TemplatesTellurium UI templates have two purposes:
More specifically, Table and List are two Tellurium objects that define UI templates.
The Template has special UIDs such as "2", "all", or "row: 1, column: 2". Looking at use case (1), the HTML source is: <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>In this example there are six links. Without templates, one would put six UrlLink objects in the UI module. By using the templates, the work is easier and simplified. ui.List(uid: "list", clocator: [tag: "ul", class: "a"], separator:"li")
{
UrlLink(uid: "{all}", clocator: [class: "b"])
}For use case (2), a common application is the data grid. Look at the "issueResult" data grid on the Tellurium Issues page for an easy and simplified result as shown below: ui.Table(uid: "issueResult", clocator: [id: "resultstable", class: "results"],
group: "true")
{
TextBox(uid: "{header: 1", clocator: [:])
UrlLink(uid: "{header: 2} as ID", clocator: [text: "*ID"])
UrlLink(uid: "{header: 3} as Type", clocator: [text: "*Type"])
UrlLink(uid: "{header: 4} as Status", clocator: [text: "*Status"])
UrlLink(uid: "{header: 5} as Priority", clocator: [text: "*Priority"])
UrlLink(uid: "{header: 6} as Milestone", clocator: [text: "*Milestone"])
UrlLink(uid: "{header: 7} as Owner", clocator: [text: "*Owner"])
UrlLink(uid: "{header: 9} as Summary", clocator: [text: "*Summary + Labels"])
UrlLink(uid: "{header: 10} as Extra", clocator: [text: "*..."])
//define table elements
//for the border column
TextBox(uid: "{row: all, column: 1}", clocator: [:])
//For the rest, just UrlLink
UrlLink(uid: "{row: all, column: all}", clocator: [:])
}The resulting definitions shown are very simple, time-saving and easy to use. If the user has multiple templates such as the "issueResult" shown in the table above, the rule generally applied to the templates is: "specific one first, general one later". "Include" Frequently Used Sets of Elements in UI ModulesWhen there is a frequently used set of elements, re-defining them repeatedly in your UI module is not necessary. Simply use the Tellurium "Include" syntax to re-use the pre-defined UI elements. Include(uid: UID, ref: REFERRED_UID) Use "ref" to reference the object to be included, then specify the UID for the object. Note: If a different UID is required, there is no need to specify it. If the Object UID is not the same as the original one, Tellurium clones a new object for users so that multiple objects with different UIDs are available. For example, first define the following reused UI module: ui.Container(uid: "SearchModule", 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"])
}Then, include it into the new UI module as follows: ui.Container(uid: "Google", clocator: [tag: "table"]) {
Include(ref: "SearchModule")
Include(uid: "MySearchModule", ref: "SearchModule")
Container(uid: "Options", clocator: [tag: "td", position: "3"], group: "true") {
UrlLink(uid: "LanguageTools", clocator: [tag: "a", text: "Language Tools"])
UrlLink(uid: "SearchPreferences", clocator: [tag: "a", text: "Search Preferences"])
UrlLink(uid: "AdvancedSearch", clocator: [tag: "a", text: "Advanced Search"])
}
}Javascript EventsMost web applications include Javascript, and thus the web testing framework must be able to handle Javascript events. What is important 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, the user has the following radio button: <input type='radio' name='mas_address_key' value='5779' onClick='SetAddress_5779()'> Alternately one can define the radio button as follows: RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779']) The above code does not respond to the Click event as the Tellurium RadioButton only supports the "check" and "uncheck" actions. This is enough for the normal case. As a result, no "click" event/action is generated during testing. To address this problem, Tellurium added the "respond" attribute to Tellurium UI objects. The "respond" attribute is used to define any events requiring the UI object to respond. The Radio Button can be redefined as: ui.Container(uid: "form", clocator: [whatever]){
RadioButton(uid: "billing", clocator: [name: 'mas_address_key', value: '5779'], respond: ["click"])
}Then 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: 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 are automatically fired by Tellurium during testing. Do not worry about the event order for the respond attribute as Tellurium automatically re-orders the events and then processes them appropriately. Logical ContainerThe 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, the Logical Container (or Virtual Container - Logical Container is preferred) can violate this rule. What is a Logic Container? It is a Container with an empty locator. For instance: Container(uid: "logical"){
......
}But empty != nothing. There are some scenarios where the Logical Container can play an important role. The Container includes an uid for a reference and it logically groups the UI element together. For example, in the following example the UI elements under the Tag li are different: <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>
<li>
similar UI
</li>
<li>
similar UI
</li>
</ul>
</div> The issue is how to write the UI template for them. The logical Container then comes into play. For example, the UI module is written as follows: 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. For example, using the search UI on the Tellurium download page, first record 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");
}
}Do not be confused by the locator "can" and "q", as they are UI element IDs and are easily expressed in XPath. The "label=regexp:\\sAll Downloads" part shows that Selenium uses regular express to match the String and the "\s" stands for a space. As a result, 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
keyType "downloads.input", searchKeyWords
click "downloads.search"
waitForPageToLoad 30000
}
}The Tellurium test case is created 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");
}
}toggleTellurium 0.7.0 provides a toggle method to animate the UI element on the web page. For example, you can the following commands to show the UI element under testing. toggle "Form.Username.Input"
pause 500
toggle "Form.Username.Input"getHTMLSourceUse getHTMLSource, users can get back the runtime html source for a UI module. Tellurium provided two methods for this purpose. public Map getHTMLSourceResponse(String uid); public void getHTMLSource(String uid); The first method get back the html source as a uid-to-html map and the second one simply print out the html source on the console. Be aware, getHTMLSource is only available in Tellurium new APIs. Example: @Test
public void testGetHTMLSource(){
useEngineLog(true);
useTelluriumApi(true);
useCache(true);
connect("JettyLogon");
jlm.getHTMLSource("Form");
}and the output is as follows: Form:
<form method="POST" action="j_security_check">
<table border="0" cellpadding="1" cellspacing="2">
<tbody><tr>
<td>Username:</td>
<td><input size="12" value="" name="j_username" maxlength="25" type="text"></td>
</tr>
<tr>
<td>Password:</td>
<td><input size="12" value="" name="j_password" maxlength="25" type="password"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input name="submit" value="Login" type="submit">
</td>
</tr>
</tbody></table>
</form>
Form.Username:
<tr>
<td>Username:</td>
<td><input size="12" value="" name="j_username" maxlength="25" type="text"></td>
</tr>
Form.Username.Label:
<td>Username:</td>
Form.Username.Input:
<input size="12" value="" name="j_username" maxlength="25" type="text">
Form.Password:
<tr>
<td>Password:</td>
<td><input size="12" value="" name="j_password" maxlength="25" type="password"></td>
</tr>
Form.Password.Label:
<td>Password:</td>
Form.Password.Input:
<input size="12" value="" name="j_password" maxlength="25" type="password">
Form.Submit:
<input name="submit" value="Login" type="submit">type and keyTypetype and keyType now accept different types of objects and convert them to a String automatically by calling the toString() method. For example, you can use the following commands: type "Google.Input", "Tellurium"
type "Google.Input", 12.15
type "Google.Input", trueCustomize Individual Test Settings Using setCustomConfigTelluriumConfig.groovy provides project level test settings. Use setCustomConfig to customize individual test settings. public void setCustomConfig(boolean runInternally, int port, String browser,
boolean useMultiWindows, String profileLocation)
public void setCustomConfig(boolean runInternally, int port, String browser,
boolean useMultiWindows, String profileLocation, String serverHost)For example, specify custom settings as follows: public class GoogleStartPageJavaTestCase extends TelluriumJavaTestCase {
static{
setCustomConfig(true, 5555, "*chrome", true, null);
}
protected static NewGoogleStartPage ngsp;
@BeforeClass
public static void initUi() {
ngsp = new NewGoogleStartPage();
ngsp.defineUi();
ngsp.useJavascriptXPathLibrary();
}
@Test
public void testGoogleSearch() {
connectUrl("http://www.google.com");
ngsp.doGoogleSearch("tellurium selenium Groovy Test");
}
......
}User Custom Selenium ExtensionsTo support user custom selenium extensions, Tellurium Core adds the following configurations to TelluriumConfig.groovy. embeddedserver {
......
//user-extension.js file, for example "target/user-extensions.js"
userExtension = ""
}
connector{
......
//user's class to hold custom selenium methods associated with user-extensions.js
//should in full class name, for instance, "com.mycom.CustomSelenium"
customClass = ""
}Where the userExtension points to the user's user-extensions.js file. For example, use the following user-extensions.js: Selenium.prototype.doTypeRepeated = function(locator, text) {
// All locator-strategies are automatically handled by "findElement"
var element = this.page().findElement(locator);
// Create the text to type
var valueToType = text + text;
// Replace the element text with the new text
this.page().replaceText(element, valueToType);
};The customClass is the user's class for custom Selenium methods, extending Tellurium org.telluriumsource.component.connector.CustomCommand class: public class MyCommand extends CustomCommand{
public void typeRepeated(String locator, String text){
String[] arr = [locator, text];
commandProcessor.doCommand("typeRepeated", arr);
}
}
}Tellurium core automatically loads up user-extensions.js and custom commands. The n, user uses: customUiCall(String uid, String method, Object[] args) to call the custom methods. For instance: 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 the Selenium server. customDirectCall(String method, Object[] args) The Dump MethodTellurium Core added a dump method to print out the UI object's and its descendants' runtime locators that Tellurium Core generates. public void dump(String uid) where the uid is the UI object id to be dumped. An important feature is that the dump() method does not require the user to run the actual tests. That is to say, the user does not need to run the Selenium server and launch the web browser. Simply define the UI module, then call the dump() method. For example, to define the UI module for Tellurium Issue Search module, complete 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"])
}
}
}The user can use the dump method in the following way: TelluriumIssueModule tisp = new TelluriumIssueModule();
tisp.defineUi();
tisp.dump("issueSearch");The above code prints out the runtime XPath locators. Dump locator information for issueSearch is as follows: -------------------------------------------------------
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"]
-------------------------------------------------------Engine State Offline UpdateFor some reasons, you may need to make Engine state calls before you actually connect to the Engine. For examples, call one of the following methods. public void enableCache(); public void disableCache(); public void useTelluriumApi(boolean isUse); public void useClosestMatch(boolean isUse); We added an Engine State tracer in the bundle tier to record all the requests if the Engine is not connected and aggregate them. Once the Engine is connected, Tellurium will automatically send out the Engine state update request. For example, the following test code works fine in 0.7.0. @Test
public void testOfflineEngineStateUpdate(){
JettyLogonModule jlm = new JettyLogonModule();
jlm.defineUi();
useCssSelector(true);
useTrace(true);
//Engine state offline update
useTelluriumApi(true);
useCache(true);
connectSeleniumServer();
connectUrl("http://localhost:8080/logon.html");
jlm.logon("test", "test");
//Back to state online update
useClosestMatch(true);
connectUrl("http://localhost:8080/logon.html");
jlm.plogon("test", "test");
}Testing SupportIn the package org.telluriumsource.test, Tellurium provides three different ways to write Tellurium tests:
Tellurium JUnit Test CaseUsed for JUnit, and supports the following JUnit 4 annotations: JUnit is upgraded to 4.7 since it provides more features. One such a good feature is the Rule annotation. To be consistent with TestNG test case, the class TelluriumJavaTestCase is deprecated and you should use TelluriumJUnitTestCase instead.
Tellurium TestNG Test CaseUsed for TestNG. Similarly, using the following annotations:
TelluriumTestNGTestCase was changed to allow the setup and teardown procedures only work once for all the tests. The magic are the @BeforeTest and @AfterTest annotations. See the following code for more details, public abstract class TelluriumTestNGTestCase extends BaseTelluriumJavaTestCase {
@BeforeTest(alwaysRun = true)
public static void setUpForTest() {
tellurium = TelluriumSupport.addSupport();
tellurium.start(customConfig);
connector = (SeleniumConnector) tellurium.getConnector();
}
@AfterTest(alwaysRun = true)
public static void tearDownForTest() {
if(tellurium != null)
tellurium.stop();
}Tellurium Groovy Test CaseUsed for Groovy test cases. Data Driven Testing: Tellurium provides the class TelluriumDataDrivenModule for users to define data driven testing modules. Class TelluriumDataDrivenTest is used to drive the actual tests. Tellurium also provides users the capability of writing Tellurium tests and Tellurium data driven tests in pure DSL scripts. The DslScriptExecutor is used to run the .dsl files. TelluriumMockJUnitTestCase and TelluriumMockTestNGTestCaseTelluriumMockJUnitTestCase and TelluriumMockTestNGTestCase incorporated the Mock Http Server so that you can load up a html web page and test against it with minimal configuration. One example is as follows, public class JettyLogonJUnitTestCase extends TelluriumMockJUnitTestCase {
private static JettyLogonModule jlm;
@BeforeClass
public static void initUi() {
registerHtmlBody("JettyLogon");
jlm = new JettyLogonModule();
jlm.defineUi();
useCssSelector(true);
useTelluriumApi(true);
useTrace(true);
useCache(true);
}
@Before
public void connectToLocal() {
connect("JettyLogon");
}
@Test
public void testJsonfyUiModule(){
String json = jlm.jsonify("Form");
System.out.println(json);
}
@AfterClass
public static void tearDown(){
showTrace();
}
}where the html file "JettyLogon" lives in the class path "org/telluriumsource/html". You can change this by the following method: public static void setHtmlClassPath(String path); The implementation is simple as shown as follows. public class TelluriumMockJUnitTestCase extends TelluriumJUnitTestCase {
protected static MockHttpServer server;
@BeforeClass
public static void init(){
server = new MockHttpServer(8080);
server.start();
connectSeleniumServer();
}
public static void registerHtml(String testname){
server.registerHtml("/" + testname + ".html", server.getHtmlFile(testname));
}
public static void registerHtmlBody(String testname){
server.registerHtmlBody("/" + testname + ".html", server.getHtmlFile(testname));
}
public static void setHtmlClassPath(String path){
server.setHtmlClassPath(path);
}
public static void connect(String testname){
connectUrl("http://localhost:8080/" + testname + ".html");
}
@AfterClass
public static void tearDown(){
server.stop();
}
}Tellurium ConfigurationThe configuration parser has been refactored. The configuration file name is stored in the Environment class as follows. @Singleton
public class Environment implements Configurable{
protected String configFileName = "TelluriumConfig.groovy";
protected String configString = "";
public static Environment getEnvironment(){
return Environment.instance;
}
public void useConfigString(String json){
this.configString = json;
}
...
That is to say, you can get the Environment singleton instance and change the file name before Tellurium core is loaded up if you have a good reason to do that. In the meanwhile, we add support to load the configuration from a JSON string, which will be stored in the configString} variable in the Environment. If the configString variable is not null or empty, Tellurium core will honor the JSON string and will ignore the configuration file. One way to use the JSON string is illustrated by the following example. public class JettyLogonJUnitTestCase extends TelluriumMockJUnitTestCase {
private static JettyLogonModule jlm;
static{
Environment env = Environment.getEnvironment();
env.useConfigString(JettyLogonModule.JSON_CONF);
}
...
where variable JSON_CONF is the JSON configuration string. public class JettyLogonModule extends DslContext {
public static String JSON_CONF = """{"tellurium":{"test":{"result":{"reporter":"XMLResultReporter","filename":"TestResult.output","output":"Console"},"exception":{"filenamePattern":"Screenshot?.png","captureScreenshot":false},"execution":{"trace":false}},"accessor":{"checkElement":false},"embeddedserver":{"port":"4444","browserSessionReuse":false,"debugMode":false,"ensureCleanSession":false,"interactive":false,"avoidProxy":false,"timeoutInSeconds":30,"runInternally":true,"trustAllSSLCertificates":true,"useMultiWindows":false,"userExtension":"","profile":""},"uiobject":{"builder":{}},"eventhandler":{"checkElement":false,"extraEvent":false},"i18n":{"locale":"en_US"},"connector":{"baseUrl":"http://localhost:8080","port":"4444","browser":"*chrome","customClass":"","serverHost":"localhost","options":""},"bundle":{"maxMacroCmd":5,"useMacroCommand":true},"datadriven":{"dataprovider":{"reader":"PipeFileReader"}},"widget":{"module":{"included":""}}}}""";
...
}If we want a pretty look of the JSON String, we can use JSON Visualization to format it as follows. {
"tellurium": {
"test": {
"result": {
"reporter": "XMLResultReporter",
"filename": "TestResult.output",
"output": "Console"
},
"exception": {
"filenamePattern": "Screenshot?.png",
"captureScreenshot": false
},
"execution": {
"trace": false
}
},
"accessor": {
"checkElement": false
},
"embeddedserver": {
"port": "4444",
"browserSessionReuse": false,
"debugMode": false,
"ensureCleanSession": false,
"interactive": false,
"avoidProxy": false,
"timeoutInSeconds": 30,
"runInternally": true,
"trustAllSSLCertificates": true,
"useMultiWindows": false,
"userExtension": "",
"profile": ""
},
"uiobject": {
"builder": { }
},
"eventhandler": {
"checkElement": false,
"extraEvent": false
},
"i18n": {
"locale": "en_US"
},
"connector": {
"baseUrl": "http://localhost:8080",
"port": "4444",
"browser": "*chrome",
"customClass": "",
"serverHost": "localhost",
"options": ""
},
"bundle": {
"maxMacroCmd": 5,
"useMacroCommand": true
},
"datadriven": {
"dataprovider": {
"reader": "PipeFileReader"
}
},
"widget": {
"module": {
"included": ""
}
}
}
}
If the JSON String is empty or null, Tellurium loads the configuration file TelluriumConfig.groovy first from the project root. If not found, it loads it from the class path. As a result, you can put the TelluriumConfig.groovy file under the resources directory if you use Maven. The configuration file TelluriumConfig.groovy includes three extra sections. That is to say, the bundle tier, //the bundling tier
bundle{
maxMacroCmd = 5
useMacroCommand = false
}the i18n, i18n{
//locale = "fr_FR"
locale = "en_US"
}and the trace. test{
execution{
//whether to trace the execution timing
trace = false
} The sample configure file for 0.7.0 is available here. Tellurium Core 0.7.0 added support for Configuration file check as suggested by our user. That can be easily demonstrated by the following example, if we comment out the following section in TelluriumConfig.groovy //the bundling tier
// bundle{
// maxMacroCmd = 5
// useMacroCommand = false
// }What will happen? Tellurium will throw the following exception java.lang.ExceptionInInitializerError at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at org.telluriumsource.bootstrap.TelluriumSupport.class$(TelluriumSupport.groovy) at org.telluriumsource.bootstrap.TelluriumSupport.$get$$class$org$telluriumsource$framework$TelluriumFrameworkMetaClass(TelluriumSupport.groovy) at org.telluriumsource.bootstrap.TelluriumSupport.addSupport(TelluriumSupport.groovy:17) at org.telluriumsource.test.java.TelluriumTestNGTestCase.setUpForTest(TelluriumTestNGTestCase.java:22) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:607) at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:417) at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:154) at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:88) at org.testng.TestRunner.beforeRun(TestRunner.java:510) at org.testng.TestRunner.run(TestRunner.java:478) at org.testng.SuiteRunner.runTest(SuiteRunner.java:332) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:327) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:299) at org.testng.SuiteRunner.run(SuiteRunner.java:204) at org.testng.TestNG.createAndRunSuiteRunners(TestNG.java:867) at org.testng.TestNG.runSuitesLocally(TestNG.java:832) at org.testng.TestNG.run(TestNG.java:748) at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:73) at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:124) Caused by: org.telluriumsource.exception.ConfigNotFoundException: Cannot find Tellurium Configuration tellurium.bundle.maxMacroCmd, please check http://code.google.com/p/aost/wiki/TelluriumConfig070 for updated TelluriumConfig.groovy, or report to Tellurium user group at http://groups.google.com/group/tellurium-users at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77) at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:107) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:52) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:192) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:200) at org.telluriumsource.config.TelluriumConfigurator.checkConfig(TelluriumConfigurator.groovy:41) Run DSL ScriptTellurium 0.7.0 provides a rundsl.groovy script for users to run DSL test script. The rundsl.groovy uses Groovy Grape to automatically download all dependencies and then run DSL script. First, you need to configure Grape. Put the following grapeConfig.xml file into your home/.groovy/. <ivysettings>
<settings defaultResolver="downloadGrapes"/>
<property
name="local-maven2-pattern"
value="${user.home}/.m2/repository/[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]"
override="false" />
<resolvers>
<chain name="downloadGrapes">
<filesystem name="cachedGrapes">
<ivy pattern="${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml"/>
<artifact pattern="${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/>
</filesystem>
<filesystem name="local-maven-2" m2compatible="true" local="true">
<ivy pattern="${local-maven2-pattern}"/>
<artifact pattern="${local-maven2-pattern}"/>
</filesystem>
<!-- todo add 'endorsed groovy extensions' resolver here -->
<ibiblio name="kungfuters.3rdparty" root="http://maven.kungfuters.org/content/repositories/thirdparty/" m2compatible="true"/>
<ibiblio name="codehaus" root="http://repository.codehaus.org/" m2compatible="true"/>
<ibiblio name="ibiblio" m2compatible="true"/>
<ibiblio name="java.net2" root="http://download.java.net/maven/2/" m2compatible="true"/>
<ibiblio name="openqa" root="http://archiva.openqa.org/repository/releases/" m2compatible="true"/>
<ibiblio name="kungfuters.snapshot" root="http://maven.kungfuters.org/content/repositories/snapshots/" m2compatible="true"/>
<ibiblio name="kungfuters.release" root="http://maven.kungfuters.org/content/repositories/releases/" m2compatible="true"/>
</chain>
</resolvers>
</ivysettings>Then run groovy rundsl.groovy -f DSL_script_name If you are behind a firewall groovy -Dhttp.proxyHost=proxy_host -Dhttp.proxyPort=proxy_port rundsl.groovy -f DSL_script_name If you defined custom UI objects in your project, you should first build the jar artifact, and install it to your local Maven repo. Then update the rundsl.groovy file to add the new dependency. For example, in the tellurium-website reference project, we defined custom UI objects, we added the following two lines into the rundsl.groovy script. Grape.grab(group:'org.telluriumsource', module:'tellurium-website', version:'0.7.0-SNAPSHOT', classLoader:this.class.classLoader.rootLoader) ... @Grab(group='org.telluriumsource', module='tellurium-website', version='0.7.0-SNAPSHOT') An example rundsl.groovy script is as follows: import groovy.grape.Grape;
Grape.grab(group:'org.telluriumsource', module:'tellurium-core', version:'0.8.0-SNAPSHOT', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'org.stringtree', module:'stringtree-json', version:'2.0.10', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'caja', module:'json_simple', version:'r1', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'org.seleniumhq.selenium.server', module:'selenium-server', version:'1.0.1-te4-SNAPSHOT', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'org.seleniumhq.selenium.client-drivers', module:'selenium-java-client-driver', version:'1.0.1', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'org.apache.poi', module:'poi', version:'3.0.1-FINAL', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'junit', module:'junit', version:'4.7', classLoader:this.class.classLoader.rootLoader)
import org.telluriumsource.dsl.DslScriptExecutor
@Grapes([
@Grab(group='org.codehaus.groovy', module='groovy-all', version='1.7.0'),
@Grab(group='org.seleniumhq.selenium.server', module='selenium-server', version='1.0.1-te4-SNAPSHOT', transitive=false),
@Grab(group='org.seleniumhq.selenium.client-drivers', module='selenium-java-client-driver', version='1.0.1'),
@Grab(group='junit', module='junit', version='4.7'),
@Grab(group='caja', module='json_simple', version='r1'),
@Grab(group='org.apache.poi', module='poi', version='3.0.1-FINAL'),
@Grab(group='org.stringtree', module='stringtree-json', version='2.0.10'),
@Grab(group='org.telluriumsource', module='tellurium-core', version='0.8.0-SNAPSHOT'),
])
def runDsl(String[] args) {
def cli = new CliBuilder(usage: 'rundsl.groovy -[hf] [scriptname]')
cli.with {
h longOpt: 'help', 'Show usage information'
f longOpt: 'scriptname', 'DSL script name'
}
def options = cli.parse(args)
if (!options) {
return
}
if (options.h) {
cli.usage()
return
}
if (options.f) {
def extraArguments = options.arguments()
if (extraArguments) {
extraArguments.each {String name ->
def input = [name].toArray(new String[0])
DslScriptExecutor.main(input)
}
}
}
}
println "Running DSL test script, press Ctrl+C to stop."
runDsl(args)
useTelluriumEngineTo make it convenient for users, Tellurium provides a useTelluriumEngine command as follows, void useTelluriumEngine(boolean isUse){
useCache(isUse);
useMacroCmd(isUse);
useTelluriumApi(isUse);
} As you can see, this command actually consists of three commands. In DslContext, Tellurium also provides two handy DSL style methods. void enableTelluriumEngine(); void disableTelluriumEngine(); TraceTellurium 0.7.0 provides built-in support for the command execution time including
For example, you can see the output as follows, TE: Name: getCurrentCachePolicy, start: 1260496075484, duration: 28ms TE: Name: useDiscardNewCachePolicy, start: 1260496075514, duration: 51ms TE: Name: getCurrentCachePolicy, start: 1260496075566, duration: 74ms TE: Name: useDiscardOldCachePolicy, start: 1260496075642, duration: 35ms TE: Name: getCurrentCachePolicy, start: 1260496075678, duration: 42ms TE: Start Time: 1260496060373 End Time: 1260496075720 Total Runtime: 15347ms Name: keyPress, count: 24, total: 1277ms, average: 53ms Name: getCurrentCachePolicy, count: 5, total: 222ms, average: 44ms Name: useDiscardOldCachePolicy, count: 1, total: 35ms, average: 35ms Name: useDiscardInvalidCachePolicy, count: 1, total: 33ms, average: 33ms Name: enableCache, count: 2, total: 151ms, average: 75ms Name: click, count: 3, total: 194ms, average: 64ms Name: isElementPresent, count: 2, total: 100ms, average: 50ms Name: useDiscardLeastUsedCachePolicy, count: 1, total: 39ms, average: 39ms Name: type, count: 1, total: 81ms, average: 81ms Name: typeKey, count: 3, total: 124ms, average: 41ms We added the following settings to TelluriumConfig.groovy test{
execution{
//whether to trace the execution timing
trace = false
}
You can use the follow methods in DslContext to turn on or off the trace, and get the trace data. public void enableTrace(); public void disableTrace(); public void showTrace(); Methods Accessible in Test CasesThere are many Tellurium APIs that used to be available only in DslContext. That is to say, you have to extend DslContext to use them. For example, you often see code in a test case likes this, GoogleSearchModule gsm = new GoogleSearchModule();
gsm.defineUi();
gsm.usejQuerySelector();
gsm.registerNamespace("te", te_ns);Now, many of them, which are not really tied to a specific UI module, are made available in Tellurium test cases. For example, the above code can be changed as follows, GoogleSearchModule gsm = new GoogleSearchModule();
gsm.defineUi();
useCssSelector();
registerNamespace("te", te_ns);New TelluriumGroovyTestCase provides the following list of new APIs for your convenience. public void useCssSelector(boolean isUse);
public void useCache(boolean isUse);
public void cleanCache();
public boolean isUsingCache();
public void setCacheMaxSize(int size);
public int getCacheSize();
public int getCacheMaxSize();
public Map<String, Long> getCacheUsage();
public void useCachePolicy(CachePolicy policy);
public String getCurrentCachePolicy();
public void useDefaultXPathLibrary();
public void useJavascriptXPathLibrary();
public void useAjaxsltXPathLibrary();
public void registerNamespace(String prefix, String namespace);
public String getNamespace(String prefix);
public void pause(int milliseconds);
public void useMacroCmd(boolean isUse);
public void setMaxMacroCmd(int max);
public int getMaxMacroCmd();
public boolean isUseTelluriumApi();
public void useTelluriumApi(boolean isUse);
public void useTrace(boolean isUse);
public void showTrace();
public void setEnvironment(String name, Object value);
public Object getEnvironment(String name);
public void allowNativeXpath(boolean allow);
public void addScript(String scriptContent, String scriptTagId);
public void removeScript(String scriptTagId);
public void EngineState getEngineState();
public void useEngineLog(boolean isUse);
public void useTelluriumEngine(boolean isUse);
public void dumpEnvironment();Tellurium Java test cases provide the same APIs and the only difference is that the APIs in Tellurium Java test cases are static. EnvironmentWe added an Environment class to Tellurium Core so that you can change the runtime environment. The Environment class is defined as follows, public class Environment {
def envVariables = [:];
public boolean isUseCssSelector();
public boolean isUseCache();
public boolean isUseBundle();
public boolean isUseScreenshot();
public boolean isUseTrace();
public boolean isUseTelluriumApi();
public boolean isUseEngineLog();
public void useCssSelector(boolean isUse);
public void useCache(boolean isUse);
public void useBundle(boolean isUse);
public void useScreenshot(boolean isUse);
public void useTrace(boolean isUse);
public void useTelluriumApi(boolean isUse);
public void useEngineLog(boolean isUse);
public useMaxMacroCmd(int max);
public int myMaxMacroCmd();
public String myLocale();
public void setCustomEnvironment(String name, Object value);
public Object getCustomEnvironment(String name);
}where setCustomEnvironment and getCustomEnvironment can be used to pass user defined environment variables. Tellurium provides the following method for users to dump out all the current environment variables. public void dumpEnvironment(); The output is like the following. Environment Variables:
useEngineCache: true
useClosestMatch: false
useMacroCommand: true
maxMacroCmd: 5
useTelluriumApi: true
locatorWithCache: true
useCSSSelector: true
useTrace: true
logEngine: false
locale: en_USIf any exception is thrown at the dispatcher tier, the environment variable will also be printed out. Get UIs by Tag NameAs requested by users, Tellurium 0.7.0 added the following two methods. UiByTagResponse getUiByTag(String tag, Map filters); void removeMarkedUids(String tag); The first method passes in the tag name and the attributes as filters and get back the UI elements associated with the tag. The response object is defined as class UiByTagResponse {
//tag name
String tag;
Map filters
//temporally assigned IDs
String[] tids;
}where the tids are assigned by Tellurium Engine. Under the hood, Tellurium core creates one AllPurposeObject for each tid in the tids array and registers it to Core so that users can use the tid as the UID to act on the UI object. The second method cleans up all the temporally assigned IDs by the Tellurium Engine. Example: In UI module JettyLogonModule, we define the following method: public String[] getInputBox(){
def attrs = ["type" : "text"];
UiByTagResponse resp = getUiByTag("input", attrs);
return resp.tids;
}The test case is as follows @Test
public void testGetUiByTag(){
useEngineLog(true);
useTelluriumApi(true);
useCache(true);
String[] teuids = jlm.getInputBox();
assertNotNull(teuids);
for(String teuid: teuids){
jlm.keyType(teuid, "Tellurium Source");
}
jlm.removeMarkedUids("input");
}MiscThe default locator in Tellurium Engine is CSS selector by default. That is to say, we set exploitCssSelector = true in the Environment class and you can use disableCssSelector() or useCssSelector(false) to switch back to xpath if you like to. Exposed the following Selenium APIs to DslContext.
The following methods in DslContext have been renamed
| |