Graphene 2.0.0.Alpha5 Released

Since we wrote this post we didn't laze around. Check our latest announcement.

The Arquillian team is proud to announce the 2.0.0.Alpha5 release of the Graphene component!

The last step before Beta release brings you a lot of new features.


Highlighted Changes

Deprecations in this release

With the support of the community, we have recognized that some parts of the API needs to be enhanced to achieve better clarity, readability and consistency. The old parts are deprecated in this release, and will be removed in the upcoming Beta1 release. (list of deprecations here)

Guard Improvements and Fixes

RequestGuard interface is now exposed as an API and brings new methods for filtering the requests that should be intercepted. This is especially useful in cases where your page does asynchronously communicate with a server (using Comet/Push method) which can make your tests undeterministic – the asynchronously invoked request can interfere with a request invoked synchronously (e.g. by clicking on a button) and Graphene can’t guess which request should be observed. With the RequestGuard API you can ignore the request which you’re not interested in guarding. (JavaDoc)

Support for parallel browser sessions

The Graphene context is no longer thread-local which enables parallel browser sessions. In other words; you can control two different browsers in one test. (an example here)

Page Objects can encapsulate their location

You can easily denote the Page Objects location via the @Location annotation. When used with @InitialPage annotation as a test parameter, Graphene will load that location as a first action for the test, and will also give you the initialized page object. (an example here)

Injecting elements from HTML frames/iframes

To facilitate the tedious process of switching to and from a particular frame/iframe; you can now just use the @InFrame annotation to specify in which frame the element should be located. The details are handled by Graphene. (an example here)

Page Fragments can implement the WebElement interface

If you want to give your Page Fragments all the capabilities of the WebElement interface, without implementing them youself; you can simply add implements WebElement and make your fragment abstract. Graphene will provide you with a proxy that delegates all the WebElement method invocations to your page fragment’s @Root element.

Drone enrichers for Page Fragments and Page Objects

It is now possible to inject objects into your Page Fragments and Page Objects via the standard Arquillian enrcihment annotations: e.g. @Drone to get the browser instance, @ArquillianResource to get resources like URL, JavascriptExecutor, LocalStorage or others.

Introduction of the GrapheneElement

GrapheneElement is our way of providing you with the missing methods of the WebElement interface. GrapheneElement can be used anywhere the WebElement is used. Currently the only addition is the isPresent method, but we plan to add far more in the upcoming releases.

Introduction of the @FindByJQuery annotation

This is the replacement for the deprecated @FindBy annotation. Read more about it in the deprecations section.

Drone upgraded to 1.2.0.Beta1

For more info, please see the Drone release annoucement here.

Removed

Graphene.guardXhr()

Graphene.guardXhr() was replaced by Graphene.guardAjax().

Deprecations (will be removed in Beta1)

Our implementation of the @FindBy annotation

To achieve better extendability and to avoid errors caused by mistyped package name, we decided to deprecate our implementation of the @FindBy annotation (read more). Its purpose was solely to add support for JQuery locating strategy. This is now replaced by the @FindByJQuery annotation. The main advantage of the new approach is its extensibility. Support for our own location strategies are guaranteed via LocationStrategy interface.

Package name changes for @Page and @Root annotations
  • org.jboss.arquillian.graphene.spi.annotations.Root replaced by org.jboss.arquillian.graphene.fragment.Root
  • org.jboss.arquillian.graphene.spi.annotations.Page replaced by org.jboss.arquillian.graphene.fragment.Page
Selenium 1 support

Selenium 1 support will be removed in the upcoming Beta1 release, but you can still use Graphene 1, which is built on top of Selenium 1. (Graphene 1 docs)

Parallel browser sessions

Your test can now use more than one browser session. It might be handy for testing e.g. server push functionality. Consider the following scenario:

@Browser1 
@Drone
private WebDriver browser1;

@Browser2
@Drone
private WebDriver browser2;

public void testServerPushFromFirefoxToChromeAfterRegistration() {
    browser1.get(“http://localhost:8080/myApp”);
    browser2.get(“http://localhost:8080/myApp”);

    formFragment.registerUser(new User(“John”), browser1);
    registeredUsers.assertContains(“John”, browser2);
}

The @Browser1 and @Browser2 annotations are qualifiers, similar concept as in CDI. The settings for these browsers can be set e.g. in the arquillian.xml:

<extension qualifier="webdriver-browser1">
  <property name="browserCapabilities">firefox</property>
</extension>
<extension qualifier="webdriver-browser2">
  <property name="browserCapabilities">chrome</property>
</extension>

Page Objects location encapsulated

First thing you have to keep in mind before interacting with the page is that its loading in the browser. Once the URL of the particular page changes, you will have to change it on several places in your tests.

Graphene, therefore, encapsulates the page’s location, and provide you with a way to load the page automatically on a correct context root, before the test execution.

Consider following snippet:

@Location("register-page.html")
public class RegisterPage {
    //implementation of the page object goes here
}

@Test
public void testRegisterPage(@InitialPage RegisterPage page) {
   //Graphene automatically load MyPageObject location in the browser
   page.getInput().sendKeys(“Blah foo”);
   page.submit();
   assertFalse(page.isSubmitted());
}

You can also leverage Graphene.goTo(RegisterPage.class) method, which will use the @Location annotation value to load the correct page in the browser.

Elements from frames/iframes

If you find working with frames/iframes tedious using the WebDriver API, we have some good news for you. Graphene is now able to specify in which frame/iframe an element reside. Consider the following snippet:

@Page
@InFrame(index = 0)
private PageObject page;

@FindBy(id = "foo-bar")
@InFrame(nameOrId = "second")
private PageFragment myFragment;

You can specify the frame either by its index or its nameOrId strategy. Graphene will automatically switche to the coresponding frame when interacting with the page object and switches back after the interaction.

Roadmap

We have introduced all the features for the upcoming Graphene 2.0 release.

The Beta1 release will be pushed in a few days which will bring you some more goodies:

  • Arquillian guide
  • finished reference documentation
  • online JavaDoc documentation for Graphene API
  • removal of deprecated APIs
  • stabilization
  • usual portion of bug fixes

Be sure to remove deprecated APIs in order to stay compatible with upcoming Graphene 2.0 releases.

What is Arquillian?

Arquillian is open source software that empowers you to test JVM-based applications more effectively. Created to defend the software galaxy from bugs, Arquillian brings your test to the runtime so you can focus on testing your application's behavior rather than managing the runtime. Using Arquillian, you can develop a comprehensive suite of tests from the convenience of your IDE and run them in any IDE, build tool or continuous integration environment.

Release details

Component Graphene
Version 2.0.0.Alpha5 view tag
Release date 2013-09-03
Released by Lukas Fryc
Compiled against

Published artifacts org.jboss.arquillian.graphene

  • org.jboss.arquillian.graphene » graphene-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-api jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium pom
  • org.jboss.arquillian.graphene » graphene-webdriver-parent pom
  • org.jboss.arquillian.graphene » graphene-webdriver-api jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver-spi jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver pom
  • org.jboss.arquillian.graphene » arquillian-graphene pom
  • org.jboss.arquillian.graphene » graphene-component-api jar javadoc pom

Release notes and resolved issues 32

Q3/13: SauceLabs; Context Propagation (Multiple Browsers Support); Continuous Integration

Component Upgrade
  • ARQGRA-323 - Update Arquillian Drone to 1.2, Arquillian Core to 1.1 and Selenium to 2.35
Enhancement
  • ARQGRA-236 - Made @FindBy annotation lookup mechanism extensible
  • ARQGRA-275 - Implement Guard observers which will filter out which request we want to intercept
  • ARQGRA-336 - Introduce FindByJQuery annotation
Feature Request
  • ARQGRA-72 - Support parallel browser sessions API for Selenium 2
  • ARQGRA-170 - Merge all static contexts to one (GrapheneContext)
  • ARQGRA-197 - Support for Page Objects encapsulating location
  • ARQGRA-224 - Page Fragments implementing WebElement delegate interface invocations to Root
  • ARQGRA-243 - Infer Graphene API from Impl module
  • ARQGRA-264 - Create possibility to "inject" elements from frames
  • ARQGRA-279 - Support for method Graphene.element(WebElement, By) and its condition isPresent
  • ARQGRA-291 - Provide wait until element is enabled
  • ARQGRA-296 - Provide a way for checking whether an element is present
  • ARQGRA-306 - Guards: Provide settings for customizing maximum timeout for callbacks scheduled in XHR onreadystatechange
Epic
  • ARQGRA-271 - The context should not be shared in static context, but propagated from the test as central point to the users (e.g. page fragments)
  • ARQGRA-276 - GhostDriver / PhantomJS support
  • ARQGRA-281 - Guard Improvements
  • ARQGRA-285 - Page Abstract. Improvements
  • ARQGRA-294 - Introduce an own implementation of WebElement
Bug
  • ARQGRA-274 - The request guard does timeout for delayed requests
  • ARQGRA-300 - Guard Ajax ends with timeout exception
  • ARQGRA-304 - The interceptor for handling StaleElementReferenceException is not present
  • ARQGRA-324 - ByChained doesn't work when jquerySelector is used
  • ARQGRA-327 - Graphene depends on deprecated interactions interfaces (Keyboard, Mouse, TouchScreen) removed in selenium 2.35.0
Task
  • ARQGRA-299 - Setup Graphene tests on SauceLabs
  • ARQGRA-339 - Deprecate Graphene's FindBy and How classes
Sub-task

Thanks to the following list of contributors: Lukas Fryc, Jan Papoušek, Juraj Huska, Marek Schmidt, Karel Piwko

Graphene 2.0.0.Alpha4 Released

Since we wrote this post we didn't laze around. Check our latest announcement.

The Arquillian team is proud to announce the 2.0.0.Alpha4 release of the Graphene component!

As we are on the way to a first Beta release, we identified a need for sharing some of the recent Graphene improvements:

Highlighted Changes

Guard Improvements and Fixes

Request Guards were polished, extended and hardened. (read more)

Creating Page Fragments Programatically

Page Fragments can now be instantiated not only using dependency injection, but also programatically. (read more)

PhantomJS Support

You can now fully leverage awesomness of headless testing with PhantomJS.

Guarding Programatically Retrieved Elements

Elements retrieved programatically using WebElement#findElement(...) or WebElement#findElements(...) are now guarded against StaleElementReferenceException.

Automatic Inference of Locators

You no longer need to define a locator in simple forms – you can leverage their automatic inference from a injection point name. (read more)

Dependency Injection of SessionStorage and LocalStorage

These resources are now exposed directly via @ArquillianResource.

Dependency Injection of Selenium Resource Parametrized by WebElement

Selenium resources which takes WebElement as an argument in a constructor (e.g. Select) can be injected using FindBy.

Support for FindBys and FindBy(How, String)
 
Improved Integration with Drone

Drone 1.2.0.Alpha2’s new Enhancer API allow us to integrate with Drone seamlessly.

Deprecations

Deprecation of Old Variant of Waiting Fluent API

Graphene.element(...) and Graphene.attribute(...) are now deprecated and they will be removed in an upcoming releases.

Deprecated guardXhr replaced by guardAjax

Not all of us are familiar with abbreviation XHR (XMLHttpRequest). In order to make the API more clear for most of Graphene users, we have deprecated guardXhr and replaced it with guardAjax. guardXhr will be removed in upcoming releases.

Guard Improvements and Fixes

waitForHttp

When an Ajax request is followed by a relocation (HTTP request), guardXhr or guardHttp can’t deterministically wait for an end of a request – in these situations you can use waitForHttp instead.

Testing delayed requests

Guards now waits for a given time interval for a request to start and once the request is started, they wait the another interval for the request to finish.

Bug Fixes

Guards had problems on Android with deterministic waiting for HTTP requests.

Creating Page Fragments Programatically

Till this release, the only option to create a page fragment was injecting it:

@FindBy(...)
MyComponent component;

With Alpha4 we have added the possibility to create a page fragment programatically:

public <T> T getContent(Class<T> clazz) {
    return PageFragmentEnricher.createPageFragment(clazz, root);
}
@Test
public void testTabPanelSwitching() {
    Panel tab3 = tabPanel.switchTo(2);
    ContentOfTab content = tab3.getContent(ContentOfTab.class);
    assertEquals("The tab panel was not switched to third tab correctly!", "Content of the tab 3", content.text.getText());
    
    Panel tab1 = tabPanel.switchTo(0);
    content = tab1.getContent(ContentOfTab.class);
    assertEquals("The tab panel was not switched to first tab correctly!", "Content of the tab 1", content.text.getText());
}

You can find reference usage in this functional test together with an implementation of #getContent(Class<T>).

Automatic Inference of Locators

How many times you have written:

// look for input with a name 'firstname'
@FindBy(name = "firstname")
private WebElement firstname;

You can now simplify your tests to just

@FindBy
private WebElement firstname;

Graphene will automatically use the strategy How.ID_OR_NAME to locate the element by its ID or name.

Since this mechanism uses the strategy pattern, you can overwrite the default strategy for your test suite and therefore find elements by e.g. their class names or even JSF component IDs.

Roadmap

This release is a maintanance release on the way to Beta1.

What is Arquillian?

Arquillian is open source software that empowers you to test JVM-based applications more effectively. Created to defend the software galaxy from bugs, Arquillian brings your test to the runtime so you can focus on testing your application's behavior rather than managing the runtime. Using Arquillian, you can develop a comprehensive suite of tests from the convenience of your IDE and run them in any IDE, build tool or continuous integration environment.

Release details

Component Graphene
Version 2.0.0.Alpha4 view tag
Release date 2013-04-12
Released by Lukas Fryc
Compiled against

Published artifacts org.jboss.arquillian.graphene

  • org.jboss.arquillian.graphene » graphene-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-api jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium pom
  • org.jboss.arquillian.graphene » graphene-webdriver-parent pom
  • org.jboss.arquillian.graphene » graphene-webdriver-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver pom
  • org.jboss.arquillian.graphene » graphene-webdriver-spi jar javadoc pom
  • org.jboss.arquillian.graphene » arquillian-graphene pom
  • org.jboss.arquillian.graphene » graphene-component-api jar javadoc pom

Release notes and resolved issues 20

Q1/13: PhantomJS

Enhancement
  • ARQGRA-199 - Provide a way to create Page Fragments dynamically
  • ARQGRA-235 - Automatically infer ID locator from field name annotated just by @FindBy
  • ARQGRA-250 - Shortcut for waiting on element's attribute value
Feature Request
  • ARQGRA-220 - Locating elements with @FindBy(how = How.ID, using = "foobar") not working
  • ARQGRA-247 - Add support for enriching @FindBys annotations
  • ARQGRA-254 - Expose LocalStorage and SessionStorage enrichments directly
  • ARQGRA-258 - Provide injecting classes requiring WebElement in their constructors via @FindBy annotation
  • ARQGRA-259 - Provide timeout setting in fluent API
  • ARQGRA-273 - Intercept WebDriver to return proxies in findElement()/findElements() methods
  • ARQGRA-288 - Remove need for Drone factory wrappers
Bug
  • ARQGRA-257 - Guards are not working with AndroidDriver
  • ARQGRA-262 - Element click with HTTP guard causes WebDriverException: ReferenceError: Graphene is not defined
  • ARQGRA-266 - Waiting for presence of element defined by jQuery selector sometimes causes "IllegalStateException: JQueryPageExtension can't be installed"
  • ARQGRA-272 - Introduced waitForHttp (guardXhr does not work for redirected pages)
  • ARQGRA-289 - JavaScript interfaces fails on Chrome and PhantomJS
  • ARQGRA-290 - testAttributeIsPresent fails on Chrome and PhantomJS
Story
  • ARQGRA-286 - Support PhantomJSDriver in Graphene
Task
  • ARQGRA-168 - Create QUnit tests for Graphene.Page.RequestGuard.js
  • ARQGRA-284 - Rename guardXhr to guardAjax
  • ARQGRA-287 - Deprecate Graphene.element and attribute methods

Thanks to the following list of contributors: Jan Papoušek, Lukas Fryc, Juraj Huska, Jiri Stefek, Andreas Vallen

Graphene 2.0.0.Alpha3 Released

Since we wrote this post we didn't laze around. Check our latest announcement.

The Arquillian team is proud to announce the 2.0.0.Alpha3 release of the Graphene component!

Highlighted Features

jQuery Selectors

Graphene gets its own @FindBy annotation and ByJQuery selector in order to fully support jQuery selectors. (read more)

Injecting Selenium Resources

The interesting Selenium APIs (HTML5, Mobile, etc.) can be injected to the test or page fragments directly. (read more)

Fluent API for Waiting

Waiting conditions can now be defined in a nice fluent API. (read more)

Injecting List of Page Fragments

It’s possible to inject a list of page fragments with @FindBy List<PageFragment>.

Page Fragment Enrichment

Any Arquillian resource available through @ArquillianResource can be injected into page fragments.

Important Bug Fixes

XHR Request Guards

guardXhr wasn’t waiting for the XHR request to fully complete. So we have improved it and now it not only waits for AJAX callback to completely finish, but it will wait for any asynchronous processing caused by that callback to finish.

Protecting against Stale Elements

You are now completely protected against StaleElementReferenceException when using page fragments and elements injected by @FindBy, because operations on those elements are evaluated lazily (at the time of usage) and they are re-evaluated when they get stale.

Refined Page Fragments

Page Fragments have received a lot of bug fixes and improvements.

jQuery Selectors

This feature injects a jQuery script to a tested page and allows Graphene to find elements using the jQuery Selectors syntax:

@FindBy(jquery = ":button:visible")
WebElement firstVisibleButton;

Note that Graphene injects jQuery in non-conflict way, so it does not affect your tested application.

Injecting Selenium Resources

Graphene leverages a long list of advanced Selenium features and their APIs to take full control of your browser:

You can inject those using @ArquillianResource annotation:

@ArquillianResource
BrowserConnection connection;

@Test
public void test() {
    connection.setOnline(false);
}

Fluent API for Waiting

The Alpha2 API allowed you to define a wait like this:

waitAjax().until(element(button).isVisible());

In order to fully support code-completion, we have decided to deprecate this API in favor of a fluent API:

waitAjax().until().element(button).is().visible();

Roadmap

This release is a significant milestone on the way to Beta1.

What is Arquillian?

Arquillian is open source software that empowers you to test JVM-based applications more effectively. Created to defend the software galaxy from bugs, Arquillian brings your test to the runtime so you can focus on testing your application's behavior rather than managing the runtime. Using Arquillian, you can develop a comprehensive suite of tests from the convenience of your IDE and run them in any IDE, build tool or continuous integration environment.

Release details

Component Graphene
Version 2.0.0.Alpha3 view tag
Release date 2013-01-08
Released by Lukas Fryc
Compiled against

Published artifacts org.jboss.arquillian.graphene

  • org.jboss.arquillian.graphene » graphene-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-api jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium pom
  • org.jboss.arquillian.graphene » graphene-webdriver-parent pom
  • org.jboss.arquillian.graphene » graphene-webdriver-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver pom
  • org.jboss.arquillian.graphene » graphene-webdriver-spi jar javadoc pom
  • org.jboss.arquillian.graphene » arquillian-graphene pom
  • org.jboss.arquillian.graphene » graphene-component-api jar javadoc pom

Release notes and resolved issues 39

Q1/13: Page Fragment enrichment; jQuery locators

Component Upgrade
Enhancement
  • ARQGRA-195 - Page Fragment Enricher fail with no context to where
  • ARQGRA-200 - Enhance Graphene's guards to wait until page fully updates
  • ARQGRA-211 - Enhance guards to wait for request
  • ARQGRA-228 - Waiting conditions improvements - string matchers, no-parameter until()
  • ARQGRA-229 - Support for JavaScript interfaces defined as abstract classes
  • ARQGRA-230 - Override a toString method in BooleanConditionWrapper
  • ARQGRA-234 - Sizzle locators: make injected jQuery noConflict
Feature Request
  • ARQGRA-84 - Support Sizzle locators with Selenium 2
  • ARQGRA-165 - Test enricher or resource provider for JSInterfaceFactory
  • ARQGRA-166 - Enrichment of page abstractions with Graphene configuration
  • ARQGRA-189 - Support @Page in Page objects
  • ARQGRA-190 - Support initializing Page Objects declared with generic types
  • ARQGRA-191 - Support @Drone and @ArquillianResource in Page objects
  • ARQGRA-192 - Support enriching also List<WebElement> fields annotated with @FindBy
  • ARQGRA-204 - Support innitialization of Page Fragments nested in other Page Fragments
  • ARQGRA-214 - Support for enriching by Page Objects and Page Fragments declared as nested classes
  • ARQGRA-215 - Enable injection of List<Page Fragment>
  • ARQGRA-216 - Support protection from StaleElementReferenceException
  • ARQGRA-227 - Support for injecting common Selenium resources: JavaScriptExecutor, Actions, Capabilities, etc.
  • ARQGRA-239 - CGLib usage interferes with test execution, leading to Security exception
  • ARQGRA-244 - Type parameters should be uppercase
Bug
  • ARQGRA-184 - Using driver in test listener causes NullPointerException
  • ARQGRA-198 - TestFactoryClass fails on NPE
  • ARQGRA-201 - Guards are not working with AndroidDriver
  • ARQGRA-202 - Page Fragments: injected web elements are not found relatively to root element
  • ARQGRA-232 - Enriched WebElement cannot be converted to JSON without manual unwrapping
  • ARQGRA-233 - Graphene does not guard against StaleElementException
  • ARQGRA-240 - Move Grapgene's FindBy from SPI to API
  • ARQGRA-241 - Double quotes in jquery fails
  • ARQGRA-242 - Dependency cleanup: remove Guava, commons-* and replace JBoss Logging with JUL
  • ARQGRA-245 - Returning a value from Function<T,V> in WebDriverwait cannot be compiled with JDK7
  • ARQGRA-246 - ByJQuery.jquerySelector with findElemens behaves differently than By
  • ARQGRA-249 - Negation in fluent API is not compilable
Task
  • ARQGRA-193 - Make Graphene 2 Configuration be compatible with Drone configuration
  • ARQGRA-207 - Page Fragments: support initialization of Page Fragments which are without @Root field
  • ARQGRA-219 - Refactor whole Enricher related code

Thanks to the following list of contributors: Jan Papoušek, Lukas Fryc, Juraj Huska, Karel Piwko

Introducing Arquillian Graphene Page Fragments

Tools like Arquillian Graphene, WebDriver, or just plain Selenium combined with concepts like Page Objects can help you automate your functional(integration, whatever…) web UI tests. We have already introduced some of the advances brought to you by Arquillian Graphene earlier. In this blog entry we will look a bit deeper into a new concept introduced in Arquillian Graphene 2.0.0.Alpha2; Page Fragments.

Consider the following example taken from the RichFaces component showcase. The page under test contain three Autocomplete widgets.

public class TestAutocompleteWidgets extends AbstractGrapheneTest {

    private JQueryLocator minCharInput = jq("input[type=text]:eq(0)");
    private JQueryLocator multipleSelectionInput = jq("input[type=text]:eq(1)");
    private JQueryLocator selectFirstFalseInput = jq("input[type=text]:eq(2)");
    private JQueryLocator selection = jq("div.rf-au-itm");

    @Test
    public void testFirstAutocomplete() {

        graphene.keyPress(minCharInput, 'a');

        assertFalse(graphene.isElementPresent(selection),
            "The suggestion list should not be visible, since there is only one char!");

        String keys = "ar";
        graphene.focus(minCharInput);
        selenium.type(minCharInput, keys);
        guardXhr(graphene).fireEvent(minCharInput, Event.KEYPRESS);

        assertTrue(graphene.isVisible(selection), 
            "The suggestion list should be visible, since there are two chars!");

        String actualArizona = graphene.getText(jq(selection.getRawLocator() + ":eq(0)"));
        assertEquals(actualArizona, "Arizona", "The provided suggestion should be Arizona!");

        String actualArkansas = graphene.getText(jq(selection.getRawLocator() + ":eq(1)"));
        assertEquals(actualArkansas, "Arkansas", "The provided suggestion should be Arkansas!");

    }

    @Test
    public void testSecondAutocomplete() {

        char key = 'a';
        selenium.focus(multipleSelectionInput);
        guardXhr(selenium).keyPress(multipleSelectionInput, key);

        assertTrue(selenium.isVisible(selection),
            "The suggestion list should be visible, since there is correct starting char!");

        selenium.keyPressNative(KeyEvent.VK_ENTER);

        key = ' ';
        selenium.keyPress(multipleSelectionInput, key);

        key = 'w';

        selenium.focus(multipleSelectionInput);
        guardXhr(selenium).keyPress(multipleSelectionInput, key);

        assertTrue(selenium.isVisible(selection),
            "The suggestion list should be visible, since there is correct starting char!");

        selenium.keyPressNative(KeyEvent.VK_ENTER);

        String actualContentOfInput = selenium.getValue(multipleSelectionInput);
        assertEquals(actualContentOfInput, "Alabama Washington", "The input should contain something else!");
    }

    @Test
    public void testThirdAutocomplete() {
        //similar autocomplete interactions as in the previous tests
    }
}

Now, ask yourself the following questions:

  • Do you find these types of tests less robust than for example unit tests for the persistence layer?
  • Do you think these tests still pass when the underlying HTML change?
  • Do you see repeating code in the these tests?

In my opinion you should have a clean answer to all of these questions. You are probably aware that tests should be loosely coupled with the underlying HTML structure of the application under test as it makes tests more robust and changes in the HTML structure of the page will not directly affect all tests.

Let’s apply the Page Objects pattern to split the HTML structure and the test logic. The Page Object will encapsulate the HTML structure so when the HTML change, only your Page Objects has to change.

  • But what about when I’m testing another application that use the same Autocomplete widget?
  • Should I copy the part of the Page Object that interact with the Autocomplete widget and paste it around in my code?

But as you’re already thinking: this would be a major don’t-repeat-yourself violation! Is there something we could do to improve this?

Yes, there is! Arquillian Graphene Page Fragments to the rescue!

Page Fragments, what are they?

  • Page Fragments are any repeating part of a page, any widget, web component, etc.
  • They encapsulate parts of the page into reusable test components across your whole test suite.
  • You can differentiate each fragment by its root element and reference other elements as children of that root.
  • They leverage Selenium WebDriver under the hood combined with all of the killer features of Graphene.
  • And they come with a set of utilities which simplify using them within tests and Page Objects.

How to define Page Fragments

public class AutocompleteFragment<T> {

    @Root
    WebElement root;

    @FindBy(css = "input[type='text']")
    WebElement inputToWrite;

    public static final String CLASS_NAME_SUGGESTION = "rf-au-itm";

    public List<Suggestion<T>> getAllSuggestions(SuggestionParser<T> parser) {
        List<Suggestion<T>> allSugg = new ArrayList<Suggestion<T>>();

        if (areSuggestionsAvailable()) {
            WebElement rightSuggList = getRightSuggestionList();
            List<WebElement> suggestions = rightSuggList.findElements(
                        By.className(CLASS_NAME_SUGGESTION));

            for (WebElement suggestion : suggestions) {
                allSugg.add(parser.parse(suggestion));
            }
        }

        return allSugg;
    }

    public List<Suggestion<T>> type(String value, SuggestionParser<T> parser) {
        List<Suggestion<T>> suggestions = new ArrayList<Suggestion<T>>();

        inputToWrite.sendKeys(value);
        try {
            waitForSuggestions();
        } catch (TimeoutException ex) {
            // no suggestions available
            return suggestions;
        }

        suggestions = getAllSuggestions(parser);
        return suggestions;
    }

    //other handy encapsulation of Autocomplete services
}

The example is just a snippet from the full implementation of the RichFaces Autocomplete component.

Notice the @Root annotation? The value of the root field is automatically injected by Graphene. The root field contain the root element as defined by the @FindBy annotation on the injection point of the Page Fragment in the Page Object or test. All @FindBy fields in the Page Fragment will use this root as a starting point.

The fragment implementation is pretty generic and therefore reusable in all tests in all applications that use the Autocomplete widget. A full implementation would encapsulate all the services this fragment provide, but it could for example also encapsulate browser specific interactions like submit or click actions. There are no boundaries!

Using Page Fragments and Page Objects together

Let’s rewrite the previous test to use our new Page Fragment together with our Page Object.

First we define a Page Object which contain the structure of the page under test.

public class TestPage {
    
    @FindBy(css = "div.rf-au:nth-of-type(1)")
    private AutocompleteFragment<String> autocomplete1;

    @FindBy(css = "div.rf-au:nth-of-type(2)")
    private AutocompleteFragment<String> autocomplete2;

    @FindBy(css = "div.rf-au:nth-of-type(3)")
    private AutocompleteFragment<String> autocomplete3;

    @FindBy(xpath = "//*[contains(@id,'sh1')]")
    private WebElement viewSourceLink;

    //all getters for the fields

    //other handy methods which you find useful when testing those three Autocomplete widgets
}

Declaring the Page Fragment in the Page Object is the preferred option, but you can also declare the Page Fragment directly in the test if desired. The only thing you need to do is to annotate the Page Fragment object with WebDriver’s @FindBy annotation to refer to the fragment’s root DOM element. That simple!

Graphene differentiates between Page Fragments and plain WebElements in the same Page Object so they can be declared side by side. All of the initialization is done automatically, so there is no need to initialize the Page Fragments or Page Objects in the @Before method of your test case.

In this last example we see how the autocomplete widgets’s Page Fragment is used in the test case.

public class TestWhichUsesPageFragments extend AbstractTest {

    @Page
    private TestPage testPage;

    @Test
    public void testFirstAutocomplete {
        AutocompleteFragment<String> autocomplete = testPage.getAutocomplete1();
        
        autocomplete.type("a");

        assertFalse(autocomplete.areSuggestionsAvailable(), 
            "The suggestion list should not be visible, since there is only one char!");

        String keys = "ar";
        autocomplete.type(keys);

        assertTrue(autocomplete.areSuggestionsAvailable(), 
            "The suggestion list should be visible, since there are two chars!");

        List<Suggestion<String>> expectedSuggestions = new ArrayList<Suggestion<String>>();
        expectedSuggestions.add(new Suggestion<String>("Arizona"));
        expectedSuggestions.add(new Suggestion<String>("Arkansas"));

        assertEquals(autocomplete.getAllSuggestions(new StringSuggestionParser()), expectedSuggestions, 
            "Suggestions are wrong!");
    }

    @Test
    public void testSecondAutocomplete() {
        AutocompleteFragment<String> autocomplete = testPage.getAutocomplete2();

        String key = "a";
        autocomplete.type(key);
        
        String errorMsg = "The suggestion list should be visible, since there was typed correct char ";
        assertTrue(autocomplete.areSuggestionsAvailable(), errorMsg + key);

        autocomplete.autocomplete(autocomplete.getFirstSuggestion());

        autocomplete.type(" ");

        key = "w"
        autocomplete.type(key);

        assertTrue(autocomplete.areSuggestionsAvailable(), errorMsg + key);

        autocomplete.autocomplete(autocomplete.getFirstSuggestion());        

        String actualContentOfInput = autocomplete.getInputValue();
        assertEquals(actualContentOfInput, "Alabama Washington", "The input should contain something else!");
    }

    @Test
    public void testThirdAutocomplete() {
        //similar autocomplete interactions as in the previous tests
    }
}

As your application grow, the only thing that needs to change is the root references of your Page Fragments. Last but not least you will be able to make your Page Fragment availabe to be used by other tests in other applications.

To try Page Fragments check out the Graphene 2.0.0.Alpha2 release. Getting start information can be found in the Reference Documentation.

Graphene 2.0.0.Alpha2 Released

Since we wrote this post we didn't laze around. Check our latest announcement.

The Arquillian team is proud to announce the 2.0.0.Alpha2 release of the Graphene component!

Graphene 2 is building on top of the Selenium WebDriver API and provide extensions for writing reusable, robust and AJAX-enabled tests with improved readability.

Graphene 2 uses the ideas from Graphene 1 (based on Selenium 1 API) and applies it to the world of Selenium WebDriver.

You can read more about the difference between Graphene 1 and Graphene 2 in the Reference Documentation

Features

Page Abstractions

Page abstractions allow you to abstract page functionality using the well-known Selenium design pattern Page Objects. In Graphene you can inject page objects directly into the test case using the @Page annotation.

But we take the Page Objects pattern one step further with the concept of Page Fragments . Page Fragments are reusable parts within a page encapsulated into an object similar to Page Objects. A good example of Page Fragments are JavaScript widgets or forms.

The concept of Page Fragments comes from an idea that those components share a same underlying DOM structure, the only difference between all occurences of a component is a location in a DOM tree where a component is rendered – this is denoted by the @Root annotation. You can define this root location for each concrete occurence using a @FindBy(locator) annotation.

Fluent-API for Waiting Conditions

Graphene provides a set of widely used conditions to avoid writing them repeatedly.

Request Guards

Request guards block the Selenium test execution until a network communication caused by a given action ends. Guards support blocking on HTTP and XHR (AJAX) but can also be used to verify that no such requests were done.

Under the Hood

There are some low level features exposed as API that are used by Graphene internally. While not used regularly within a normal test, can be handy to write more robust tests:

JavaScript Interface

This allows you to write a Java interface which automatically translates Java API invocations to JavaScript calls:

@JavaScript
public interface Document {
    String getTitle();
}
Page Extensions

Page Extensions allow you to inject arbitrary JavaScript code in the page under the test. The javascript code might help you to test complex situations as well as bring completely new features for improving testing. Graphene internally uses Page Extensions for Request Guards.

Graphene Context

Graphene allows you to obtain current thread-local context of a browser session and inject it exactly where you need. No more need for driver reference propagation.

Documentation

Last but definitely not the least addition is the great documentation for all of the features above including answers for general questions like how Selenium 1, Graphene 1, Selenium WebDriver and Graphene 2 relates to each other, what’s the relation between Graphene’s, Drone and Arquillian.

Getting Started

It’s the same as with any other Arquillian extension: open the IDE, setup the Maven dependency and you are ready to start testing!

What’s next?

In the upcoming releases, we would like to focus on more new features as well as migrate some features from Graphene 1 to Graphene 2:

  • Forge plugin for quick start
  • command interceptors
  • parallel browser session control
  • jQuery/Sizzle locators
  • AJAX/HTTP Communication Halter
  • page abstraction improvements
  • closer integration with the Arquillian event system
  • helpers for writing browser specific code

Call to Action

Let us know what you think and help us shape Graphene to be what you want it to be.

What is Arquillian?

Arquillian is open source software that empowers you to test JVM-based applications more effectively. Created to defend the software galaxy from bugs, Arquillian brings your test to the runtime so you can focus on testing your application's behavior rather than managing the runtime. Using Arquillian, you can develop a comprehensive suite of tests from the convenience of your IDE and run them in any IDE, build tool or continuous integration environment.

Release details

Component Graphene
Version 2.0.0.Alpha2 view tag
Release date 2012-09-12
Released by Lukas Fryc
Compiled against

Published artifacts org.jboss.arquillian.graphene

  • org.jboss.arquillian.graphene » graphene-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-parent pom
  • org.jboss.arquillian.graphene » graphene-selenium-api jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-selenium pom
  • org.jboss.arquillian.graphene » graphene-webdriver-parent pom
  • org.jboss.arquillian.graphene » graphene-webdriver-impl jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver-drone jar javadoc pom
  • org.jboss.arquillian.graphene » graphene-webdriver pom
  • org.jboss.arquillian.graphene » graphene-webdriver-spi jar javadoc pom
  • org.jboss.arquillian.graphene » arquillian-graphene pom
  • org.jboss.arquillian.graphene » graphene-component-api jar javadoc pom

Release notes and resolved issues 30

Adopting critical functionality from Graphene 1; Page Fragments

Component Upgrade
  • ARQGRA-150 - Upgrade to Arquillian Core 1.0.2.Final
Feature Request
  • ARQGRA-40 - Support for Component Objects pattern for Selenium 2
  • ARQGRA-41 - Support for Java-to-JavaScript calling interfaces
  • ARQGRA-62 - Umbrella: Adopt Graphene for usage of WebDriver/Selenium 2 API
  • ARQGRA-67 - Migrate request guards to Selenium 2
  • ARQGRA-71 - Adopt JavaScript injection (Page Extensions) for Selenium 2
  • ARQGRA-74 - Support Graphene utility in original scope with Selenium 2
  • ARQGRA-156 - Introduce Opera and RemoteWebDriver factory wrappers
  • ARQGRA-157 - Support XHR interception
  • ARQGRA-160 - Create a configurator for Graphene 2 using arquillian.xml
  • ARQGRA-161 - Introduce profile to enable execution from several browsers
  • ARQGRA-179 - Document difference between Graphene 1 and Graphene 2 (Selenium 1 and WebDriver)
  • ARQGRA-180 - Document Graphene - Getting Started
  • ARQGRA-181 - Document Using WebDriver
  • ARQGRA-182 - Document Using Drone
  • ARQGRA-183 - Document Testing AJAX
Bug
  • ARQGRA-6 - Using of JQuery 1.5 causes that JQuery selectors are not escaped properly
  • ARQGRA-151 - Graphene.getProxyForInterfaces returns wrong proxy
  • ARQGRA-159 - Use Drone depchain in Graphene depchain
Task
  • ARQGRA-132 - Add BOM usage into documentation
  • ARQGRA-158 - Check and complete all documentation for all features in Alpha2
  • ARQGRA-163 - Refactor Graphene Webdriver SPI module
  • ARQGRA-171 - Refactor Component Objects - mainly make Component Obejcts as POJO
  • ARQGRA-172 - Documentation for Page Extensions
  • ARQGRA-173 - Documentation for Page Fragments
  • ARQGRA-175 - Documentation for JavaScript interfaces
  • ARQGRA-176 - Documentation for GrapheneContext and proxies
  • ARQGRA-177 - Documentation for Request Guards
  • ARQGRA-178 - Documentation for Graphene utility class
  • ARQGRA-186 - Rename Component Objects dependencies to Page Fragments

Thanks to the following list of contributors: Jan Papoušek, Lukas Fryc, Juraj Huska, Brian Leathem