This document covers the following topics:
Natural for Ajax is based on running HTML pages in a browser. These pages are designed as XML page layouts.
Test automation tools like Selenium (see http://docs.seleniumhq.org/) need to locate specific HTML elements in an HTML page to either check or adapt the content or to trigger corresponding events. In a Selenium test program, the developer usually passes identifiers using the Selenium Java API which enable Selenium to locate the elements for testing.
For stable automated tests, it is extremely important to use stable identifiers. For
instance, rearranging controls in a layout or adding an additional control must not
change the identifiers. For the most common controls, Natural for Ajax automatically
generates stable identifiers, the so-called "test tool IDs". They are
generated as data-testtoolid
attributes into the HTML page. Test tools like
Selenium can use this data-testtoolid
attribute to locate the element.
The following gives a brief introduction for using stable identifiers in Natural for Ajax applications.
All Natural for Ajax applications automatically generate stable identifiers for the most common controls. So a developer need not do anything to set them.
Let us have a look at the helloworld.xml page layout of the njxdemos. The most interesting controls for automated tests are the FIELD and BUTTON controls.
In the following example, you see that the valueprop
property is set in the FIELD control, but the testtoolid
property is not explicitly set.
If a value for the testtoolid
property is not explicitly
set in a FIELD control, the HTML will contain a data-testtoolid
attribute
with the value of the valueprop
property. This is shown in the HTML snippet
below. You do not need to understand all the HTML details. The snippet just shows that a
data-testtoolid
attribute is automatically generated
for a FIELD control; you do not have to do anything.
... <input id="F_13" name="CC" class='FIELDInputEdit' data-testtoolid='yourname' type="text" style="width: 185px;"> ...
Caution:
The above HTML code contains the id
attribute with the value
F_13
. Do not use this in your test tool. It will break your tests sooner
or later because it is not stable. For example, if you add another FIELD control in
front of the yourname
FIELD control, the id
of the
yourname
FIELD control will change its value to F_14
.
In the BUTTON control, the method
property is set. Again,
the testtoolid
property is not explicitly set.
For a BUTTON control, a data-testtoolid
attribute with the value of the
method
property is automatically generated as shown in
the HTML snippet below. Again, you need not understand all the HTML details, just look
at the data-testtoolid
attribute.
... <button type="button" id="B_17" data-testtoolid='onHelloWorld' style="width: 185px; height: 80px;" name="CC" class="BUTTONInput"> ...
With the Selenium tool, for example, you can locate the FIELD and BUTTON controls using
an XPATH expression which contains the data-testtoolid
value. This XPATH expression can be passed to the Selenium locator
org.openqa.selenium.By.ByXPath
:
By myfieldlocator = new ByXPath(".//*[@data-testtoolid='yourname"]"); By mymemthodlocator = new ByXPath(".//*[@data-testtoolid='onHelloWorld"]");
See http://docs.seleniumhq.org/ for more information about the Selenium Java API.
testtoolid
In some cases, you may not want to use the valueprop
property of a control as the testtoolid
. Instead, you want to specify your
own testtoolid
. Examples for this are layouts in which several controls are
bound to the same Natural data field. You can then simply set an explicit
testtoolid
property for each of these controls.
... <input id="F_13" name="CC" class='FIELDInputEdit' testtoolid='myowntesttoolid' type="text" style="width: 185px;"> ...
For complex controls, a single testtoolid
is not enough to locate the
individual parts of the control. The following table provides examples for the most
common XPATH expressions for some complex controls.
Control | testtoolid |
XPATH |
---|---|---|
ICONLIST | testtoolid="myiconlist" |
.//*[@data-testtoolid='myiconlist0'], .//*[@data-testtoolid='myiconlist1'],... |
BUTTONLIST | testtoolid="mybuttonlist" |
.//*[@data-testtoolid='mybuttonlist0'], .//*[@data-testtoolid='mybuttonlist1'],... |
ROWTABLEAREA2 | testtoolid="lines" |
.//*[@data-testtoolid='lines_table'] Rows/columns: .//*[@data-testtoolid=’lines.items[0].<col1testtoolid>’] .//*[@data-testtoolid=’lines.items[0].<col2testtoolid>’]... .//*[@data-testtoolid=’lines.items[1].<col1testtoolid>’] .//*[@data-testtoolid=’lines.items[1].<col2testtoolid>’]... |
ROWTABSUBPAGES | testtoolid="mytabs" |
.//*[@data-testtoolid='mytabs0'], .//*[@data-testtoolid='mytabs1'],... |
MULTISELECT | testtoolid="mychoice" |
XPATH for entries: .//*[@data-testtoolid='mychoice0'], .//*[@data-testtoolid='mychoice1'],... XPATH for buttons: .//*[@data-testtoolid='mychoicebutton0'], .//*[@data-testtoolid='mychoicebutton1'], .//*[@data-testtoolid='mychoicebutton2'], .//*[@data-testtoolid='mychoicebutton3']... |
BMOBILE:SIMPLEGRID | testtoolid="lines" |
.//*[@data-testtoolid='lines'] Column text: .//*[@data-testtoolid='lines'] 1.row/1.column //*[@data-testtoolid='lines']//tr[1]/td[1] 1.row/2.column.//*[@data-testtoolid='lines']//tr[1]/td[2] 2.row/1.column .//*[@data-testtoolid='lines']//tr[2]/td[1] Button to enable editing Select the row/column: //*[@data-testtoolid='lines']//tr[1]/td[1] Use the className Selenium example: driver.findElement(By.xpath(".//*[@data-testtoolid='lines']//tr[1]/td[2]")).findElement(By.className("SIMPLEGRIDEditbutton")); Input field of editable column: .//*[@data-testtoolid='SIMPLEGRIDColinput’] OK Button editable column: .//*[@data-testtoolid='SIMPLEGRIDColinputok’] Cancel Button editable column: .//*[@data-testtoolid='SIMPLEGRIDColinputok’] |
In complex controls, you need not explicitly set the
testtoolid
property in the page layout. If you do not
specify any testtoolid
, the corresponding
*prop
properties such as valueprop
,
griddataprop
, iconlistprop
or pagesprop
will be used.
Here is the table from above when not specifying a
testtoolid
explicitly:
Control | *prop |
XPATH |
---|---|---|
ICONLIST | iconlistprop="myiconlist" |
.//*[@data-testtoolid='myiconlist0'], .//*[@data-testtoolid='myiconlist1'],... |
BUTTONLIST | buttonlistprop="mybuttonlist" |
.//*[@data-testtoolid='mybuttonlist0'], .//*[@data-testtoolid='mybuttonlist1'],... |
ROWTABLEAREA2 | griddataprop="lines" |
.//*[@data-testtoolid='lines_table'] Rows/columns: .//*[@data-testtoolid=’lines.items[0].<col1testtoolid>’] .//*[@data-testtoolid=’lines.items[0].<col2testtoolid>’]... .//*[@data-testtoolid=’lines.items[1].<col1testtoolid>’] .//*[@data-testtoolid=’lines.items[1].<col2testtoolid>’]... |
ROWTABSUBPAGES | pagesprop="mytabs" |
.//*[@data-testtoolid='mytabs0'], .//*[@data-testtoolid='mytabs1'],... |
MULTISELECT | valueprop="mychoice" |
XPATH for entries: .//*[@data-testtoolid='mychoice0'], .//*[@data-testtoolid='mychoice1'],... XPATH for buttons: .//*[@data-testtoolid='mychoicebutton0'], .//*[@data-testtoolid='mychoicebutton1'], .//*[@data-testtoolid='mychoicebutton2'], .//*[@data-testtoolid='mychoicebutton3']... |
BMOBILE:SIMPLEGRID | gridprop="lines" |
.//*[@data-testtoolid='lines'] Column text: .//*[@data-testtoolid='lines'] 1.row/1.column //*[@data-testtoolid='lines']//tr[1]/td[1] 1.row/2.column.//*[@data-testtoolid='lines']//tr[1]/td[2] 2.row/1.column .//*[@data-testtoolid='lines']//tr[2]/td[1] Button to enable editing Select the row/column: //*[@data-testtoolid='lines']//tr[1]/td[1] Use the className Selenium example: driver.findElement(By.xpath(".//*[@data-testtoolid='lines']//tr[1]/td[2]")).findElement(By.className("SIMPLEGRIDEditbutton")); Input field of editable column: .//*[@data-testtoolid='SIMPLEGRIDColinput’] OK Button editable column: .//*[@data-testtoolid='SIMPLEGRIDColinputok’] Cancel Button editable column: .//*[@data-testtoolid='SIMPLEGRIDColinputok’] |