Chapter 8. Defining Page Objects

8.1. Using pages in a step library
8.2. Opening the page
8.3. Working with web elements
8.4. Working with Asynchronous Pages
8.5. Executing Javascript
8.6. Uploading files
8.7. Using Fluent Matcher expressions
8.8. Running several steps using the same page object

If you are working with WebDriver web tests, you will be familiar with the concept of Page Objects. Page Objects are a way of isolating the implementation details of a web page inside a class, exposing only business-focused methods related to that page. They are an excellent way of making your web tests more maintainable.

In Thucydides, page objects can be just ordinary WebDriver page objects, on the condition that they have a constructor that accepts a WebDriver parameter. However, the Thucydides PageObject class provides a number of utility methods that make page objects more convenient to work with, so a Thucydides Page Object generally extends this class.

Here is a simple example:

@DefaultUrl("http://localhost:9000/somepage")
public class FindAJobPage extends PageObject {

    WebElement keywords;
    WebElement searchButton;

    public FindAJobPage(WebDriver driver) {
        super(driver);
    }

    public void look_for_jobs_with_keywords(String values) {
        typeInto(keywords, values);
        searchButton.click();
    }

    public List<String> getJobTabs() {
        List<WebElement> tabs = getDriver().findElements(By.xpath("//div[@id='tabs']//a"));
        return extract(tabs, on(WebElement.class).getText());
    }
}

The typeInto method is a shorthand that simply clears a field and enters the specified text. If you prefer a more fluent-API style, you can also do something like this:

@DefaultUrl("http://localhost:9000/somepage")
public class FindAJobPage extends PageObject {
        WebElement keywordsField;
        WebElement searchButton;

        public FindAJobPage(WebDriver driver) {
            super(driver);
        }

        public void look_for_jobs_with_keywords(String values) {
            **enter(values).into(keywordsField);**
            searchButton.click();
        }

        public List<String> getJobTabs() {
            List<WebElement> tabs = getDriver().findElements(By.xpath("//div[@id='tabs']//a"));
            return extract(tabs, on(WebElement.class).getText());
        }
}

You can use an even more fluent style of expressing the implementation steps by using methods like find, findBy and then.

For example, you can use webdriver "By" finders with element name, id, css selector or xpath selector as follows:

page.find(By.name("demo")).then(By.name("specialField")).getValue();

page.find(By.cssSelector(".foo")).getValue();

page.find(By.xpath("//th")).getValue();

You can also use findBy method and pass the css or xpath selector directly. For example,

page.findBy("#demo").then("#specialField").getValue(); //css selectors

page.findBy("//div[@id='dataTable']").getValue(); //xpath selector