Skip to content
This repository has been archived by the owner on Nov 23, 2021. It is now read-only.

PageObject

kkarolk edited this page Aug 31, 2016 · 6 revisions

Page Object and Page Factory

Introduction

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.

@PageObject locators and @FindPageObject

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.

@FindBy annotations

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.

Nested page objects

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.

@FindBy

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: page object image

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!

@CurrentScope

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:

page object image

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

@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;
  
}

Getting started with Bobcat

  1. Getting started

AEM Related Features

  1. Authoring tutorial - Classic
  1. AEM Classic Authoring Advanced usage
  1. Authoring tutorial - Touch UI
Clone this wiki locally