JavaScript + DOM awaiting visible/invisible

In another thread (Verify Element Attribute Value - #36 by Mate_Mrse), @Mate_Mrse suggested I post a Tip about using JS + DOM to wait for elements to be come visible/invisible. Since responding directly on that thread would have been a significant derailing of the thread’s topic, I did as he suggested and posted my response here.

But I do know JS :rofl:

Sorry. Couldn’t resist. :slight_smile:

You happened to choose the most (IMO) “needless” WebUI API: waitForElementClickable

Profound statement: waitForElementClickable is essentially pointless

Providing an element is visible it is clickable (visibility itself is an exercise in pedantics and semantics but “we know what we mean”, right?)

Let me say that again, another way…

Testing if an element is clickable when all elements are intrinsically clickable, is pointless. You’re testing the browser’s implementation of HTML and DOM, not your AUT.

If it’s visible and in the viewport, it’s clickable, end of story.

(In fact, in javascript, it doesn’t even need to be in the viewport.)

The reason for the pedancy offered by WebDriver is simply that it is trying ensure that they mimic a HUMAN. Hence, your target needs to be on screen, visible, and in the viewport. That is completely worthwhile but can sometimes get in your way. If a human does NOT have an issue at some point in your test, then don’t let WebDriver stand in your way (I expanded on this point here: Send Keys or Set Text - #2 by Russ_Thomas)

I don’t know what Kat does in that clickable API (don’t really care, either). But since the purpose I (and you) have in mind is best dealt with by the removal of another element from the scene which reveals my target element, that, to me, is the better way to go. I’m saying, the clickability of elements is not in question - unless you work for google, or mozilla… see?

Anyway, back to your question:

Then THAT is what it is. Doesn’t matter if you think there’s something else going on under the covers, you have one thing to deal with and its effect(s) on the client. Don’t get bogged down with shit you can’t nail down. Absolutely and fundamentally, when that guy has left the scene, you can proceed. Period.

Profound statement 2: WebDriver is a very faithful but very dumb robot

The most likely cause of your grief is synchronicity. WebDriver is a very faithful but very dumb robot. He does exactly what you tell him, exactly when you tell him. Your task is to make sure you tell him when the time is right.

Yes, I know you know all this, but ask yourself if that’s exactly what you are doing when you bump into an issue like this. I can virtually guarantee the times you have issues, you’re not getting the timing right (for whatever reason).

Okay. Enough theory.

Instead of giving you working code, I’ll give you well-commented pseudo-code. Your (or anyone else’s) situation is unlikely to fit my situation exactly, so pseudo code will help us better.

// Generate a conversation (ajax perhaps) between AUT and Server
clickSomething()

// Expect a spinner. If network is super-quick, spinner might not be seen 
// (destroyed even before browser gets chance to show it)
// handleSpinner must handle that situation too!
handleSpinner()

So now let’s look at handleSpinner …

handleSpinner() {
  // The spinner might appear very quickly and then disappear just as quickly
  // so DO NOT wait for it to APPEAR!
  // One thing is for sure (because we tested this, didn't we?) it does appear 
  // before two seconds have elapsed.
  WebUI.delay(2)  // Pragmatic use of a fixed delay for well-documented reasons!
  // But even so, test this by reducing that delay, even to zero. Check results 
  // over a series of tests/suites and pay attention to the results!

  // Okay... the spinner should be up!
  // Now we can "wait" for it to disappear...
  // Use a Groovy+JS loop which incorporates fixed 1-second delay with a maximum wait
  // period of TIMEOUT_SECONDS (under dev, I use 20, in suites I use 90)
  int count = 0 // number of times the loop actually looped
  boolean spinner_still_visible = true // when this turns false, we're finished
  // Now the JS string that proves the spinner is invisible: 
  //   could be jQuery, 
  //   could be checking a CSS property change
  //   anything that works for your situation
  String js = 'if spinner is visible return true else false'
  while(spinner_still_visible) {
    WebUI.comment('handleSpinner checking visibility: ' + count )
    count++
    if(count == TIMEOUT_SECONDS) {
      throw StepErrorException(failMessage) // "Spinner still up!!!" perhaps?
    }
    spinner_still_visible = (boolean) WebUI.executeJavaScript(js, Arrays.asList(...))
    // Is the spinner invisible? break out of the while loop if so...
    if(! spinner_still_visible) break
    // The spinner is still visible. Wait 1 second and loop back.
    WebUI.delay(1)
  }
  WebUI.comment('handleSpinner success: Spinner has disappeared!')
}

Now, a little more theorizing…

Because we know everything is essentially clickable, we’re not waiting until our target is visible and clickable, we are inferring our target is clickable after ensuring that the thing blocking it is invisible. That’s pragmatic and meaningful. Better still, it’s testable

And if something is testable you can reason about it. When it isn’t, you get this: :exploding_head:

:sunglasses:

2 Likes

That’s great Russ, but you didn’t address this

:sweat_smile:.

Could the js be something like "return $(document).ready"?

EDIT:

I did it with js = "!!document.getElementById('loading')".

That’s fine. And that makes the point I was making here:

What your code is doing is checking element existence (not visibility, per se). That’s perfectly fine.

No, not really. Checking for any form of “document ready” doesn’t help when checking for visibility (not directly, anyway). And the code you quoted is jQuery (or perhaps a poor-man’s mimic of it), which doesn’t help if jQuery is not in use.

If we (you) want to talk about waiting in general (not just waiting on visibility) we can certainly do that - but I’d change the title of this thread. Let me know.