Ext JS is a Javascript framework with many configurable UI components right out of the box. Geb is a browser automation library which makes it easy for complex web applications to be broken down into pages and reusable modules. These two complement each other quite nicely as Geb modules can be created for each Ext JS component, making testing Ext JS applications very easy. This same technique could be used for other Javascript frameworks as well.

In order to demonstrate how Geb can be used to model/test Ext JS, we have chosen the Ext JS Kitchen Sink application. This demo application showcases many of the widgets Ext JS provides. If we navigate to the Kitchen Sink application, we will see a high level view of various Ext JS components. By clicking on one of these types, you are able to further drill down into these components and the different features they offer. For example, after clicking on Panels you will see additional drill downs for Basic Panel, Framed Panel, and Header Positioning. These then instantiate actual Ext JS components that you can see and interact with. For this article, we will focus just on testing basic panels.

So, how might you use Geb to test some of these panel features? Well, one of the high level abstractions that Geb provides is the Page which allows you to model a web application page to help organize the components that you will be testing. As we will see, you could potentially have one HTML page which shows/hides various components using Javascript and CSS and could therefore create multiple Pages to model logical 'places' in the application even though there is just one HTML page behind it all. In this case, the Kitchen Sink application stays on a single HTML page but hides/shows various components based on which ones you wish to view.

So, we will start by creating a Page for the landing area of the kitchen sink application. We will call this KitchenSinkPage and let's walk through some of its major pieces:

All Geb pages need to extend from Page.

By providing a URL for the page, we can then use commands such as:

in our Spock tests to easily go right to the page. But how do we know if we are at the right page? We can use:

to check the title of the page to ensure we are in the right place.

Ok, so now how do we indicate which components are on this page? Well, looking at the landing page of the kitchen sink application, we see a bunch of components which we will just call Thumbnails. They have pictures and words and when you click on them, you are taken to a different 'page' related to the thumbnail's description. So since we have a bunch of these, we want an easy way to encapsulate them for our tests. If we skip to a possible answer, it would be nice to have something like:

which we could use to indicate that a particular thumbnail exists on the page (based on the label of 'Panels') and what happens when we click on the thumbnail (it will take us to the PanelsPage). The content block for a Page allows us to specify components of the page or attributes/expressions that may be useful and in this case, we use the module method to indicate we are referencing a module for this panelsThumbnail content. So, let's look at how we might create a module representing a thumbnail.

When creating Geb modules like this, there are two main things to think about. One is how the user is going to specify how to get to/find a particular component. In the thumbnail case we are looking at, each thumbnail has a label so we will use this in order to find a particular thumbnail. The second consideration for modules is how the user will interact with the component. In this case, users will be clicking on thumbnails which will take them to other (logical) pages in our application. So let's look at the ThumbnailModule.

All Geb modules must extend from Module.

Here we specify content just like on the Page. For this module though, we will be allowing the user to specify both the label of the thumbnail and the page for when the user clicks on the thumbnail. So, these are class variables which can then be set using the Groovy syntax

as we saw above. We then reference these class variables in our 'thumbnail' content. Geb allows for many different ways to find the components you are looking for using CSS selectors and various helper methods. In this case, we are looking for a div with a class of thumbnail-text which has text matching the label passed in to our ThumbnailModule when it was created. Once we have a navigator representing this element, we call .parent() on it so that our thumbnail content refers to the div surrounding the entire thumbnail which includes both the text as well as the icon for the thumbnail. We will see this a few times - finding elements by some child text and then going up the DOM tree to set our content item. In order to actually figure out these low level details of the components we are modeling - like how there is a div with a class of thumbnail-text which is a child of the div we really want - we can simply use the inspector tools which come with modern browsers to inspect the DOM and figure it out.

So we've figured out how to find a particular thumbnail - but what about clicking on one?

In this case we will create a navigate() method which users can use on our module in order to navigate to the page represented by that thumbnail. When we declared the thumbnail content, we specified:

which let Geb know that when we call click() on that element, that we should be taken to the specified page (so Geb can verify we are indeed at that page using that page's 'at' checker like we saw above). We could have had users of our module simple call <module>.thumbnail.click() but sometimes it is nice to have these convenience methods to abstract some of the module details away.

Ok, so now we finally have our ThumbnailModule in place. In order to get

to work on our KitchenSinkPage, we just need to create our PanelsPage. This page will be very similar to our KitchenSinkPage since it will just be referencing another thumbnail:

BasicPanelsPage we can create without any content for now since we will look at it a little later.

At this point, perhaps we should look at writing our Spock spec to actually start doing some testing! We will create PanelsSpec and walk through some of it:

All Geb specs should extend from GebReportingSpec.

Here we see all of our hard work come together to make it easy to construct/understand tests. Here we are using the 'to' method in order to navigate to the KitchenSinkPage we created. This will go to that page's URL and verify we got there successfully using the 'at' checker which verifies the page's title is as we expect. Since we are now 'at' that page, we can easily reference any content declared for that page. So, we reference the panelsThumbnail of that page which encapsulates the thumbnail with the label 'Panels' and call navigate() on it in order to go to the PanelsPage. Once we are on the PanelsPage, we can reference the basicPanelsThumbnail content we declared and again call navigate() on it in order to finally get to a place where Ext JS panels are being created. We can run this Spec class and we should see the browser starting up, going to the Kitchen Sink page, and then clicking on the thumbnails to get to the Basic Panels examples. For details on configuring the browser driver for Geb, you can see check out the GebConfig we are using or read the excellent Geb Manual for more details on how to run Geb tests for the first time.  You can also read up on Spock for how specs work in general.

So, we are now on the basic panels example page and want to actually test out Ext JS panels. So, let's start to map out what we would want the content for this BasicPanelsPage to look like:

We see four panels on the basic panels example page, so we would probably expect to see four different pieces of content for our Geb page. The main difference here from anything we've seen before is that instead of working with a ThumbnailModule, we are working with a PanelModule. This is the crux of what we wish to accomplish - modeling Geb modules after Ext JS components! As we can see, for panels with header text, we would like to be able to easily reference those panels by this header text. However, since not all panels may have header text, we also want to provide our own selectors to find panels as well. In the above example, we see we can use the hasNot helper method in Geb to find elements with a class of x-panel that do not have any child elements with a class of x-title-text.  So, onto PanelModule for how this might work!

After seeing the ThumbnailModule above, there should be no huge surprises here - just a few differences. We have header and selector class members to allow users to specify either the header text for finding the panel or an actual selector if there is no convenient header text. This is then used for the main panel content. The closest() method is used instead of parent() so that it will go up the DOM tree until it sees an element with a class of x-panel (parent will just go up one level). Again, we can use browser inspector tools to figure out all of these details.

Getting a hold of the main panel element is key and once we have this, we can easily expose other useful things about the panel. The bodyText content uses the panel navigator to then find a child element with a class of x-panel-body and then retrieves the text for that element. The tool content demonstrates the use of a parameter - in this case called toolClass - to find a child element of the panel navigator with a class of x-tool-<toolClass> and then goes up to find the closest parent element with a class of .x-tool. It can be a little tedious to construct these selectors to find the content but then once we do, we can easily enhance our spec to use these:

How beautiful! We can reference and test the various basic panels of this application in a way that is very easy on the eyes. Since we created PanelModules in the content block of our BasicPanelsPage, we can reference these and then reference the content within a particular PanelModule. So, we can assert <panel module>.panel to verify the panel exists and assert that<panel module>.bodyText matches text we expect to be there. For the collapsible panel, we can interact with the collapse and expand tools and verify that the bodyText is no longer present/visible when the panel is collapsed.

It took us a little while to look at how to build Geb Pages and Modules in order to start modeling the Ext JS Kitchen Sink application, but once you get a hang of the Groovy capabilities of Geb, things are usually pretty straightforward. You should be able to see how this could be extended to all of the different Ext JS components which would then make it much easier to test your application if it was written in Ext JS. Or, as stated above, if you are using any kind of client side framework (or have your own), you can use Geb to model your UI components as Geb modules to lower the barriers of entry for setting up functional testing in an easy-to-read, maintainable way!