Tutorial - Writing integration tests for Confluence using PageObjects

Level of experience: Intermediate

Our tutorials are classified as 'beginner', 'intermediate' and 'advanced'. This one is at 'intermediate' level. If you have never developed a plugin before, you may find this one a bit difficult.

Overview

This tutorial shows you how to write PageObjects for your Confluence plugin's pages and use them in integration tests. It is inspired by the Writing integration tests using PageObjects tutorial that gives an example for Jira.

If you are not familiar with PageObjects, you can read this article

On this page:

Plugin Source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the plugin source code on Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, issue the following command:

 

$ git clone git@bitbucket.org:seuqra/confluence-pageobjects-integration-tests-tutorial.git

Alternatively, you can download the source using the Downloads page here: https://bitbucket.org/seuqra/confluence-pageobjects-integration-tests-tutorial

Step 1. Creating the plugin project

Use the atlas-create-confluence-plugin command to create your plugin.

When you create the plugin using the above SDK, you will be prompted for some information to identify your plugin. Enter the following information:

  • groupId: : com.seuqra.confluence.tutorial

  • artifactId: : confluence-pageobjects-integration-tests-tutorial

  • version:  1.0-SNAPSHOT
  • package: com.seuqra.confluence.tutorial

Step 2. Adding PageObjects dependencies to the POM

		<dependency>
			<groupId>com.atlassian.confluence</groupId>
			<artifactId>confluence-webdriver-pageobjects</artifactId>
			<version>${confluence-webdriver-pageobjects.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>${guava.version}</version>
            <scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-core</artifactId>
			<version>${hamcrest.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-library</artifactId>
			<version>${hamcrest.version}</version>
            <scope>test</scope>
		</dependency>

And set following versions:

        <hamcrest.version>1.3</hamcrest.version>
        <guava.version>13.0.1</guava.version>
        <confluence-webdriver-pageobjects.version>2.0.10</confluence-webdriver-pageobjects.version>

Step 3. Writing a PageObject for the Confluence Welcome Page

package it.com.seuqra.confluence.tutorial;
import com.atlassian.confluence.webdriver.pageobjects.page.ConfluenceAbstractPage;
import com.atlassian.pageobjects.elements.ElementBy;
import com.atlassian.pageobjects.elements.PageElement;
import com.atlassian.pageobjects.elements.query.TimedQuery;

public class WelcomeToConfluencePage extends ConfluenceAbstractPage {
	private static final String PREFIX = "/display/ds/"; 
	private static final String PAGE = "Welcome+to+Confluence"; 
	@ElementBy(id = "WelcometoConfluence-WithConfluenceitiseasytocreate,editandsharecontentwithyourteam.Chooseatopicbelowtostartlearninghow.")
	protected PageElement welcomeTitle;
	
	@Override
	public String getUrl() {
		return PREFIX+PAGE;
	}
	public TimedQuery<String> getWelcomeTitle() {
		return welcomeTitle.timed().getText();
	}
}
    • PageElement is a DOM element that you want to interact with (modify, click, read or write to)
    • The @ElementBy annotation defines how to search for the element; you can search by things like id, cssSelector, name, and tag
    • The getUrl method defined in ConfluenceAbstractPage indicates where to go to open the expected page
    • To avoid race conditions, your Page Object should always return a TimedQuery or TimedCondition.

You can type or click on elements. You can also get their attributes, search for page elements inside of page elements, and so on. What's great is that Page Objects are run in the real browser so you really imitate the user and their experience.

Step 4. Using PageObjects in your tests

package it.com.seuqra.confluence.tutorial;
import static com.atlassian.pageobjects.elements.query.Poller.*;
import static org.hamcrest.Matchers.*;
import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.atlassian.confluence.webdriver.pageobjects.ConfluenceTestedProduct;
import com.atlassian.confluence.webdriver.pageobjects.page.ConfluenceAbstractPage;
import com.atlassian.confluence.webdriver.pageobjects.page.ConfluenceLoginPage;
import com.atlassian.pageobjects.Page;
import com.atlassian.pageobjects.TestedProductFactory;
public class TestWelcomeToConfluencePage {
	private static ConfluenceTestedProduct confluenceTestedProduct = TestedProductFactory.create(ConfluenceTestedProduct.class);
	
	@BeforeClass
	public static void login() {
		// WORKAROUND: Method ConfluenceAbstractPage.doWait() raises always an exception because the condition "return typeof window.ajsScriptsFinishedLoading != 'undefined'" is never true
		// If setupComplete variable is false, this condition is never checked :)
		ConfluenceAbstractPage.setSetupComplete(false);
		ConfluenceLoginPage confluenceLoginPage = confluenceTestedProduct.gotoLoginPage();
		confluenceLoginPage.login("admin", "admin", true);
	}
	
	@AfterClass
	public static void logout() {
		confluenceTestedProduct.logOut();
	}
	
	@Test
	public void testWelcomePageTitle() {
		Page page = confluenceTestedProduct.getPageBinder().navigateToAndBind(WelcomeToConfluencePage.class);
		WelcomeToConfluencePage welcomeToConfluencePage = (WelcomeToConfluencePage) page;
		waitUntil(welcomeToConfluencePage.getWelcomeTitle(), (Matcher<String>) containsString("With Confluence it is easy to create, edit and share content with your team."));
	}
}
  • The pageBinder object is responsible for binding the Page Object to the DOM.
  • Use Poller.waitUntil to check the assertion. This will poll the Page Object until the assumption is met or the timeout has passed. This is great if you have elements that are dynamically created, or you use AJAX to retrieve data. It will make sure that there are no race conditions in your code.
  • Use org.hamcrest.Matchers.* and org.hamcrest.CoreMatchers.* predefined matchers as parameter of waitUntil method.

Step 5. Running your tests

  1. Go to a command line.
  2. Make sure you are in at the top level PLUGIN_HOME directory for you plugin.
  3. Enter the atlas-integration-test command:

$ atlas-integration-tests

Notice in command's output, the system runs the unit tests, starts the host application, and then runs your integration tests.  The command output should succeed with test output similar to the following:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.com.seuqra.confluence.tutorial.TestWelcomeToConfluencePage
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 51.976 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

Step 6. Adjusting PageObjects timeouts

By default, Pageobjects for Confluence use the default timeout values defined in class com.atlassian.pageobjects.elements.timeout.DefaultTimeouts:

# All but EVALUATION_INTERVAL are timeouts. EVALUATION_INTERVAL is the polling interval.
com.atlassian.timeout.EVALUATION_INTERVAL=100
# setting timeouts below 1000ms is not recommended!
# The following values are the one defined in com.atlassian.pageobjects.elements.timeout.DefaultTimeouts
com.atlassian.timeout.DEFAULT=5000
com.atlassian.timeout.UI_ACTION=1000
com.atlassian.timeout.PAGE_LOAD=20000
com.atlassian.timeout.SLOW_PAGE_LOAD=20000
com.atlassian.timeout.DIALOG_LOAD=5000
com.atlassian.timeout.COMPONENT_LOAD=3000
com.atlassian.timeout.AJAX_ACTION=5000

If you need to increase the timeout of Poller.waitUntil method, create a properties file named com/atlassian/timeout/pageobjects-timeouts.properties, put all the above properties and increase the value of com.atlassian.timeout.DEFAULT to 10000:

com.atlassian.timeout.EVALUATION_INTERVAL=100
 
com.atlassian.timeout.DEFAULT=10000
com.atlassian.timeout.UI_ACTION=1000
com.atlassian.timeout.PAGE_LOAD=20000
com.atlassian.timeout.SLOW_PAGE_LOAD=20000
com.atlassian.timeout.DIALOG_LOAD=5000
com.atlassian.timeout.COMPONENT_LOAD=3000
com.atlassian.timeout.AJAX_ACTION=5000