General Problems With Geb (StaleElementReferenceException & Wait Timeouts)

AndyDoe picture AndyDoe · Mar 27, 2014 · Viewed 7.1k times · Source

According to the "Book of Geb" I started to map our portal's web pages. I prefer to use variables defined within static content closure block and accessing them afterwards in page methods:

static content = {
    buttonSend { $("input", type: "submit", nicetitle: "Senden") }
}
def sendLetter() {
    waitFor { buttonSend.isDisplayed() }
    buttonSend.click()
}

Unfortunately, sometimes I get an Geb waiting timeout exception (after 60 secs) or even worse I receive the well known "StaleElementReferenceException".

I could avoid the wait timeout when using "isEnabled" instead of "isDisplayed" but for the "StaleElementReferenceException" I could only apply the below solution:

def sendLetter() {
    waitFor { buttonSend.isEnabled() }
    try {
        buttonSend.click()
    } catch (StaleElementReferenceException e) {
        log.info(e.getMessage())
        buttonSend.click()
    }
}

I guess, this solution is not really nice but I could not apply an explicitly wait as described in another article. Thus, I have some general questions:

  • Should I avoid to use static content definitions when pages are dynamically?
  • At what time or event Geb is refreshing its DOM? How can I trigger the DOM refreshment?
  • Why I still get a "StaleElementReferenceException" when using CSS selectors?

I would appreciate every hint which helps to understand or to solve this issue. The best would be to have a simple code example since I'm still a beginner. Thank you!

Answer

twinj picture twinj · Jun 24, 2014

If you defined an at check on your page class the page would first verify that condition and wait for the first n seconds. Which is assigned in your gebConfig file. The default is 30 seconds.

static at = {
    waitFor { buttonSend.isDisplayed() }
}

Thus once you call your pages 'to' method with a test or whatever you are using it for the page will wait and then perform your page manipulations.

to MyPage
buttonSend.click()

Should I avoid to use static content definitions when pages are dynamically?

No. Actually, the static definitions are of closures. So what is actually happening is each time you make use of that Pages static components you are calling a closure which is run dynamically on the current page(collection of webElements). Understanding this is key to using Geb and discovering the problems you will run into.

At what time or event Geb is refreshing its DOM? How can I trigger the DOM refreshment?

When you call: to, go, at, click ,withFrame(frame, page), withWindow and browser drive methods it will refresh the current set of WebElements. Geb has a nice collection of utiliities to make switching between pages and waiting for page manipulations easy. Note: Geb is actually built on WebDriver WebElements.

Why I still get a "StaleElementReferenceException" when using CSS selectors?

It is possible the page hasn't finished loading, has been manipulated with ajax calls or has been refreshed in some other way. Sometimes an 'at' PAGE method call can fix these issues. They are for me most common when using frames as Geb seems to become confused between pages and frames a little. There are workarounds.

In short if you use the page pattern you can easily switch expected pages using the Page class you have defined with a static content, at, and url closure using the below:

  • to(Page)
  • at(Page)
  • Navigator.click(Page)
  • withFrame(frame, Page) { }