Waiting for one of multiple matching elements to become visible

364 views
Skip to first unread message

Brian Kotek

unread,
Feb 13, 2014, 12:29:52 PM2/13/14
to sele...@googlegroups.com
Selenide is proving to be a very nice add-on for Webdriver, so thank you for the work so far. However, I'm having a frustrating problem that seems so basic that I feel like I have to be missing something.

Say I have a page with one or more elements that will match a given selector (e.g. "div.myclass"). All I want to do is wait for ONE of those elements to become visible, and then be able to manipulate it.

I've tried this every way I can imagine, including:
  • $( "div.myclass" ).waitUntil( appears, 5000 )
  • $( "div.myclass" ).waitUntil( visible, 5000 )
  • $( "div.myclass:not([style*='display: none']" ).waitUntil( appears, 5000 )
  • $( "div.myclass:not([style*='visibility: hidden']" ).waitUntil( appears, 5000 )
  • $( "div.myclass:not([style*='display: none']" ).waitUntil( visible, 5000 )
  • $( "div.myclass:not([style*='visibility: hidden']" ).waitUntil( visible, 5000 )
  • $$( div.myclass ).findBy( visible ).waitUntil( appears, 5000 )
  • $$( div.myclass ).findBy( visible ).waitUntil( visible, 5000 )
  • $$( div.myclass ).findBy( visible ).waitUntil( present, 5000 )
And so on. I can do a $$( div.myclass ).size() and confirm the matching elements do exist (they're just hidden to start with). And I know they become visible because I can SEE them become visible when the test runs. I can also look at the elements in the Chrome elements panel and see that they have the 'display: none' style, which obviously changes when the element becomes visible on the page.

Part of the problem is also the Selenide documentation. I totally understand that open-source work is time-consuming and often thankless. I'm part of several OSS projects myself. But the docs are just so limited that it's very hard to tell the difference between many of the methods and conditions. (What's the difference between appears, appear, and visible? find() and findBy()? filter() and filterBy()? etc.)

This is driving me nuts because I have to believe that this is an extremely common thing to need to do. I'm hoping that I'm just being an idiot and there's a simple answer to this.

Thanks,

Brian

Brian Kotek

unread,
Feb 13, 2014, 3:45:37 PM2/13/14
to sele...@googlegroups.com
Just to follow up, I figured this out. In case it helps anyone else in the future:

The div I was waiting on is a loading mask that shows up while an async process is going on. Because I'm using "mock" JSON data during testing, the load happens very quickly. So even though I could SEE the mask appear and then disappear, it looks like it happened fast enough that Selenium never even caught it. I'm not sure what internal Selenium checks the DOM, but apparently things can slip though if they happen fast enough.

Once I realized this, I just created a helper function like:

protected void waitForLoadingMaskRemoved() {
  SelenideElement existingMask = $$( "div.x-mask" ).find( visible )
  if( existingMask.exists() ) {
    existingMask.waitUntil( disappears, 5000 )
  }
}

So that IF a visible mask is found, it waits for it to be hidden. Otherwise, things just keep going. This seems to work perfectly.

Regards,

Brian



--
You received this message because you are subscribed to the Google Groups "selenide" group.
To unsubscribe from this group and stop receiving emails from it, send an email to selenide+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Andrei Solntsev

unread,
Feb 13, 2014, 3:46:29 PM2/13/14
to Brian Kotek, sele...@googlegroups.com
Hi!
Thank you for feedback! 

At a first glance, I think that your wish is not typical. It's not a good idea to wait ANY of matching elements. Correct test should exactly know which element to operate with. Test should not contain/use any random data. Test should consist of three steps:

1. Prepare data. 
    At this point, you exactly know that there should be e.g. 5 rows in page. And 3rd of them should be visible.

2. Open web page and assert that the 3rd row is visible and contains expected text. 
 

Getting back to your question. Unfortunatelly it's not possible to filter collection by css tricks like "div:visible" or ":not([style*='display: none']" because Selenium WebDriver supports only very simple CSS selectors.

But filter or findBy methods should help:
  • $$( div.myclass ).filter( visible ).shouldHave(size(1));
  • $$( div.myclass ).findBy( visible ).shouldHave(text("your expected text"));
P.S. Yes, Selenide documentation is not absolutely full yet. You are welcome to fullfill it and send Pull Request! By the way, do you mean javadoc or documentation page?

As a developer, I never trust documentation. I believe that source code is the only reliable source of knowledge. For example, in Intellij IDEA It's very easy to click "download sources" and browse Selenide source code right in your IDE. It's just not needed to read documentation. I believe the code is very straightforward and easy to read. 
By the way, methods "find and "findBy"are synonyms. "filter" and "filterBy" synonyms. Conditions "present" and "exist" are synonyms. "appear" and "visible" are synonyms. These synonyms were created to allow writing readable and expressive tests. Compare "shouldBe(visible)" and "should(appear)". 

Andrei Solntsev


Andrei Solntsev

unread,
Feb 13, 2014, 3:49:50 PM2/13/14
to Brian Kotek, sele...@googlegroups.com
Hi again!
Good catch. Let me suggest you even better and simpler solution. 

Just drop this check. I mean, drop method waitForLoadingMaskRemoved. I believe it's not critical for your business that loading mask appears and disappears. 

Test the next thing. What should happen after loading mask disappears? Test it. Forget the loading mask. It's important. 

Andrei Solntsev
Reply all
Reply to author
Forward
0 new messages