Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Selenium 3 support in Confluence Enterprise

After a fun year of ups and downs, backwards and sideways, I am happy to announce that we finally brought Selenium 3 support to Confluence Enterprise development !

In this article, I am going to cover some of my team’s best practices around plugin development and share examples of how to run Selenium 3 tests using Confluence’s Stateless Test Runner. I will also share simple build configurations to get those Selenium 3 tests up and running on Bitbucket Pipelines

 

note: All of the examples assume the use of Maven, but the concepts are transferable to other build tools

project structure

If you are building a Confluence plugin, I would recommend to structure its modules the following way:

project-root
> plugin
> integration-tests

This is because we want to have more control over our production and integration test dependencies; it also gives us the flexibility to upgrade one before the other.

We typically define our dependency management as follows:

- plugin
...
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence-plugins-platform-pom</artifactId>
            <version>${confluence.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
- integration-tests
...
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence-plugins-platform-test-pom</artifactId>
            <version>${confluence.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

I understand that this is not the default project structure you get when using atlas-create-confluence-plugin command from the Atlassian Plugin SDK; but while the team is revisiting the existing tooling to account for our revised development best practices, you can refer to the following repo as boilerplate for all your future plugins atlassian/confluence-devrel-plugin.

setup an initial build configuration

Using Bitbucket Pipeline Maven starter, we end up with a simple configuration which we have to tweak a little. It means that we have to use a different maven docker image that specifies the JDK in use ... otherwise, the default image uses latest JDK … which Confluence is yet to add support for.

This translates to using either one of the following images based on the Confluence version we want to test our plugin against.

  • version < 7.1     : 3.6.3-jdk-8-slim
  • version >= 7.1  : 3.6.3-jdk-11-slim
image: maven:3.6.3-jdk-11-slim

pipelines:
  default:
    - parallel:
      - step:
          name: Confluence 7.4.6
          caches:
            - maven
          script:
            - mvn -B verify --file pom.xml -Dconfluence.version=7.4.6
          after-script:
            - pipe: atlassian/checkstyle-report:0.2.0
      - step:
          name: Security Scan
          script:
            - pipe: atlassian/git-secrets-scan:0.4.3

In order for the build to resolve all the required dependencies, we need to ensure that our Maven project is configured to consume the Atlassian public package repositories.

<repositories>
    <repository>
        <id>atlassian-public</id>
        <url>https://packages.atlassian.com/mvn/maven-external/</url>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
        </snapshots>
        <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
        </releases>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>atlassian-public</id>
        <url>https://packages.atlassian.com/mvn/maven-external/</url>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
        </snapshots>
        <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
        </releases>
    </pluginRepository>
</pluginRepositories>

selenium 3 support

I am going to skip over our Frontend Development best practices which involve using React to build new UI components in Confluence Enterprise … because it really deserves its own article; instead I am going to touch on using Confluence Stateless Test Runner to write web-driver integration tests.

We need to enrich our intergation-tests module definition with the following dependencies

<properties>
    <atlassian.selenium.and.webdriver.version>3.1.5</atlassian.selenium.and.webdriver.version>
</properties>
...

<!--
  we need to override the versions of these dependencies
  as they are yet to be included in our platform-pom
  note that these versions bring in support for Selenium 3
-->
<dependency>
  <groupId>com.atlassian.confluence</groupId>
  <artifactId>confluence-stateless-test-runner</artifactId>
  <version>8.0.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.atlassian.confluence</groupId>
  <artifactId>confluence-webdriver-pageobjects</artifactId>
  <version>11.0.3</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.atlassian.selenium</groupId>
  <artifactId>atlassian-webdriver-core</artifactId>
  <version>${atlassian.selenium.and.webdriver.version}</version>
  <scope>test<scope>
</dependency>
<dependency>
  <groupId>com.atlassian.selenium</groupId>
  <artifactId>atlassian-pageobjects-api</artifactId>
  <version>${atlassian.selenium.and.webdriver.version}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.atlassian.selenium</groupId>
  <artifactId>atlassian-pageobjects-elements</artifactId>
  <version>${atlassian.selenium.and.webdriver.version}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.atlassian.browsers</groupId>
  <artifactId>atlassian-browsers-auto</artifactId>
  <version>3.0.2</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>26.0-jre</version>
  <scope>test</scope>
</dependency>

To generate fake content, we like to use DiUS/java-faker library, it allows us to rely on  different locales, and therefore uncover any encoding issues.

<!-- fake data -->
<dependency>
    <groupId>com.github.javafaker</groupId>
    <artifactId>javafaker</artifactId>
    <version>1.0.2</version>
    <scope>test</scope>
</dependency>

Confluence Stateless Test Runner has also some dependencies that are required at runtime; these are a set of functional tests plugins that need to be installed on Confluence. So in order for the test runner to resolve them properly, they need to be available on the classpath too:

<!-- needed by stateless-test-runner at runtime -->
<dependency>
    <groupId>com.atlassian.confluence.plugins</groupId>
    <artifactId>confluence-functestrpc-plugin</artifactId>
    <version>${confluence.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.atlassian.confluence.plugins</groupId>
    <artifactId>confluence-functest-rest-plugin</artifactId>
    <version>${confluence.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.atlassian.confluence.plugins</groupId>
    <artifactId>confluence-scriptsfinished-plugin</artifactId>
    <version>${confluence.version}</version>
    <scope>test</scope>
</dependency>
<!-- the version of this can varry based on the version of confluence,
     so be ready to tweak your build configs a little -->
<dependency>
    <groupId>com.atlassian.upm</groupId>
    <artifactId>atlassian-universal-plugin-manager-javascript-tests</artifactId>
    <version>4.0.13</version>
    <scope>test</scope>
</dependency>

With all these on the classpath, we can now write a simple test that creates a page, types types types, publishes the page, and asserts the content value.

I know … it is a lot of XML, but it unlocks precious lines of Java that will increase your confidence in the quality of your plugin.

This is what a simple web-driver test looks like.

@RunWith(ConfluenceStatelessTestRunner.class)
public class DevRelPageStatelessTest {

    @Inject
    private static ConfluenceTestedProduct product;

    @Fixture
    private static final UserFixture user = userFixture().build();
    @Fixture
    private static final UserFixture anotherUser = userFixture().build();
    @Fixture
    private static final SpaceFixture space = spaceFixture()
            .permission(user, REGULAR_PERMISSIONS)
            .permission(anotherUser, REGULAR_PERMISSIONS)
            .build();

    private static final Faker faker = Faker.instance();

    @Test
    public void editingAndPublishing() {
        final CreatePage createPage = product.loginAndCreatePage(user.get(), space.get());
        final String title = format("%s - %s", faker.rickAndMorty().character(), faker.rickAndMorty().location());
        final String content = faker.rickAndMorty().quote();

        createPage.setTitle(title);
        EditorContent editorContent = createPage.getEditor().getContent();
        editorContent.type(content);
        editorContent.type(Keys.ENTER.toString());

        ViewPage publishedPage = createPage.save();
        assertThat(publishedPage.getTitle(), equalTo(title));
        waitUntil(publishedPage.getRenderedContent().getTextTimed(), containsString(content));
    }

}

Note the usage of :

  • @Fixture which allows us to create all the data needed to orchestrate our test flow; these must be declared as static fields

  • @Inject which gives us access to services that allow us to navigate around Confluence and interact with its user interface

Without going into too much detail, this is how stateless test runner does its magic:

Screen Shot 2020-12-14 at 1.59.37 pm.png

I would recommend discovering our Stateless and WebDriver PageObjects API by poking around your IDE a little; for instance, look up implementations of:

  • com.atlassian.confluence.test.stateless.TestFixture to find out which fixtures you can create

Screen Shot 2020-12-14 at 2.18.26 pm.png

  • com.atlassian.confluence.webdriver.pageobjects.page.ConfluenceAbstractPage to find out which UI components you can navigate to

Screen Shot 2020-12-14 at 2.45.27 pm.png

 

selenium support on Bitbucket Pipelines

There are quite a few options to run selenium tests on Bitbucket Pipelines, for instance using browserstack, but the option I am describing here relies on using selenium-standalone docker images which are defined in SeleniumHQ/docker-selenium

We need to define our browsers as services:

definitions:
  services:
    chrome:
      image: selenium/standalone-chrome:3.11.0
    firefox:
      image: selenium/standalone-firefox:3.11.0

Then make use of those services in our build steps:

- step:
    size: 2x
    name: "[chrome] Confluence 7.4.6"
    caches:
      - maven
    services:
      - chrome
    script:
      - mvn -B verify --file pom.xml -Dxvfb.enable=false -Dwebdriver.browser="chrome:url=http://127.0.0.1:4444" -Dconfluence.version=7.4.6
    after-script:
      - pipe: atlassian/checkstyle-report:0.2.0

Notice the use of:

  • size: 2x , this is because we are adding a new service into the mix which shares parts of the resources with Confluence, so we need to throw more power onto our build agents. For more details please read up on supporting large builds in Bitbucket Pipelines.

  • -Dxvfb.enable=false -Dwebdriver.browser="chrome:url=http://127.0.0.1:4444" , these properties tell Confluence Stateless Test Runner how to access the remote selenium browser.

test reports on Bitbucket Pipelines

Now that we have our integration tests running, it would be nice to see them reported on the pipeline build results. According to test reporting in Bitbucket Pipelines , the Maven failsafe/surefire reports are expected to be available in one of the following directories:

./**/surefire-reports/**/*.xml
./**/failsafe-reports/**/*.xml
./**/test-results/**/*.xml
./**/test-reports/**/*.xml

… with a directory depth of 3 levels …

Looking at our default project structure and configuration, we can clearly see that the integration tests reports are generated as follows.

test-reports-levels (1).png

This means that we have to go back to our project definition, and make some more adjustments to our lovely XML.

We have to override where maven-failsafe-plugin stores its reports, we can just place them in the project’s root target directory:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>3.0.0-M5</version>
    <configuration>
        <reportsDirectory>${project.basedir}/../target/failsafe-reports/integration-tests</reportsDirectory>
    </configuration>
</plugin>

And we have to let confluence-maven-plugin know about our configuration override.

<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>confluence-maven-plugin</artifactId>
    <version>${amps.version}</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <id>integration-tests</id>
            <phase>integration-test</phase>
            <goals>
                <goal>integration-test</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <versionOverrides>
            <versionOverride>maven-failsafe-plugin</versionOverride>
        </versionOverrides>
        ...
    </configuration>
    ...
</plugin>

Voilà !

successful-tests.png

In case of test failures, our Stateless Test Runner is configured to capture screenshots which can help you understand and troubleshoot your tests. This is great, because we can update our pipelines configuration to extract them as artifacts !

...
- step:
    size: 2x
    name: "[chrome] Confluence 7.4.6"
    caches:
      - maven
    services:
      - chrome
    artifacts:
      - "integration-tests/target/webdriverTests/**/*.png"
...

Voilà !

Screen Shot 2020-12-16 at 2.15.53 pm.png

what's next ?

This was just an introduction to using Confluence’s Stateless Test Runner in order to run Selenium 3 WebDriver tests on Bitbucket Pipelines.

We will come back after the holiday season with more articles around the hidden gems of our team’s test libraries.

1 comment

Wow! Great post! Thanks @Hasnae 

Like Hasnae likes this

Comment

Log in or Sign up to comment
TAGS
Community showcase
Published in Confluence

Confluence Mythbusters: Does Atlassian even use Confluence?

Hi, Confluence collaborators! As part of #Confluence-Collaboratory month, we’ve created a very special Mythsbusters segment, where we're dive into an interesting myth and uncover the truth behind i...

1,457 views 7 29
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you