In my previous post we have recorded simple test case to ensure that new entity object can be created via web application. As you observed using original objects description defined by Selenium IDE represents test scenario in not human readable format. Also, it is hard to maintain. Therefore we will use UI-elements. From this post such objects as Pagesets and UI Elements to be specified before we can start using this feature in our test cases.
Pagesets shorthand
I’ve looked thru the application and determine next pages to work with: allPages (it contains objects related to any page), mainPage (there is the only page in the application that any user may have access to with no authentication) and areaPage. This page includes shorthand for all objects related to area entity.
Pagesets shorthand
I’ve looked thru the application and determine next pages to work with: allPages (it contains objects related to any page), mainPage (there is the only page in the application that any user may have access to with no authentication) and areaPage. This page includes shorthand for all objects related to area entity.
applMap.addPageset({
name: 'allPages'
, description: 'all appl.ua pages'
, pathRegexp: '.*'
});
applMap.addPageset({
name: 'areaPages'
, description: 'all pages for Areas'
, pathRegexp: 'member\.php\?lang=(eng|ukr)&op=' + page_ids['Areas']
});
applMap.addPageset({
name: 'mainPages'
, description: 'home pages for a user who is not logged in'
, pathRegexp: 'appl/(eng|ukr)/home'
});
Regular expression in areaPages description includes some list (pairs of possible page id with corresponding name). With this list it is easy to make such object descriptions. For instance, choosing areas page you do not need to know exact id value of this page, just select ‘Areas’ item from the list. page_ids list represents key/value pairs. Below you may find an example of this list:
var page_ids = {
'Dashboard': 'page_dashboard'
, 'Areas': 'list_areas'
…
, 'Activities': 'list_activity'
};
In shorthand for mainPages above you may see the regular expression contains ‘(eng|ukr)’ value. It means we make this object in the repository language independent. It means that the page will be discovered regardless locale selected at the moment.
So, meaningful pages are specified. Remaining pages can be covered the same way. The only thing left is to add object descriptions for certain entities and link them with pages from the set.
UI-Elements shorthand
I will describe objects to be used in this blog below (like credentials, menu items, table elements, popup dialog box etc). Others objects can be covered using similar approach. The following description shows how to specify credentials.
UI-Elements shorthand
I will describe objects to be used in this blog below (like credentials, menu items, table elements, popup dialog box etc). Others objects can be covered using similar approach. The following description shows how to specify credentials.
- Credentials:
applMap.addElement('mainPages', {
name: 'credentials'
, description: 'Credentials properties'
, args: [
{
name: 'credential_name'
, description: 'the name of the credential'
, defaultValues: keys(credential_ids)
}
]
, getLocator: function(args) {
var topic = credential_ids[args.credential_name];
return "//input[@name='" + topic + "']";
}
});
In this example credential_ids there is nothing else but set of key/value. Wit this approach you can make object description language independent.
var credential_ids = {
'User': 'username'
, 'Password': 'password'
, 'OK': 'imageField'
}
Menu items:
applMap.addElement('allPages', {
name: 'menuitem'
, description: 'Manage menuitem'
, args: [
{
name: 'submenu'
, description: 'the name of the submenu'
, defaultValues: keys(menuitem_ids)
}
]
, getLocator: function(args) {
var topic = menuitem_ids[args.submenu];
return "//td[@id='menuItem_" + topic + "']/table/tbody/tr/td[2]/table/tbody/tr/td[1]";
}
});
where menuitem_ids is:
var menuitems = [
'Dashboard',
'Manage',
…
'Language',
'Help',
'Logout'
];
![]() |
| Menu items |
- Table elements:
Table consists of several objects such as cells, column headers, filters etc. Cell intended to hold respective values. Clicking on the headers sort order can be specified for elements in the table. With filters search criteria can be determined. If some filters are not empty – all rows with values in corresponding columns that matched the criteria to be shown.
- Cells:
applMap.addElement('allPages', {
name: 'gridbox_cell'
, description: 'A cell in the common gridbox'
, args: [
{
name: 'row_index'
, description: 'the index of the row'
, defaultValues: range(2, 20)
}
, {
name: 'column_index'
, description: 'the index of the column'
, defaultValues: range(1, 20)
}
]
, getLocator: function(args) {
var xpath = "//div[@id='gridboxObjectBuffer']/table/tbody/tr[" + (args.row_index || 1) + "]/td[" + (args.column_index || 1) + "]";
return xpath;
}
});
This description is parameterized. Every cell can be defined using row and column ids.
Concerning column headers - two ways on how to get certain headers are shown below. First one implemented the way using column name while the second one – its id. You are free to use any of these ways depending on your needs. I admit that the second way has an issue – respective description is language dependent. It works fine for one language only. If you are going to test some application on different locales – this description should be updated in order to cover this case. Let’s keep it as is since it is minor, and respective solution has already been described above. I’m just adding this to the outstanding issues list.
- Column headers:
Concerning column headers - two ways on how to get certain headers are shown below. First one implemented the way using column name while the second one – its id. You are free to use any of these ways depending on your needs. I admit that the second way has an issue – respective description is language dependent. It works fine for one language only. If you are going to test some application on different locales – this description should be updated in order to cover this case. Let’s keep it as is since it is minor, and respective solution has already been described above. I’m just adding this to the outstanding issues list.
applMap.addElement('allPages', {
name: 'gridbox_header_by_index'
, description: 'Column in the header of the common gridbox'
, args: [
{
name: 'column_index'
, description: "the index of the column in the gridbox header"
, defaultValues: range(1, 20)
}
]
, getLocator: function(args) {
var xpath = "//div[@id='gridboxHeader']/table/tbody/tr/td[1]/table/tbody/tr[2]/td[" + (args.column_index || 1) + "]/div";
return xpath;
}
});
applMap.addElement('allPages', {
applMap.addElement('allPages', {
name: 'gridbox_header_by_name'
, description: 'Column in the header of the common gridbox'
, args: [
{
name: 'column_name'
, description: "the index of the column in the gridbox header"
, defaultValues: headers
}
]
, getLocator: function(args) {
var xpath = "//div[@id='gridboxHeader']/table/tbody/tr/td[1]/table/tbody/tr/td/div[contains(text(),'" + args.column_name + "')]";
return xpath;
}
});
Filter description is nothing else but an object related to every column and can be reached by its id:
applMap.addElement('allPages', {
name: 'gridbox_filter_criteria'
, description: 'Filter criteria in the header of the common gridbox'
, args: [
{
name: 'column_index'
, description: "Text area for the filter criteria for the column in the gridbox header"
, defaultValues: range(2, 20)
}
]
, getLocator: function(args) {
var xpath = "//div[@id='gridboxHeader']/table/tbody/tr/td[1]/table/tbody/tr[3]/td[" + (args.column_index || 1) + "]/input";
return xpath;
}
});
- Dialog box:
Any entity object can be registered/updated via respective popup dialog box. Area entity has such dialog window as well as others entities. This box includes primary and auxiliary properties (in our particular case located on General and Customer places tabs correspondingly). Also, there few button located on this box.
applMap.addElement('areaPages', {
name: 'tabs_in_dialog_box'
, description: "Tabs located in dialog box"
, args: [
{
name: 'name'
, description: 'the name of the tab'
, defaultValues: keys(tab_ids)
}
]
, getLocator: function(args) {
var xpath = "//div[@id='a_tabbar']/div/div[1]/div/div[" + tab_ids[args.name] + "]/div[3]/div";
return xpath;
}
});
applMap.addElement('areaPages', {
applMap.addElement('areaPages', {
name: 'dialog_box_area_property'
, description: 'Filter criteria in the header of the dialog box'
, args: [
{
name: 'property_name'
, description: "Property for selected area in respective dialog box"
, defaultValues: keys(area_property_ids)
}
]
, getLocator: function(args) {
var xpath = "" + area_property_ids[args.property_name] + "";
return xpath;
}
});
Using the description above you may reach any property on General tab (i.e. Area name, Description and Other information). area_property_ids set of key/value lists possible items:
var area_property_ids = {
'Area name': 'def'
, 'Description': 'DESCRIPTION'
, 'Other information': 'OTHERINFO'
}
applMap.addElement('areaPages', {
applMap.addElement('areaPages', {
name: 'buttons_in_dialog_box'
, description: "Buttons located in dialog box appeared on top for an object"
, args: [
{
name: 'name'
, description: 'the name of the button'
, defaultValues: [
'Submit'
, 'Close'
, 'Check'
, 'Check all'
, 'Uncheck'
, 'Uncheck all'
]
}
]
, getLocator: function(args) {
var xpath = "//input[@type='button' and @value='" + args.name + "']";
return xpath;
}
});
var tab_ids = {
var tab_ids = {
'General': '1'
, 'Customer places': '2'
}
It seems all objects required to record our simple scenario have been described using this wonderful UI-Element locator plug-in for Selenium IDE. Without this plug-in it will be too hard to make the automation readable and maintainable.
Oh, forgot about one more object – this is that button which calls dialog box to register new area object. Here it is:
![]() |
| Dialog box for area entity object |
- Buttons in the dialog box:
Oh, forgot about one more object – this is that button which calls dialog box to register new area object. Here it is:
applMap.addElement('areaPages', {
name: 'buttons_under_menu'
, description: "Buttons located under main menu on top common grid"
, args: [
{
name: 'name'
, description: 'the name of the button'
, defaultValues: button_names
}
]
, getLocator: function(args) {
var xpath = "//td/table[contains(@title,'" + args.name + "')]";
return xpath;
}
});
var button_names = [
var button_names = [
'Add new area (Ins)'
, 'Edit (Enter)'
…
];
Initially, I was going to describe object repository and update simple test case in scope of one post. However as I can see it became to long. So it would be better to divide the material on two separate posts. Please see the updated test case in my later post.




