RichFaces-Selenium Library

Version 5
    This wiki talks about internal of richfaces-selenium library. You can easily see how it makes development of AJAX test cases using Selenium more developer friendly, more type safe and more stable while running same tests with different test environment, primarily browsers.

    Encapsulated Types

     

    Locators

    When writing selenium test cases, you are working with locators all the time. All the locators are encapsulated as instances of Locator interface, divided into to two main groups - ElementLocators and AttributeLocators.

     

     

     

    ElementLocator

     

    Separated by location strategies to following types:

     

    • JQuery implements Iterable, Compoundable
    • XPath  implements Iterable, Compoundable
    • CSS implements Compoundable
    • ID
    • DOM
    • Link
    • Name
    • Identifier (ID or name)

     

    Iterable locators means that you can refer to list of its children (Iterable<ElementLocator>).

     

    JQueryLocator myTable = new JQueryLocator("#mytable");
    
    for (JQueryLocator row :  myTable.getChildren("tr")) {
    ...
    }
    

     

    Compoundable locator signs that you are able to use original expression and some partial expression to derive new composite expression. That way you can desribe the structure of your tested DOM tree in manner of reusability.

     

    ...
    JQueryLocator row.getChild(new JQueryLocator("span.link a"))
    ...
    

     

    It's obvious that by composing and iterating over locator's descendants you can only use and derive only the same type of locator!

     

    (Note: it is not case of WebDriver / Selenium 2.0 location strategy - this framework uses the direct references to elements, so you can use each element again to derive new element by referring to it, using whatever location strategy you want - but this is quite problematic in testing AJAX-based frameworks, since the reference can quickly became stale after interaction with page).

     

    You can also see that use of type-safe locators is quite verbose - you can then use the capabilities of LocatorFactory - it is a utility class able to create type-safe locators in less verbose way:

     

    import static org.jboss.test.selenium.locator.LocatorFactory.*;
    
    JQueryLocator  myTable = jq("#mytable");
    ElementLocator theSameTable = id("mytable");
    

     

    You can see the sample usage in Iteration and Composition Sample.

     

    AttributeLocator

     

    Creating  attribute locators  is also very straight forward. When you want to refer to attribute's value, you need some element locator first as the base for deriving locator:

     

    import static org.jboss.test.selenium.locator.LocatorFactory.jq;
    import static org.jboss.test.selenium.locator.Attribute.CLASS;
    
    jq("#foo-link").getAttribute(CLASS);
    

     

    Attribute is the class, which encapsulates the common logic around attributes and also gives option to create new attributes side-by-side to the list of predefined (at this moment there are STYLE, CLASS, SRC and HREF predefined which should cover bulk of use cases).

     

    You can find sample usage in Attribute Sample.

     

    Here is the structure of locators:

     

    • Locator* 
      • Element*
        • Compoundable* 
          • Iterable*
            • JQuery* ƚ
            • Xpath*
          • CSS*
        • ID*
        • DOM*
        • Link*
        • Identifier*

     

    * all the names have suffix Locator, e.g. Element* = ElementLocator

     

    You can read more about location best practices.

     

    Events

     

    These are events in the DOM.

     

    Not all user actions have obvious trigger - you need sometimes to study the DOM to find out, what DOM Event needs to be triggered to fire the action (it mostly hides in event attributes, such as onmouseover, onclick, etc.).

     

    The special case is when you are typing the letters into input using selenium.type(...) method - this method doesn't implicitly trigger any action - so you need to choose and explicitly trigger the one that suits.

     

    You can fire the event using selenium.fireEvent(...) method on the specific element.

     

    You can select one from events listed in Event class, or you can specify your own by extending this class (it should not be necessary since the list is close to complete).

     

    Browser

     

    The browser comes in to play by defining in the test project definition and bubbles to selenium setting. But it also can be used to influence the test logic, for example, there is built-in TestNG parameter for explicit enabling/disabling the browser for the given test.

     

    Each Browser consists of it's BrowserMode (as execution mode of the given browser) and optional executable File.

    Each BrowserMode also has to bind to one BrowserType, which actually represents the brand of given browser.

    To keep the things simple, you can still define the browser by it's string representation defined in Selenium API.

     

    Geometry

     

    There is also encapsulation for types around defining the screen positions and dimensions.

     

    It is represented by:

     

    • Point (as the specific point on the screen)
    • Dimension (as screen dimensions of object)
    • Offset (as offset of movement of the object)

     

    This types are used typically when obtaining the dimensions/position of object, or when you are triggering the DOM Event on specific position.

     

    Drag

     

    The drag action is the special implementation of dragging the element from one position to another where you can specify and test the particular phases of dragging.

     

    Drag takes one element locator to drag and one as the target and by calling it's methods, you can go through all the phases and test behaviour of dragged object.

     

    JavaScript

     

    In testing complex examples, it is often needed to perform custom JavaScript code. For this purposes, there is implemented JavaScript class, which encapsulates the common functionality:

     

    • constructing the JavaScript using new JavaScript(String) constructor or JavaScript.js(String) factory method with same semantics
    • loading the JavaScript code...
      • from classpath resource - using factory method JavaScript.fromResource(String resourceName)
      • from file - using factory method JavaScript.fromFile(File)

     

    • joining two JavaScripts to one using JavaScript#join(JavaScript) method
    • appending the code to the JavaScript object using JavaScript#append(String)
    • parametrize the JavaScript code placeholders (using SimplifiedFormat format) with JavaScript#parametrize(Object...)

     

    Waiting

     

    Almost every interaction with tested application, there is a need for waiting to completing of that action i.e. to wait for reaction of your action.

    Each of this reaction should provide some visible or hidden change to the DOM tree. We can then find conditions, which describes this change.

    We can say, that we wait for satysfing the conditions after our interaction.

     

    For example, we have application implementing the incremental counter, which must be started first, described by this locators:

     

    JQueryLocator BUTTON_START = jq("#start");
    JQueryLocator BUTTON_INCREMENT = jq(":button");
    IdLocator TEXT_COUNT = id("#count");
    

     

    First, we need to start the counter, let's click on the button:

     

    selenium.click(BUTTON_START);

     

    To satisfy, that we will not continue with another interaction before the latter was completed, we have to wait for satysfing the condition "counter is prepared for incrementation":

     

    Wait.waitSelenium().timeout(60000).interval(1500).until(new ElementPresent().locator(TEXT_COUNT));

     

    The declaration above says that we test in interval 1,5 sec. the condition testing that element with locator TEXT_COUNT is present. Note that first test for condition will start in delay of one interval (1,5 sec). After 60 sec. of evaluating conditions as unsatisfied, the code above will raise the exception.

     

    This unreadable section can be easily modified to be pretty more readable, using capabilities of our AbstractTestCase (the more about it later), and we will continue to present this method of improving readability:

     

    waitModel.until(elementPresent.locator(TEXT_COUNT));

     

    So we have satisfied that counter was started, and it should be definitely preset to value zero:

     

    assertEquals("0", selenium.getText(TEXT_COUNT));

     

    Let's try the functionality of button Increment:

     

    selenium.click(BUTTON_INCREMENT);

     

    After this step we will need define another condition, which will be satisfied after increasing the value of counter to "1" - for this purpose, we will create own condition object, reusing the predefined condition of type TextEquals:

     

    TextEquals countEquals = textEquals.locator(TEXT_COUNT);

     

    Let's use this condition in action:

     

    waitAjax.until(countEquals.text("1"));

     

    (Note: waitGui is similar to waitModel, and was created by factory method Wait.waitAjax() or by call new AjaxWaiting()).

     

    It's obvious, that with waiting for satisfying each condition, we also assert that the condition has to become true to successfully continue in current test. There is no need for additional assertion on value.

     

    Sometimes however we need to wait for value that is unknown in time of interaction.

     

    Retrievers

     

    Waiting for inexact interaction result can be easily satisfied by waiting for change of the conditions by Retrievers.

     

    We can modify the application introduced above - we do not have application generating sequence of numbers, it generates the random numbers greater than previous one. For this purpose we have one button, and one input box:

     

    ElementLocator GENERATE_RANDOM = ...;
    ElementLocator RANDOM_VALUE = ...;
    
    String initialRandomValue = selenium.getText(RANDOM_VALUE);
    
    selenium.click(GENERATE_RANDOM);
    
    waitAjax.waitForChange(initialRandomValue, retrieveText.locator(RANDOM_VALUE));
    
    String randomValue = selenium.getText(RANDOM_VALUE);
    
    assertTrue(Long.valueOf(initialRandomValue) > Long.valueOf(randomValue));
    

     

    In this example, the retrieveText is the Retriever (with type TextRetriever) and waitForChange is a method which waits for change of the first argument's value to value obtained by retriever specified as second argument.

     

    We can also tune this sample in several ways:

     

    1) use the second version of waitForChange, called waitForChangeAndReturn, which additionally returns the new value:

     

    String randomValue =  waitAjax.waitForChangeAndReturn(initialRandomValue, retrieveText.locator(RANDOM_VALUE));

     

    2) We can introduce the retriever e.g. on class properties level, and then we can call it in the test on every place we need the value.

     

    TextRetriever retrieveRandom = retrieveText.locator(RANDOM_VALUE);
    ...
    String initialRandomValue = retrieveRandom.retrieve();
    ...
    String randomValue = waitAjax.waitForChangeAndReturn(initialRandomValue, retrieveRandomValue);
    

     

    3) we can create own Retriever, which will not return the String, but Long instead (such retriever is not currently implemented in Library). It will little simplify our tests.

     

    Creating custom Retrievers

     

    For creating the Retriever, we must create class implementing at least one of the interfaces SeleniumRetriever<T> or JavaScriptRetriever<T>.

     

    As names suggest, we will need to call the selenium here (in SeleniumRetrieve<T>#retrieve() body) or specify JavaScript object (in JavaScriptRetriever<T>#getJavaScriptRetrieve()), which will after evaluation result into given value.

     

    The generic type <T> is declare here so that we can also return the another values than String.

     

    However in case of JavaScriptRetriever, we need to additionaly specify the Convertor implementation, which will convert the value before returning from waitForChangeAndReturn method.

     

    We can also implement both the interfaces for selenium and ajax retrieving - it will then depend on implementation of Waiting object used in condition, what retriever will be used (this principle uses all Retrievers implemented in Library):

     

    TextRetriever  retrieveCount = retrieveText.locator(TEXT_COUNT);
    waitAjax().waitForChange("1", retrieveCount);
    waitSelenium().waitForChange("2", retrieveCount);
    

     

    Command Interception

     

    Advanced theme is the command interception. The basics of this technique comes from Aspect Oriented Programming model.

     

    The interceptor is basically the code, which will be performed around invocation of selenium command.

     

    This way, you can:

     

    • execute code before/after the selenium command will be performed
    • catch exceptions thrown when performing selenium command

     

    For this purpose, you have to implement interface CommandInterceptor - it requires to implement method

     

    void intercept(CommandContext ctx) throws CommandInterceptionException;

     

    where you implement the interception code.

     

    If you will implement this method, you must additionally satisfy one contract - the CommandContext#invoke() method must be invoked at least once - otherwise, after end of intercept(...) method, the  CommandInterceptionException will be thrown and your test will fail!

     

    (Warning: the satisfying the contract mentioned above is checked at runtime, not at compile-time)

     

    You get to disposition the CommandContext object. On the context, you have to call the CommandContext#invoke() method. You can also obtain the current command name and it's arguments here.

     

    The implementation of invocation nesting can be seen on CommandContext (seek for invoke() method).

     

     

    InterceptedProxy

     

    Registration and unregistration of interceptors to selenium commands is provided by InterceptedProxy, which is the member of AjaxSelenium.

     

    On the InterceptedProxy can be registered zero and more interceptors, which will be in sequence called to achieve desired functionality, whereas each interceptor's body is nested into the other, using CommandContext#invoke() method. The call of invoke for last of the interceptors in sequence will cause actual performing of the command.

     

    The entry point for command execution, AjaxSelenium, contains one  InterceptedProxy, which triggers all the command through sequence of interceptors registered to it.

     

    Two well-known implementation of CommandInterceptor are RequestTypeGuard and AjaxAwareInterceptor:

     

    RequestTypeGuard

     

    Its purpose is to guard that the interaction of user will raise specific request to server, differ between following types:

     

    • No Request
    • XHR Request (AJAX)
    • HTTP Request (regular HTTP)

     

    You can register create new RequestTypeGuard with given type defined in constructor:

     

    RequestTypeGuard xhrGuard = new RequestTypeGuard(RequestType.XHR);
    selenium.getInterceptionProxy().registerInterceptor(xhrGuard);
    selenium.click(BUTTON_START);
    

     

    More intuitive use and preferred way of usage is using RequestTypeGuardFactory static members:

     

    import static org.jboss.test.selenium.guard.request.RequestTypeGuardFactory.*;
    ...
    guardXhr(selenium).click(BUTTON_CAUSE_XHR);
    ...
    guardHttp(selenium).click(BUTTON_CAUSE_HTTP);
    ...
    selenium.click(ANY_BUTTON);
    

     

    Internally, factory creates immutable copy of InterceptionProxy and also AjaxSelenium and returns this modified copies, thereby the last call clicking to ANY_BUTTON will not be intercepted by any RequestTypeGuard.

    It brings simplicity to quite hard-to-understand topic.

     

    Wait for RequestType

     

    Together with guarding the request type done, there is also the possibility to waiting until the page will catch the given request type, ignoring all other requests.

     

    For this purpose, you can again use RequestTypeGuardFactory, similary to previous sample:

     

     

    import static org.jboss.test.selenium.guard.request.RequestTypeGuardFactory.*;
    ...
    waitXhr(selenium).click(BUTTON_CAUSE_XHR);
    ...
    waitHttp(selenium).click(BUTTON_CAUSE_HTTP);
    

     

     

    This is useful in cases, where you expect that also other request can be done. For example you are relocating to new URL, but on the current page is still active using AJAX (triggering XMLHttpRequest).

    AjaxAwareInterceptor

     

    Library also contrains nterceptor to simplify test development or migrate to Internet Explorer. Internet Explorer is known for the problems with handling the child window by JavaScript, since the page is still loading. In some case, Internet Explorer can raise the Permission Denied exception.

     

    This Interceptor will catch such execptions, identify and try to call the same command again with delay. In this way, you are able to perform tests more reliably.

     

    Selenium Extensions

     

    Most of the extensions into Selenium API are done via extending JavaScript from the core of Selenium. The process of extending is done by loading of ordered set of scripts and injecting into DOM of Test Runner window.

     

    Besides loading the script, SeleniumExtensions are also able to refresh (reload) the script with new definition, which is a great feature in development process. Only step needed to load or reload script is specifying that the given script resource is required, using SeleniumExtensions#requireScript(String resourceName).

     

    Page Extensions

     

    For managing the script behaviour of tested application, the set of page extensions was also developed.

     

    This extensions can be installed via PageExtensions class (avaiable from AjaxSelenium instance). Currently there can be done with no modifications to extension scripts and it satisfies following functionality:

     

    • catches XMLHttpRequests (XHR)
    • decides if some request was done in time and what type of request it was (HTTP, XHR)

     

    RequestType

     

    It brings the enumaration of supported page request types:

     

    • NONE - no request was done
    • XHR - XMLHttpRequest (AJAX) was done
    • HTTP - regular HTTP request (relocation) was done

     

    RequestInterceptor

     

    Provides the functionality intercepting the page request (HTTP or XHR).

     

    It depends on installation of Page Extensions and provides following methods:

     

    • getRequestTypeDone() - obtains the last request intercepted by code in Page Extensions
    • clearRequestTypeDone() - clears the last request (sets the NONE) and returns the last request type set
    • waitForRequestTypeChange() - waits until the request done will change to value other than NONE

    Additional TestNG listeners

     

    ConsoleStatusListener

     

    TestNG writes no information about running test. For fixing this behaviour, the ConsoleStatusListener was developed. It can write the current method start and stop times incl. test case name, method name and parameters of test to console.

     

    You can register it in maven-surefire-plugin, using configuration/properties/property section:  http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html

     

     

    <properties>
         <property>
              <name>listener</name>
              <value>org.jboss.test.selenium.listener.ConsoleStatusTestListener</value>
         </property>
    </properties>
    

     

    SelenumLoggingTestListener

     

    Desired to be simplification for browsing Selenium Server logs. Prints timestamps incl. test case name and method name to selenium log.

     

    TestMethodSelector

     

    To simplify development, Maven can offer property ${test} (defined by maven-surefire-plugin) to select only whose test you want to execute.

     

    But this is not enough in some cases - you can use the functionality which offers you TestMethodSelector - it is implementation of IAnnotationTransformer, which registered as test listener is triggered before the test suite starts.

     

     

    TestMethodSelector for each @Test annotation checks that the annotated method has name corresponding to system property method (when using functional-test-template, you can just use Maven system property ${method}, e.g. -Dmethod=testListOrdering or -Dmethod=testList*)

     

    Color Utils

     

    For simplifying manipulation with browser representation of color and java.awt.Color class, you can use ColorUtils class.

     

    URL Utils

     

    Since all the manipulation with URLs should be done via java.net.URL class, you can use utilities in URLUtils to build new URLs from the specific context. For example you can build new URL from test's contextPath and the relative URL obtained from page.

     

    AjaxSelenium

     

    The entry point for functionality is interface AjaxSelenium, which we already mentioned above.

     

    It has several purposes:

     

    • extends TypedSelenium interface
    • extends the functionality by interesting functions in ExtendedTypedSelenium interface
    • provides extensionability (PageExtensions, SeleniumExtensions)
    • provides interception of commands (InterceptionProxy)
    • provides interception of page requests (RequestInterceptor)
    • make itself immutable extending Cloneable interface

     

    TypedSelenium

     

    The TypedSelenium is core interface providing type-safe functionality from Selenium API (Selenium interface).

     

    Note: Not all methods from Selenium is currently supported, you can see it's list in UnsupportedTypedSelenium interface.

     

    ExtendedTypedSelenium

     

    There is several methods designed to extend or simplify functionality and usage of TypedSelenium, namely:

     

    • getStyle
    • scrollIntoView
    • mouseOverAt
    • isDisplayed
    • belongsClass
    • isAttributePresent

     

    AjaxSeleniumProxy

     

    The AjaxSelenium context is stored inside AjaxSeleniumProxy and tied as ThreadLocal. It offers the possibility of test parallelization.

     

    AjaxSeleniumProxy also gives you simple method for obtaining current thread-local context, using AjaxSeleniumProxy.getInstance() method.

     

    Obtaining the thread-local context of AjaxSelenium

     

    AjaxSelenium selenium = AjaxSeleniumProxy.getInstance()

     

    This call will not actually give you real instance, but only a proxy. This proxy will internally wrap the interface calls and provide you invocation of methods on real thread-local context.

    AbstractTestCase

     

    There is lot of abstraction across library - all leading to AbstractTestCase class, which offers lot of predefined and partially configurable behaviour. You can also abstract this class to achieve desired behaviour.

     

    The AbstractTestCase is use in cooperation with functional-test-template and offers you the following features:

     

    • AjaxSelenium selenium
      • the API exposition
    • predefined Waiting objects
      • waitGui - waiting for GUI interaction, such as rendering
      • waitAjax - waiting for AJAX interaction with server (not computationally difficult operations)
      • waitModel - waiting for computationally difficult operation requests
    • predefined Conditions
      • elementPresent
      • textEquals
      • attributePresent
      • attributeEquals
    • predefined Retrievers
      • textRetriever
      • attributeRetriever
    • URLs for contextRoot and contextPath
      • contextRoot = root of the server context, e.g. http://localhost/
      • contextPath = root of the servlet path incl. contextRoot, e.g.  http://localhost//my-application/
        • warning: the contextPath are defined without the contextRoot in project definition level; the final URL is compound later
    • Maven environment
      • mavenProjectBuildDirectory = target directory
      • mavenResourcesDir = ???
    • Selenium Debug flag
      • the boolean flag for indicating the debug mode of tests (currently isn't used implicitly by library)
    • selected Browser