-
Notifications
You must be signed in to change notification settings - Fork 40
PageObject
Selenium references: https://github.com/SeleniumHQ/selenium/wiki/PageFactory https://github.com/SeleniumHQ/selenium/wiki/PageObjects
PageObject is a central concept of the Bobcat. It's a term derived from Selenium framework and it means a class that encapsulates page HTML and exposes page features:
Page object class example:
public class LoginPage {
@Inject
private WebDriver webDriver;
public void login(String username, String password) {
webDriver.findElement(By.id("username")).sendKeys(username);
webDriver.findElement(By.id("password")).sendKeys(password);
webDriver.findElement(By.cssSelector("button[type=submit]")).click();
}
}
Page HTML details are hidden in the login() method, so user doesn't have to know all fields identifiers, etc.
Below you can find a decription how to use PageObject annotation with @FindBy. However in many cases we can use more simple way by using locators. If we know css or xpath for our page object we can use it with our @PageObject
Here are two examples
@PageObject(css = "div.taglabel")
@Frame("$cq")
public class AemTagItem {
...
}
@PageObject(xpath = "//div[contains(@class, 'x-grid3-body')]/div")
public class SidekickGridRow {
...
}
We have locator set in Page Object it is very easy to find it. We just have to use @FindPageObject.
@PageObject
@Frame("$cq")
@DialogComponent("tags")
public class AemTags implements Iterable<AemTagItem>, Configurable {
...
@FindPageObject
private List<AemTagItem> items;
...
}
We our page object has locator set and we inject it using @FindPageObject then Bobcat will find it for us.
Writing webDriver.findElement(...) is not very efficient. Luckily, Selenium provides the PageFactory util class that allows us to use @FindBy annotations. You don't have to call the PageFactory manually. If you annotate the class with @PageObject, Bobcat will do it for you:
@PageObject // we need this so Bobcat takes care of @FindBy fields
public class LoginPage {
@FindBy(id = "username")
private WebElement usernameField;
@FindBy(id = "password")
private WebElement passwordField;
@FindBy(css = "button[type=submit]")
private WebElement submitButton;
public void login(String username, String password) {
usernameField.sendKeys(username);
passwordField.sendKeys(password);
submitButton.click();
}
}
In this case we use FindBy annotation in the LoginPage to inject another page object: LoginBox. The FindBy annotations in the LoginBox looks for WebElements inside the div#login-box rather than in the whole page. The div#login-box can be called the LoginBox scope.
Generally it's a good practice and one of basic Object Oriented principals to have classes with limited responsibility. But FindBy annotation and webdriver.findBy(...) method uses global browser scope when seeking for elements. That is why, you may want to narrow scope for locating elements in PageObject class.
Sometimes it may happen that HTML structure is too complicated to be represented by one class and it makes sense to split it into multiple classes. Consider a following page:
We'll split its markup in two parts: the outer HTML will be represented as LoginPage and the inner as LoginBox. Bobcat allows to nest one page object into another:
@PageObject
public class LoginPage {
@FindBy(css = "div#login-box")
private LoginBox loginBox;
}
@PageObject
public class LoginBox {
@FindBy(id = "username")
private WebElement usernameField;
@FindBy(id = "password")
private WebElement passwordField;
@FindBy(css = "button[type=submit]")
private WebElement submitButton;
}
Why is it so cool? The coolest part is under the hood: usernameField, passwordField and submitButton WebElements are located in scope limited to div#login-box element!
What about a WebElement? Is it possible to have div#login-box as a WebElement that I can access to? Something like this object but for WebElements?
Let's go back to our login page once more, but this time bare in mind that with Bobcat we can limit the scope for locating elements:
With @CurrentScope annotation we have an access to WebElement which represents the same limited scope that was used for locating @FindBy elements. In simple words:
WebElement scope = webdriver.findElement(By.cssSelector("div#login-box"))
will represent the same element as WebElement annotated with @CurrentScope:
@PageObject
public class LoginBox {
@Inject
@CurrentScope
private WebElement currentScope; // it will be the div#login-box
}
@Global annotation is the exact opposite to @CurrentScope. You may want to use it when there is a need to ignore scope limitation. But be careful and use it wisely. Possible use case: floating dialog.
@PageObject
public class LoginBox {
@Global
@FindBy(css = ".floating.dialog")
private WebElement floatingDialog;
}
- Configuring Bobcat
- Selenium enhancements
- Cucumber enhancements
- Traffic analyzer
- Email support
- Reporting
- Cloud integration
- Mobile integration
- Executing tests on different environments
- Working with multiple threads
- Tips and tricks
- Authoring tutorial - Classic
- AEM Classic Authoring Advanced usage
- Siteadmin
- Sidekick
- Aem Component
- Working with author pages
- Working with Publish pages
- Advanced component interactions
- Working with Context Menu
- Using Aem Content Tree
- Aem Content Finder
- Storing component configurations
- Working with packages
- Jcr Support
- Authoring tutorial - Touch UI
- Adding and editing a component
- Sites management tutorial