In English you would read this as Simple Se-lenium Page Objects.
We decided to sound a bit desi as well. So in Hindi you would just read this name as Simple sey page objects.
Obviously we wanted to associated with Selenium which explains the suffix Se.
This library is called as SimpleSe because it attempts at providing a simple way to work with page objects.
Page objects as a concept has been built by different people in different ways. This library attempts at building page objects keeping in mind the following things:
-
Simple to use (try staying away from complex approaches)
-
De-couple the java code from the locators so that a locator change would merely warrant a change in the externalised data source (JSON file in our case)
-
Support localization i.e., being able to define locators for different countries and still have a unified way of working with the html elements.
-
Support a simple way of expressing explicit waits in Selenium. E.g., would include
- Wait for
30 secondsfor a particular element to show up before interacting with it. - Wait for an additional
x secondsfor a particular element to become clickable.
- Wait for
Simple Se Page Objects requires that you use :
- JDK 8.
- Selenium 3.0.1 (or) higher.
Simple Se Page Objects is a Maven artifact. In order to consume it, you merely need to add the following as a dependency in your pom file.
<dependency>
<groupId>com.rationaleemotions</groupId>
<artifactId>simple-se-pageobjects</artifactId>
<version>1.0.0</version>
</dependency>At the heart of this project is the JSON file that contains all your locators information.
The intent is to define one JSON file per page. So if your UI automation flow has 10 pages, then effectively you
would define 10 pages respectively.
Here's how a typical JSON file that represents the locators could look like :
{
"name": "FooPage",
"defaultLocale": "en_US",
"elements": [
{
"name": "bar",
"locale": [
{
"name": "en_US",
"locator": "//h1"
},
{
"name": "en_FR",
"locator": "//h1"
}
],
"wait": {
"until": "visible",
"for": 45
}
}
]
}name- (Mandatory) This is a user friendly name that you would be giving to your page. Names have to be unique so that there's no clash.defaultLocale- (Mandatory) You would provide a fall back locale here. The expectation is that there would be atleast one locale definition withinelementsthat matches the value ofdefaultLocale. Typical values could been_US(which suggests that for a particular element if there are no country specific locators defined, thenen_USis to be used as the fall back locale anden_USspecific locators could be used.)elements- (Mandatory) All your individual html elements that make up your page would go here. Every element would have the following attributes.name- (Mandatory) This is a user friendly name that you would be giving to your html element. Here again names have to be unique so that there's no clash. Typical e.g., could includeloginBtn(or)userNameTxtField.locale- (Mandatory) Represents the list of countries and their corresponding locators. Its attributes are described as below.name- (Mandatory) The name/key of the country for which the locator is being defined.locator- (Mandatory) The actual locator which can be an xPath (or) css locator (or) id/name. Here's some conventions associated with this :- xpath format : Can be of the form
./input[@name='first_name'](or)//input[@name='first_name'](or)/input[@name='first_name'] - css format : Can be of the form
css=input[name='first_name'] - id (or) name format : Can be of the form
first_name{ herefirst_namecan either be theidof an element (or) thenameof an element. }
- xpath format : Can be of the form
wait- (Optional) If you feel that a particular element is either a slow loading element (or) you would need to have some extra waits defined, then those go here. It can have the following attributes.until- We currently support only one of the following values:Available- (case in-sensitive) Waits for the element to be available in the Dom. Its functionally equivalent toExpectedConditions#presenceOfElementLocated(or)ExpectedConditions#presenceOfAllElementsLocatedBy.Visible- (case in-sensitive) Waits for the element to be available in the Dom and also to be visible. Its functionally equivalent toExpectedConditions#visibilityOfElementLocated(or)ExpectedConditions#visibilityOfAllElementsLocatedBy.Clickable- (case in-sensitive) Waits for the element to be available in the Dom and also to be clickable. Its functionally equivalent toExpectedConditions#elementToBeClickable.
for- An integer that represents the wait time in seconds. If no value is specified (or) if this attribute is omitted butuntilis included, the system falls back to45 seconds.This value can be altered globally via the JVM argumentdefault.wait.time.
Lets say we have the below json sample which is located in src/test/resources/HomePage.json.
{
"name": "HomePage",
"defaultLocale": "en_US",
"elements": [
{
"name": "heading",
"locale": [
{
"name": "en_US",
"locator": "//h1[@class='heading']"
}
],
"wait": {
"until": "visible",
"for": 45
}
},
{
"name": "checkboxesLink",
"locale": [
{
"name": "en_US",
"locator": "//a[text()='Checkboxes']"
}
],
"wait": {
"until": "clickable",
"for": 45
}
}
]
}A sample code that would work with the above mentioned would look like below :
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.List;
public class HerokkuAppTest {
private RemoteWebDriver driver;
@BeforeClass
public void setup() {
driver = new ChromeDriver();
}
@Test
public void testMethod() {
driver.get("http://the-internet.herokuapp.com/");
PageObject homePage = new PageObject(driver, "src/test/resources/HomePage.json");
Label heading = homePage.getLabel("heading");
Assert.assertEquals(heading.getText(), "Welcome to the Internet");
Link checkbox = homePage.getLink("checkboxesLink");
Assert.assertEquals(checkbox.isDisplayed(), true);
}
@AfterClass
public void cleanup() {
if (driver != null) {
driver.quit();
}
}
}Lets say we have a json that looks like below located at src/test/resources/CheckboxPage.json
{
"name": "CheckboxPage",
"defaultLocale": "en_US",
"elements": [
{
"name": "checkbox",
"locale": [
{
"name": "en_US",
"locator": "//input[@type='checkbox']"
}
],
"wait": {
"until": "visible",
"for": 45
}
}
]
}A sample code that would work with the above mentioned would look like below (working with list of elements) :
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.List;
public class HerokkuAppTest {
private RemoteWebDriver driver;
@BeforeClass
public void setup() {
driver = new ChromeDriver();
}
@Test
public void testMethod() {
driver.get("http://the-internet.herokuapp.com/checkboxes");
PageObject checkboxPage = new PageObject(driver, "src/test/resources/CheckboxPage.json");
List<Checkbox> checkboxList = checkboxPage.getCheckboxes("checkbox");
Assert.assertEquals(checkboxList.size(), 2);
int uncheckedCount = 0;
int checkedCount = 0;
for (Checkbox checkbox : checkboxList) {
if (checkbox.isChecked()) {
checkedCount += 1;
} else {
uncheckedCount += 1;
}
}
Assert.assertEquals(checkedCount, 1);
Assert.assertEquals(uncheckedCount, 1);
}
@AfterClass
public void cleanup() {
if (driver != null) {
driver.quit();
}
}
}Here's a sample that shows how to create a PageObject for the locale en_FR.
public class HerokkuAppTest {
@Test
public void testMethod() {
//Here we are creating a page object instance for the locale "en_FR"
PageObject homePage = new PageObject(driver, "src/test/resources/HomePage.json").forLocale("en_FR");
//More source code goes here.
// ..
}
}Many times we may have to work with generic elements (such as date picker or a scroll bar etc) for which Simple Se
may not have anything to offer as a ready made object. But you can still regard them as GenericElement and work
with them. Here's a sample:
public class HerokkuAppTest {
@Test
public void testMethod() {
PageObject homePage = new PageObject(driver, "src/test/resources/HomePage.json").forLocale("en_FR");
GenericElement datePicker = homePage.getGenericElement("datePickerControl");
}
}