Geb 101: Simple Scripts

5 minute read Published:

These days I’m in between projects, so I decided to automate a task related to a web interface and a tedious list shaped as an HTML form. This automation would imply two processes: parsing an HTML page and filling some fields in another page, plus some small and simple processing.

I had two options, to use HTTP directly through HTTP Builder or something like that or to code various small scripts with Geb. Of course, I chose the latter.

A little bit of context

From its webpage:

Geb is a browser automation solution.

It brings together the power of WebDriver, the elegance of jQuery content selection, the robustness of Page Object modelling and the expressiveness of the Groovy language.

With a simple script, I can have a Firefox or Google Chrome webdriver (among others) up and running quickly, use jQuery-like selectors to traverse and modify the webpage DOM and process its information with all the power of Groovy.

And best of all, the dependencies are managed by Grape, so the only thing I need in any machine to run this scripts is Groovy itself.

Requirements

To run this examples, the only requirements needed are the Firefox Web Browser and the Groovy Runtime, version 2.X or greater.

Dependencies and Hello World

First of all, we need to add the dependencies to our script.

@Grab(group='org.gebish', module='geb-core', version='0.10.0')
@Grab(group='commons-logging', module='commons-logging', version='1.2')
@Grab(group='org.seleniumhq.selenium', module='selenium-firefox-driver', version='2.43.0')

Usually, the commons-logging dependency is transitive to the other two and should be resolved automaticaly, but Groovy is unable to find the transitive version, so we put the one we found on Maven Central explicitly and problem solved.

When used inside a script, Geb works with a simple DSL notation. Our hello world script is going to drive the browser to the google webpage and fill the form with “Hello Geb!”.

@Grab(group='org.gebish', module='geb-core', version='0.10.0')
@Grab(group='commons-logging', module='commons-logging', version='1.2')
@Grab(group='org.seleniumhq.selenium', module='selenium-firefox-driver', version='2.43.0')
import geb.Browser


Browser.drive {
    go "http://google.com"

    $("#gbqfq").value("Hello Geb!")
}

At this point, you can go to your terminal and run:

groovy HelloWorld.groovy

Selecting content

Geb uses jQuery-like selectors to access the content of the page through the $ function. This selectors always return a geb.navigator.Navigator object that represents one or many elements on the page. This function can be used through the find alias.

The signature of the $ function is

$([CSS SELECTOR], [INDEX OR RANGE], [ATTRIBUTE / TEXT MATCHERS])

being all the arguments optional, so the following calls are valid

$("#content h1")
$("#content h1", 0)
$(0)
$("div", title: "my-title")
$(name: "password-input")

We can select again over a geb.navigator.Navigator object, calling $ or find again

// <div class="a">
//   <p class="b par" data="validateable">Something</p>
// </div>
// <div class="a">
//   <p class="b par" data="validateable">Something more</p>
// </div>

$(".a").find(".b")
assert $(".a").find(".b") == $(".a").$(".b")

This selectors are iterable too

// <p>Something</p>
// <p>Something more</p>

$("p").collect {
    it.text()
}
//=> ['Something', 'Something more']

they expose the following special methods: tag(), text() and classes()

// <p class="b par">Something</p>
// <p class="b">Something more</p>

def el = $(".b", 0)
el.tag()
//=> 'p'
el.text()
//=> 'Something'
el.classes()
//=> ['b', 'par']

and all their attributes are exposed throught @attribute

// <p class="b par" data="validateable">Something</p>

def el = $(".b", 0)
el.@data
//=> 'validateable'

Handling forms

Geb provides us with tools to manage forms and input fields. Input fields have the method value() that we can use to both get the current field value or set our own. Given this code

// <form action="/" method="POST">
//   <input type="text" name="username" />
//   <input type="password" name="password" />
// </form>

$("form", name: "password").value()
//=> my username
$("form", name: "password").value("another username")

We can use value on elements of type input, select or textarea. If we try to set a value on a different type of element, we will get an UnableToSetElementException exception.

To ease working with forms, we can use shortcuts on the Navigator objects to access the form fields by their name

// <form action="/" method="POST">
//   <input type="text" name="username" />
//   <input type="password" name="password" />
// </form>

assert $("form").username == "my username"
$("form").username = "another username"
$("form").password = "mysupersecret"

Special inputs like textarea, select, checkbox, etc. are accessed each one in its particular way. You can find each case explained in detail in the setting values section of the documentation.

Moving around

Among all the possible interactions that we have in Geb, one of the most important ones is the click. Navigator objects have a click() method, that simply clicks on the first element of the navigator

$("a").click()

Geb has tools for waiting to some conditions to happen or some content to appear on the page. This is useful when following links, when interacting with the page or when working with timers.

The waitFor() method allows us to wait a certain amount of time

waitFor(10)
waitFor(10, 0.5)  // wait up to 10 seconds, with half a second between retries

and to wait for some closure to return a true object

waitFor { title.contains("Search Results") }

To finally close the browser, we use the quit() method of the DSL

Geb and Javascript

If we use Geb with the DSL, we have a js object to both interact with the data and execute code in Javascript.

Accessing variables

Any Javascript global varialbe is accesible by name

// <html>
//   <script type="text/javascript">
//     var aVariable = 1;
//   </script>
// <body>
// </body>
// </html>

assert js.aVariable == 1

we can even access nested variables

assert js."document.title" == "My Webpage"

Executing code

You can execute arbitrary javascript code through the js object

assert js.exec('return 1 + 1;') == 2

if you want to send arguments to the javascript snippet, they are the first in the exec() signature

assert js.exec(1, 2, 'arguments[0] + arguments[1];') == 3

Beyond the script

Geb is awesome to write simple and powerful scripts, but its power doesn’t stop there. If we need bigger and more complicated logic, Geb provides us with the Driver and Page classes and a lot of abstractions way more structured than a DSL to use.

Reach the manual or the API documentation if you want to find more and have fun with Groovy and Geb.