I write a lot of Selenium automation for a very dynamic and Ajax-y application.
Lucky me!
Pages load with some areas populating more quickly than others as endpoints are accessed in the background and the page must wait for the data to load. While this is happening, the areas in progress display ‘spinners’, swirling animated icons to visually indicate to the end user that we’re working as hard as we can to get the data they’re looking for.
This is a fine and very helpful thing, but a fair pain in the butt to encounter when doing browser automation. While writing a method to wait for these things to come and go is pretty straightforward, there can be complications. In our product, we have large spinners and small, each with different class names, which can lead to some fugly coding to cope with them all.
With the olde and oft-maligned XPATH locators, there is an easier way!
I’m not going to get into the logic of how best to implement a wait strategy for these things. There are many ways, but most will rely on the basic idea of
1. Wait for spinner(s) to appear (in case the spinner itself takes a while to load)
2. Wait for the spinner(s) to disappear
What I did was to write a single method to wait for any and all spinners of any size or naming convention on a given page to stop. The kewl thing is, no matter how many varieties there are, with XPATH, we can handle them all with a single locator as follows:
public final static By by_roundSpinnerIcon = By.xpath(
"//*[@class='fa-circle-o-notch fa-2x fa-spin'] | "
+ "//*[@class='spinner_container'] | "
+ "//*[@class='fa-spin fa-spin-fast'] | "
+ "//*[@class='spinny'] | "
+ "//*[@class='fa fa-circle-o-notch fa-4x fa-spin'] |" // many kinds of spinners
A personal quirk of my automation visible here is a fondness for using By objects to store locators, be they CSS, XPATH, link text or whatever. This makes it much easer to create helper methods to deal with dynamic content, like dependableClick(By), waitForElementExists(By)
and so on. There are other and probably better ways to do this, but this is my current approach.
In any case, what’s great about this sort of xpath locator, is the ability to use an OR operator ‘|’ to consolidate different xpath locator strings into 1 single entity. Now, without any programming logic whatsoever, I can wait for and interact with an element that matches any 1 of those xpath strings.
Just today I ran into some tests that were failing suddenly, and I noticed that they were no longer heeding the waitForSpinner()
method like they used to, and were trying to interact with elements of a page that was not fully loaded. Once I spotted this, I did a bit of investigation and found a new class name had been added to one of the spinners, which I was quickly able to create an xpath string for: "//*[@class='loading_spinner spin']"
.
I then added this 1 string to the others on the by_roundSpinnerIcon
locator, which resulted in fixing 4 broken tests in one fell swoop! Here’s the new xpath locator:
public final static By by_roundSpinnerIcon = By.xpath(
"//*[@class='fa-circle-o-notch fa-2x fa-spin'] | "
+ "//*[@class='spinner_container'] | "
+ "//*[@class='fa-spin fa-spin-fast'] | "
+ "//*[@class='spinny'] | "
+ "//*[@class='fa fa-circle-o-notch fa-4x fa-spin'] |"
+ "//*[@class='loading_spinner spin']"); // many kinds of spinners
Of note, troubleshooting a spinner icon is not always easy, as they tend to fly by quickly before you can inspect them. Here’s one low tech way to do this without having to get into javascript, break points and so on:
1. Open your application to the area with the spinner and dynamic content in the Chrome browser
2. Open the Development Console and click to ‘Customize and control DevTools’ on the right
3. Go down to the ‘More tools’ menu and choose ‘Network conditions’
4. Throttle the network down to something low enough for your application to load, and then when the spinner itself loads, throttle down even further.
5. With the spinner available, right-click-Inspect it, and hopefully you’ll have enough time to at least grab a screen shot you can look at more closely afterwards!
Like I said, ‘lo tech’! It worked for me though and can be handy in a pinch to catch a fleeting element that you want to inspect.