Extending Geb Navigators to work with Third Party Javascript Libraries

In Asgard, we use the select2 jQuery library to make combo boxes on our pages more user friendly.

This causes a problem when writing functional tests, however, since you cannot use the default drop down selection mechanisms provided by Geb.

A pretty powerful technique mentioned in Marcin Edrmann’s Advanced Geb talk is to extend the default navigators to provide your own methods to acommodate third party libraries.

In this post, I will show you how we use this to provide our own dropdown selection method.

Creating your own navigator classes.

In Geb, there are two navigator classes. If your selector $(‘.myclass’) returns an element, it will be bound to a NonEmptyNavigator. If your selector returns empty, it will use an EmptyNavigator, which will return reasonable defaults ( 0 for .size(), for example ).

You need to create two new classes, a NonEmptyNavigator class and an EmptyNavigator class, the NonEmptyNavigator looks as follows:

package com.netflix.asgard.navigator

import geb.Browser
import org.openqa.selenium.WebElement

class NonEmptyNavigator extends geb.navigator.NonEmptyNavigator {
    NonEmptyNavigator(Browser browser, Collection<? extends WebElement> contextElements) {
        super(browser, contextElements)
    }
}

And the EmptyNavigator looks like this:

package com.netflix.asgard.navigator
import geb.Browser

class EmptyNavigator extends geb.navigator.EmptyNavigator {
    EmptyNavigator(Browser browser) {
        super(browser)
    }
}

Hooking up your own navigators

After you have created your navigators, modify GebConfig.groovy to use your custom navigators:

import com.netflix.asgard.navigator.EmptyNavigator
import com.netflix.asgard.navigator.NonEmptyNavigator

innerNavigatorFactory = { Browser browser, List<WebElement> elements ->
    elements ? new NonEmptyNavigator(browser, elements) : new EmptyNavigator(browser)
}

Adding your own methods

With select2, we can programmatically change the dropdown selection via the following javascript:

jQuery('#myElement').select2("val", "selectedValue");

We can convert this to a Geb friendly expression using the built-in javascript executor. The method I add to my NonEmptyNavigator looks like this:

void select(String value){
    browser.js.exec(firstElement(), value, 'jQuery(arguments[0]).select2("val", arguments[1]);')
}

We are using the navigator’s firstElement() helper so our method can be applied to the first result of our selector. These values are passed into javascript via the arguments array.

In our Geb tests, we can now call the new method we have added to choose our drop down menus that use select2:

    $('#myDropdown').select('cookies')
    $('form').deploymentType().select('multiCluster')

As you can see, this mechanism allows you to quickly and elegantly extend Geb Elements to work with your custom libraries.

Other uses

You can use this technique to add your own custom methods to check element state in AngularJS:

    boolean hasChanged() {
        !hasClass 'ng-pristine'
    }

Add your own utility methods:

    String rawHtml() {
        browser.js.exec(firstElement(), "return arguments[0].innerHTML;")
    }

or even do more elaborate things like waiting for CSS transitions to finish up:

    void waitForCssTransition(Closure trigger) {
        def element = firstElement()
        browser.js.exec(element, '''
            var o = jQuery(arguments[0]);
            window.setTransitionFinishedClass = function() {
                $(this).addClass('transitionFinished');
            }
            o.bind('webkitTransitionEnd', window.setTransitionFinishedClass);
        ''')
        try {
            trigger.call()
            browser.waitFor {
                hasClass('transitionFinished')
            }
        } finally {
            browser.js.exec(element, '''
                var o = jQuery(arguments[0]);
                o.removeClass('transitionFinished')
                o.unbind('webkitTransitionEnd', window.setTransitionFinishedClass);
                window.setTransitionFinishedClass = undefined;
            ''')
        }
    }

4 thoughts on “Extending Geb Navigators to work with Third Party Javascript Libraries

  1. Pingback: Extending Geb Navigators to work with Third Party Javascript Libraries | Adobe

  2. Denis

    Hello
    I have content object like this
    findbutton(wait: true) { $(“a#fCoverage”) }
    and i need add js listener for this object “.addEventListener(‘click’, function(){alert(‘GTM’)});”
    Can i do this?

    Reply
  3. Denis

    now i have error
    groovy.lang.MissingMethodException: No signature of method: geb.navigator.NonEmptyNavigator.plus() is applicable for argument types: (java.lang.String) values: [.addEventListener(‘click’, function(){alert(‘GTM’)});]
    Possible solutions: plus(geb.navigator.Navigator), has(java.lang.String), is(java.lang.String), last(), value(), add(java.lang.String)

    Reply
  4. Denis

    i have error after this code browser.js.exec(object + “.addEventListener(‘click’, function(){alert(‘GTM’)});”)

    Reply

Leave a comment