|
TelluriumNewReferenceProjects
IntroductionTellurium 0.7.0 merged the original tellurium-junit-java and tellurium-testng-java two reference projects into a new reference project tellurium-website and add a new reference project ui-examples. The first one is real world tellurium tests using tellurium website as an example and the second one uses html source, mostly contributed by Tellurium users, to demonstrate the usage of different Tellurium UI objects. Tellurium Website ProjectThe tellurium website project illustrates the following usages of Tellurium:
Create Custom UI objectsTellurium supports custom UI objects defined by users. For most UI objects, they must extend the "UiObject" class and then define actions or methods they support. For container type UI objects, i.e., which hold other UI objects, they should extends the Container class. Please see Tellurium Table and List objects for more details. In the tellurium-website project, we defined the custom UI object "SelectMenu" as follows: class SelectMenu extends UiObject{
public static final String TAG = "div"
String header = null
//map to hold the alias name for the menu item in the format of "alias name" : "menu item"
Map<String, String> aliasMap
def click(Closure c){
c(null)
}
def mouseOver(Closure c){
c(null)
}
def mouseOut(Closure c){
c(null)
}
public void addTitle(String header){
this.header = header
}
public void addMenuItems(Map<String, String> menuItems){
aliasMap = menuItems
}
......
}For each UI object, you must define the builder so that Tellurium knows how to construct the UI object when it parses the UI modules. For example, we define the builder for the "SelectMenu" object as follows: class SelectMenuBuilder extends UiObjectBuilder{
static final String ITEMS = "items"
static final String TITLE = "title"
public build(Map map, Closure c) {
def df = [:]
df.put(TAG, SelectMenu.TAG)
SelectMenu menu = this.internBuild(new SelectMenu(), map, df)
Map<String, String> items = map.get(ITEMS)
if(items != null && items.size() > 0){
menu.addMenuItems(items)
}
menu.addTitle(map.get(TITLE))
return menu
}
}You may wonder how to hook the custom objects into Tellurium core so that it can recognize the new type. The answer is simple, you just add the UI object name and its builder class name to Tellurium configuration file TelluriumConfig.groovy. Update the following section: uiobject{
builder{
SelectMenu="org.telluriumsource.ui.builder.SelectMenuBuilder"
}
}Create UI modulesYou should create UI modules in Groovy files, which should extend the DslContext class. In the defineUi method, define your UIs and then define all methods for them. Take the Tellurium Downloads page as an example: class TelluriumDownloadsPage extends DslContext{
public void defineUi() {
//define UI module of a form include download type selector and download search
ui.Form(uid: "downloadSearch", clocator: [action: "list", method: "GET"], group: "true") {
Selector(uid: "downloadType", 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"])
}
ui.Table(uid: "downloadResult", clocator: [id: "resultstable", class: "results"], group: "true"){
//define table header
//for the border column
TextBox(uid: "{header: 1}", clocator: [:])
UrlLink(uid: "{header: 2}", clocator: [text: "*Filename"])
UrlLink(uid: "{header: 3}", clocator: [text: "*Summary + Labels"])
UrlLink(uid: "{header: 4}", clocator: [text: "*Uploaded"])
UrlLink(uid: "{header: 5}", clocator: [text: "*Size"])
UrlLink(uid: "{header: 6}", clocator: [text: "*DownloadCount"])
UrlLink(uid: "{header: 7}", clocator: [text: "*..."])
//define table elements
//for the border column
TextBox(uid: "{row: all, column: 1}", clocator: [:])
//the summary + labels column consists of a list of UrlLinks
List(uid: "{row: all, column: 3}"){
UrlLink(uid: "{all}", clocator: [:])
}
//For the rest, just UrlLink
UrlLink(uid: "{all}", clocator: [:])
}
ui.RadioButton(uid: "test", clocator: [:], respond: "click")
}
public String[] getAllDownloadTypes(){
return getSelectOptions("downloadSearch.downloadType")
}
public String getCurrentDownloadType(){
return getSelectedLabel("downloadSearch.downloadType");
}
public void selectDownloadType(String type){
selectByLabel "downloadSearch.downloadType", type
}
public void searchDownload(String keyword){
type "downloadSearch.searchBox", keyword
click "downloadSearch.searchButton"
waitForPageToLoad 30000
}
public int getTableHeaderNum(){
enableCache();
enableTelluriumApi();
return getTableHeaderColumnNum("downloadResult")
}
public List<String> getHeaderNames(){
enableCache();
enableTelluriumApi();
List<String> headernames = new ArrayList<String>()
int mcolumn = getTableHeaderColumnNum("downloadResult")
for(int i=1; i<=mcolumn; i++){
headernames.add(getText("downloadResult.header[${i}]"))
}
return headernames
}
public List<String> getDownloadFileNames(){
int mcolumn = getTableMaxRowNum("downloadResult")
List<String> filenames = new ArrayList<String>()
for(int i=1; i<=mcolumn; i++){
filenames.add(getText("downloadResult[${i}][2]").trim())
}
return filenames
}
public void clickFileNameColumn(int row){
click "downloadResult[${row}][2]"
pause 1000
chooseCancelOnNextConfirmation()
pause 500
}
...
}
Create Java Test CasesYou can create Java Test Cases by extending the TelluriumJUnitTestCase or TelluriumTestNGTestCase class. Nothing special, just like regular JUnit test cases. For instance, public class TelluriumDownloadsPageTestNGTestCase extends TelluriumTestNGTestCase{
private static TelluriumDownloadsPage downloadPage;
@BeforeClass
public static void initUi() {
downloadPage = new TelluriumDownloadsPage();
downloadPage.defineUi();
connectSeleniumServer();
useCache(true);
}
@BeforeMethod
public void setUpForMethod(){
connectUrl("http://code.google.com/p/aost/downloads/list");
}
@Test
public void testValidate(){
downloadPage.validate("downloadResult");
}
@Test
public void testDownloadTypes(){
String[] allTypes = downloadPage.getAllDownloadTypes();
assertNotNull(allTypes);
assertTrue(allTypes[1].contains("All downloads"));
assertTrue(allTypes[2].contains("Featured downloads"));
assertTrue(allTypes[3].contains("Current downloads"));
assertTrue(allTypes[4].contains("Deprecated downloads"));
}
@Test
public void testDefaultDownloadType(){
// Set download type with other value
downloadPage.selectDownloadType(" All downloads");
// Navigate away from download page
connectUrl("http://code.google.com/p/aost/downloads/list");
String defaultType = downloadPage.getCurrentDownloadType();
assertNotNull(defaultType);
assertTrue(defaultType.contains("Current downloads"));
}
@Test
public void testSearchByText(){
// Set download type with other value
downloadPage.selectDownloadType(" All downloads");
downloadPage.searchDownload("Tellurium-0.6.0");
useTelluriumApi(true);
List<String> list = downloadPage.getDownloadFileNames();
useTelluriumApi(false);
assertNotNull(list);
assertFalse(list.isEmpty());
assertTrue(Helper.include(list, "tellurium-core.0.6.0.tar.gz"));
}
@Test
public void testSearchByLabel(){
// Set download type with other value
downloadPage.selectDownloadType(" All downloads");
downloadPage.searchDownload("label:Featured");
useTelluriumApi(true);
List<String> list = downloadPage.getDownloadFileNames();
useTelluriumApi(false);
assertNotNull(list);
assertFalse(list.isEmpty());
}
@Test
public void testDownloadFileNames(){
int mcolumn = downloadPage.getTableHeaderNum();
assertEquals(7, mcolumn);
List<String> list = downloadPage.getHeaderNames();
assertNotNull(list);
assertEquals(7, list.size());
assertTrue(Helper.include(list, "Filename"));
list = downloadPage.getDownloadFileNames();
assertNotNull(list);
assertFalse(list.isEmpty());
assertTrue(Helper.include(list, "tellurium-core.0.6.0.tar.gz"));
}
......
}Create and Run DSL scriptsIn Tellurium, you can create test scripts in pure DSL. Take TelluriumPage.dsl as an example, //define Tellurium project menu
ui.Container(uid: "menu", clocator: [tag: "table", id: "mt", trailer: "/tbody/tr/th"], group: "true"){
//since the actual text is Project Home, we can use partial match here. Note "*" stands for partial match
UrlLink(uid: "project_home", clocator: [text: "*Home"])
UrlLink(uid: "downloads", clocator: [text: "Downloads"])
UrlLink(uid: "wiki", clocator: [text: "Wiki"])
UrlLink(uid: "issues", clocator: [text: "Issues"])
UrlLink(uid: "source", clocator: [text: "Source"])
}
//define the Tellurium project search module, which includes an input box, two search buttons
ui.Form(uid: "search", clocator: [:], group: "true"){
InputBox(uid: "searchbox", clocator: [name: "q"])
SubmitButton(uid: "search_project_button", clocator: [value: "Search projects"])
}
openUrl "http://code.google.com/p/aost/"
click "menu.project_home"
waitForPageToLoad 30000
click "menu.downloads"
waitForPageToLoad 30000
click "menu.wiki"
waitForPageToLoad 30000
click "menu.issues"
waitForPageToLoad 30000
openUrl "http://code.google.com/p/aost/"
type "search.searchbox", "Tellurium Selenium groovy"
click "search.search_project_button"
waitForPageToLoad 30000To run the DSL script, you should run the code with Maven mvn test Then, use the rundsl.sh to run the DSL script ./rundsl.sh src/test/resources/org/telluriumsource/dsl/TelluriumPage.dsl For Windows, you should use the rundsl.bat script. In addition, Tellurium provides a new rundsl.groovy script using the Groovy grape feature. import groovy.grape.Grape;
Grape.grab(group:'org.telluriumsource', module:'tellurium-core', version:'0.7.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-te3-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-te3-SNAPSHOT'),
@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.7.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)
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 Data Driven TestingWe use Tellurium Issue page as the data driven testing example, we define tests to search issues assigned to a Tellurium team member and use the input file to define which team members we want the result for. We first define a TelluriumIssuesModule class that extends TelluriumDataDrivenModule class and includes a method "defineModule". In the "defineModule" method, we define UI modules, input data format, and different tests. The UI modules are the same as defined before for the Tellurium issue page. The input data format is defined as fs.FieldSet(name: "OpenIssuesPage") {
Test(value: "OpenTelluriumIssuesPage")
}
fs.FieldSet(name: "IssueForOwner", description: "Data format for test SearchIssueForOwner") {
Test(value: "SearchIssueForOwner")
Field(name: "issueType", description: "Issue Type")
Field(name: "owner", description: "Owner")
}Here we have two different input data formats. The "Test" field defines the test name and the "Field" field define the input data name and description. For example, the input data for the test "SearchIssueForOwner" have two input parameters "issueType" and "owner". The tests are defined use "defineTest". One of the test "SearchIssueForOwner" is defined as follows, defineTest("SearchIssueForOwner") {
String issueType = bind("IssueForOwner.issueType")
String issueOwner = bind("IssueForOwner.owner")
int headernum = getCachedVariable("headernum")
int expectedHeaderNum = getTableHeaderNum()
compareResult(expectedHeaderNum, headernum)
List<String> headernames = getCachedVariable("headernames")
String[] issueTypes = getCachedVariable("issuetypes")
String issueTypeLabel = getIssueTypeLabel(issueTypes, issueType)
checkResult(issueTypeLabel) {
assertTrue(issueTypeLabel != null)
}
//select issue type
if (issueTypeLabel != null) {
selectIssueType(issueTypeLabel)
}
//search for all owners
if ("all".equalsIgnoreCase(issueOwner.trim())) {
searchForAllIssues()
} else {
searchIssue("owner:" + issueOwner)
}
......
}
}
As you can see, we use "bind" to tie the variable to input data field. For example, the variable "issueType" is bound to "IssueForOwner.issueType", i.e., field "issueType" of the input Fieldset "IssueForOwner". "getCachedVariable" is used to get variables passed from previous tests and "compareResult" is used to compare the actual result with the expected result. The input file format looks like OpenTelluriumIssuesPage ## Test Name | Issue Type | Owner SearchIssueForOwner | Open | all SearchIssueForOwner | All | matt.senter SearchIssueForOwner | Open | John.Jian.Fang SearchIssueForOwner | All | vivekmongolu SearchIssueForOwner | All | haroonzone The actual test class is very simple class TelluriumIssuesDataDrivenTest extends TelluriumDataDrivenTest{
public void testDataDriven() {
includeModule org.telluriumsource.ddt.TelluriumIssuesModule.class
//load file
loadData "src/test/resources/org/telluriumsource/data/TelluriumIssuesInput.txt"
useCache(true);
useClosestMatch(true);
//read each line and run the test script until the end of the file
stepToEnd()
//close file
closeData()
}
}We first define which Data Driven Module we want to load and then read input data file. After that, we read the input data file line by line and execute the appropriate tests defined in the input file. Finally, close the data file. The test result looks as follows, <TestResults>
<Total>6</Total>
<Succeeded>6</Succeeded>
<Failed>0</Failed>
<Test name='OpenTelluriumIssuesPage'>
<Step>1</Step>
<Passed>true</Passed>
<Input>
<test>OpenTelluriumIssuesPage</test>
</Input>
<Assertion Value='10' Passed='true' />
<Status>PROCEEDED</Status>
<Runtime>2.579049</Runtime>
</Test>
<Test name='SearchIssueForOwner'>
<Step>2</Step>
<Passed>true</Passed>
<Input>
<test>SearchIssueForOwner</test>
<issueType>Open</issueType>
<owner>all</owner>
</Input>
<Assertion Expected='10' Actual='10' Passed='true' />
<Assertion Value=' Open Issues' Passed='true' />
<Status>PROCEEDED</Status>
<Runtime>4.118923</Runtime>
<Message>Found 10 Open Issues for owner all</Message>
<Message>Issue: Better way to wait or pause during testing</Message>
<Message>Issue: Add support for JQuery selector</Message>
<Message>Issue: Export Tellurium to Ruby</Message>
<Message>Issue: Add check Alter function to Tellurium</Message>
<Message>Issue: Firefox plugin to automatically generate UI module for users</Message>
<Message>Issue: Create a prototype for container-like Dojo widgets</Message>
<Message>Issue: Need to create Wiki page to explain how to setup Maven and use Maven to build multiple projects</Message>
<Message>Issue: Configure IntelliJ to properly load one maven sub-project and not look for an IntelliJ project dependency</Message>
<Message>Issue: Support nested properties for Tellurium</Message>
<Message>Issue: update versions for extensioin dojo-widget and TrUMP projects</Message>
</Test>
...
</TestResults>Tellurium ui-examples ProjectSome of the html sources in the ui-examples project are contributed by tellurium users. Thanks for their contributions. ui-examples project includes the following different types of UI objects. SelectorHTML source: <form method="POST" action="check_phone">
<select name="Profile/Customer/Telephone/@CountryAccessCode" style="font-size:92%">
<option value="1" selected=selected>US</option>
<option value="2" id="uk">UK</option>
<option value="3">AT</option>
<option value="4">BE</option>
<option value="4" id="ca">CA</option>
<option value="6">CN</option>
<option value="7">ES</option>
<option value="8">VG</option>
</select>
<input type="text" class="medium paxFerryNameInput" value="" name="Profile/Customer/Telephone/@PhoneNumber"
maxlength="16" id="phone1" tabindex="26">
<input name="submit" type="submit" value="Check">
</form>UI Module: class SelectorExampleModule extends DslContext{
public void defineUi(){
ui.Form(uid: "Form", clocator: [method: "POST", action: "check_phone"]){
Selector(uid: "Country", clocator: [name: "\$CountryAccessCode"])
InputBox(uid: "Number", clocator: [name: "\$PhoneNumber"])
SubmitButton(uid: "check", clocator: [value: "Check"])
}
}
public void check(String country, String number){
select "Form.Country", country
keyType "Form.Number", number
click "Form.check"
waitForPageToLoad 30000
}
}ContainerHTML source: <div class="mainMenu">
<a href="http://localhost:8080/ContainerExample.html">Events</a>
<a href="http://localhost:8080/ContainerExample.html">Suppliers</a>
<a href="http://localhost:8080/ContainerExample.html">Venues</a>
<a href="http://localhost:8080/ContainerExample.html">Booking Report</a>
<a href="http://localhost:8080/ContainerExample.html">Notifications</a>
<a href="http://localhost:8080/ContainerExample.html">Help</a>
</div>UI Module: class ContainerExampleModule extends DslContext {
public void defineUi(){
ui.Container(uid: "mainnav", clocator: [tag: "div", class: "mainMenu"], group: true) {
UrlLink(uid: "events", clocator: [text: "Events"])
UrlLink(uid: "suppliers", clocator: [text: "Suppliers"])
UrlLink(uid: "venues", clocator: [text: "Venues"])
UrlLink(uid: "bookingReport", clocator: [text: "Booking Report"])
UrlLink(uid: "notifications", clocator: [text: "Notifications"])
UrlLink(uid: "help", clocator: [text: "Help"])
}
}
}FormHTML Source: <H1>FORM Authentication demo</H1>
<form method="POST" action="j_security_check">
<table border="0" cellspacing="2" cellpadding="1">
<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" type="submit" value="Login">
</td>
</tr>
</table>
</form>UI Module: public class FormExampleModule extends DslContext {
public void defineUi() {
ui.Form(uid: "Form", clocator: [tag: "table"]){
Container(uid: "Username", clocator: [tag: "tr"]){
TextBox(uid: "Label", clocator: [tag: "td", text: "Username:", direct: "true"])
InputBox(uid: "Input", clocator: [tag: "input", type: "text", name: "j_username"])
}
Container(uid: "Password", clocator: [tag: "tr"]){
TextBox(uid: "Label", clocator: [tag: "td", text: "Password:", direct: "true"])
InputBox(uid: "Input", clocator: [tag: "input", type: "password", name: "j_password"])
}
SubmitButton(uid: "Submit", clocator: [tag: "input", type: "submit", value: "Login", name: "submit"])
}
}
public void logon(String username, String password){
keyType "Form.Username.Input", username
keyType "Form.Password.Input", password
click "Form.Submit"
waitForPageToLoad 30000
}
}ListHTML Source: <table id="hp_table" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="sidebar" valign="top">
<DIV class="sub_cat_section">
<DIV class="sub_cat_title" style="">Fiction</DIV>
<P><A href="http://books.google.com/books?q=+subject:%22+Literature+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Literature</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Science+Fiction+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Science fiction</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Fantasy+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Fantasy</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Romance+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Romance</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Fiction+/+Mystery+%26+detective+/+General+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Mystery</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Fairy+tales+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Fairy tales</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Short+Stories+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Short stories</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Poetry+%22&as_brr=3&rview=1&source=gbs_hplp_fict" style="">Poetry</A></P></DIV>
<DIV class="sub_cat_section">
<DIV class="sub_cat_title">Non-fiction</DIV>
<P><A href="http://books.google.com/books?q=+subject:%22+Philosophy+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Philosophy</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Economics+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Economics</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Political+Science+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Political science</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Linguistics+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Linguistics</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Mathematics+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Mathematics</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Science+/+Physics+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Physics</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Science+/+Chemistry+/+General+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Chemistry</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Science+/+Life+Sciences+/+Biology+/+General+%22&as_brr=3&rview=1&source=gbs_hplp_nofict">Biology</A>
</P></DIV>
<DIV class="sub_cat_section">
<DIV class="sub_cat_title">Random subjects</DIV>
<P><A href="http://books.google.com/books?q=+subject:%22+Toys+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Toys</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Solar+houses+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Solar houses</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Stories+in+rhyme+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Stories in rhyme</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Courtship+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Courtship</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Mythology+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Mythology</A>
</P>
<P><A href="http://books.google.com/books?q=+subject:%22+Differential+equations+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Differential equations</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Inscriptions,+Latin+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Latin inscriptions</A></P>
<P><A href="http://books.google.com/books?q=+subject:%22+Nuclear+energy+%22&as_brr=3&rview=1&source=gbs_hplp_subj">Nuclear energy</A></P>
</DIV>
</td>
</tr>
</tbody>
</table>UI Module: class ListExampleModule extends DslContext {
public void defineUi() {
ui.Container(uid: "GoogleBooksList", clocator: [tag: "table", id: "hp_table"]) {
List(uid: "subcategory", clocator: [tag: "td", class: "sidebar"], separator: "div") {
Container(uid: "{all}") {
TextBox(uid: "title", clocator: [tag: "div", class: "sub_cat_title"])
List(uid: "links", separator: "p") {
UrlLink(uid: "{all}", clocator: [:])
}
}
}
}
}
}TableHTML Source: <table id="xyz">
<tbody>
<tr>
<th>one</th>
<th>two</th>
<th>three</th>
</tr>
</tbody>
<tbody>
<tr>
<td>
<div class="abc">Profile</div>
</td>
<td><input class="123"/><br/>
<div class="someclass">
<span class="x">Framework</span><br/>
<a href="http://code.google.com/p/aost">Tellurium</a>
</div>
</td>
<td>
Hello World!
</td>
</tr>
</tbody>
</table>UI Module: class TableExampleModule extends DslContext {
public void defineUi() {
ui.StandardTable(uid: "GT", clocator: [id: "xyz"], ht: "tbody"){
TextBox(uid: "{header: all}", clocator: [:])
TextBox(uid: "{row: 1, column: 1}", clocator: [tag: "div", class: "abc"])
Container(uid: "{row: 1, column: 2}"){
InputBox(uid: "Input", clocator: [tag: "input", class: "123"])
Container(uid: "Some", clocator: [tag: "div", class: "someclass"]){
Span(uid: "Span", clocator: [tag: "span", class: "x"])
UrlLink(uid: "Link", clocator: [:])
}
}
}
}
public void work(String input){
keyType "GT[1][2].Input", input
click "GT[1][2].Some.Link"
waitForPageToLoad 30000
}
}RepeatHTML Source: <form name="selectedSailingsForm">
<div class="segment clearfix">
<div class="option">
<ul class="fares">
<li>
<input type="radio">
<label>Economy</label>
</li>
<li>
<input type="radio">
<label>Flexible</label>
</li>
</ul>
<div class="details">
<dl>
<dt>Ship:</dt>
<dd>A</dd>
<dt>Departs</dt>
<dd>
<em>08:00</em>
</dd>
<dt>Arrives</dt>
<dd>
<em>11:45</em>
</dd>
</dl>
</div>
</div>
<div class="option">
<ul class="fares">
<li>
<input type="radio">
<label>Economy</label>
</li>
<li>
<input type="radio">
<label>Flexible</label>
</li>
</ul>
<div class="details">
<dl>
<dt>Ship:</dt>
<dd>B</dd>
<dt>Departs</dt>
<dd>
<em>17:30</em>
</dd>
<dt>Arrives</dt>
<dd>
<em>21:15</em>
</dd>
</dl>
</div>
</div>
</div>
<div class="segment clearfix">
<div class="option">
<ul class="fares">
<li>
<input type="radio">
<label>Economy</label>
</li>
<li>
<input type="radio">
<label>Flexible</label>
</li>
</ul>
<div class="details">
<div class="photo"><img/></div>
<dl>
<dt>Ship:</dt>
<dd>C</dd>
<dt>Departs</dt>
<dd>
<em>02:00</em>
</dd>
<dt>Arrives</dt>
<dd>
<em>06:00</em>
</dd>
</dl>
</div>
</div>
<div class="option">
<ul class="fares">
<li>
<input type="radio">
<label>Economy</label>
</li>
<li>
<input type="radio">
<label>Flexible</label>
</li>
</ul>
<div class="details">
<dl>
<dt>Ship:</dt>
<dd>D</dd>
<dt>Departs</dt>
<dd>
<em>12:45</em>
</dd>
<dt>Arrives</dt>
<dd>
<em>16:30</em>
</dd>
</dl>
</div>
</div>
</div>
</form>UI Module: class RepeatExampleModule extends DslContext {
public void defineUi(){
ui.Form(uid: "SailingForm", clocator: [name: "selectedSailingsForm"] ){
Repeat(uid: "Section", clocator: [tag: "div", class: "segment clearfix"]){
Repeat(uid: "Option", clocator: [tag: "div", class: "option", direct: "true"]){
List(uid: "Fares", clocator: [tag: "ul", class: "fares", direct: "true"], separator: "li"){
Container(uid: "{all}"){
RadioButton(uid: "radio", clocator: [:], respond: ["click"])
TextBox(uid: "label", clocator: [tag: "label"])
}
}
Container(uid: "Details", clocator: [tag: "div", class: "details"]){
Container(uid: "ShipInfo", clocator: [tag: "dl"]){
TextBox(uid: "ShipLabel", clocator: [tag: "dt", position: "1"])
TextBox(uid: "Ship", clocator: [tag: "dd", position: "1"])
TextBox(uid: "DepartureLabel", clocator: [tag: "dt", position: "2"])
Container(uid: "Departure", clocator: [tag: "dd", position: "2"]){
TextBox(uid: "Time", clocator: [tag: "em"])
}
TextBox(uid: "ArrivalLabel", clocator: [tag: "dt", position: "3"])
Container(uid: "Arrival", clocator: [tag: "dd", position: "3"]){
TextBox(uid: "Time", clocator: [tag: "em"])
}
}
}
}
}
}
}
}Resources
| |