I have been working with GEB and selenium for some time now, and many a time I have run into the dreaded stale element exception because one of the pages I have to test loads dynamically, thus inducing the stale element exception.
I have come very close to creating a catch all solution to the stale element exception but alas not close enough which is why I need help.
My solution was to override the NonEmptyNavigator class that comes with GEB. I am going to show my click() method as an example:
class NonEmptyNavigator extends geb.navigator.NonEmptyNavigator {
def NonEmptyNavigator() {
super()
}
NonEmptyNavigator(Browser browser, Collection<? extends WebElement> contextElements) {
super(browser, contextElements)
}
//overridden click method (all of the methods are overridden though
Navigator click(count = 0){
if (count >= 60) {
return super.click()
}
else{
try{
return super.click()
}
catch (StaleElementReferenceException s) {
def oData = this.toString()
def matcher = Pattern.compile("-> ([^:]+): (.*)]").matcher(oData) //Parses out the xPath
matcher.find() //Again more Parsing
def newXpath = matcher.group(2) //final Parsing step
newNav = browser.$(By.xpath(newXpath)) //create a new NonEmptyNavigator from the Stale Navigator's xpath
return newNav.click(count + 1) //attempt to click the new Navigator
}
}
}
}
Now you might be thinking "Wow this is a really good solution" (and it is) but there are instances where this doesn't work, and I am not sure how to overcome. Let me give an example.
If I do something like this (simplified for readability):
class SomePage extends Page{
static content = {
table(required: false) {$(By.xpath("//table/tbody"))}
}
//assume this method gets called in a test script
def someMethod(){
table.click() //assume this throws a StaleElementException
}
}
Referencing my overridden method above, oData.toString() ends up being something like: "[[[ChromeDriver: chrome on XP (2cd0a7132456fa2c71d1f798ef32c234)] -> xpath: //table/tbody]]"
as you can see I am able to extract the xpath and create a new navigator object which is great.
Where I run into problems is when faced with a situation like this:
class SomePage extends Page{
static content = {
table(required: false) {$(By.xpath("//table/tbody"))}
}
//assume this method gets called in a test script
def someMethod(){
table.children().getAt(1).children().getAt(2).click() //assume this throws a StaleElementException
}
}
When executing the click() throws a stale element, oData.toString() appears like this: "[[[[[ChromeDriver: chrome on XP (2cd0a7132456fa2c71d1f798ef32c234)] -> xpath: //table/tbody]] -> xpath: child::*]] -> xpath: child::*]]"
As you can see there is some information showing that I am currently trying to access the child of a child node, but I no longer have the reference I need to redefine that specific element. I don't have the index of the specific child (or children) I want.
I am wondering if there is any way I can obtain that information given my current framework. I would also be open to other ideas and suggestions.
All in all I am essentially looking to create a catch all solution to the StaleElementException. I think I am pretty close and need a little nudge to get over the final hump.