38

I currently have a large number of circumstances where I need to verify that a page (along with all of its elements) are displaying correctly. The isDisplayed() method of WebElement appears to be a logical way to do this, however I would like to understand precisely what this method is doing to determine whether or not an element "is displayed". The javadoc does not shed any light on the inner workings of the method and other information on the web appears to be sparse at best.

If anyone could provide a detailed description of how this method works, I would be very grateful.

Mrchief
  • 75,126
  • 20
  • 142
  • 189
Mr. Spice
  • 1,552
  • 1
  • 15
  • 15
  • I am asking a related question: http://stackoverflow.com/questions/27851122/where-is-the-implementation-for-the-webelement-isdisplayed-method-in-selenium – djangofan Jan 08 '15 at 23:04

3 Answers3

35

I would trust in Selenium to work out if an element is displayed or not. If it doesn't work you can raise a bug and/or fix any issues you see and provide a patch.

This is what the method does (Taken from the current Selenium source code):

/**
 * Determines whether an element is what a user would call "shown". This means
 * that the element is shown in the viewport of the browser, and only has
 * height and width greater than 0px, and that its visibility is not "hidden"
 * and its display property is not "none".
 * Options and Optgroup elements are treated as special cases: they are
 * considered shown iff they have a enclosing select element that is shown.
 *
 * @param {!Element} elem The element to consider.
 * @param {boolean=} opt_ignoreOpacity Whether to ignore the element's opacity
 *     when determining whether it is shown; defaults to false.
 * @return {boolean} Whether or not the element is visible.
 */
bot.dom.isShown = function(elem, opt_ignoreOpacity) {
  if (!bot.dom.isElement(elem)) {
    throw new Error('Argument to isShown must be of type Element');
  }

  // Option or optgroup is shown iff enclosing select is shown (ignoring the
  // select's opacity).
  if (bot.dom.isElement(elem, goog.dom.TagName.OPTION) ||
      bot.dom.isElement(elem, goog.dom.TagName.OPTGROUP)) {
    var select = /**@type {Element}*/ (goog.dom.getAncestor(elem, function(e) {
      return bot.dom.isElement(e, goog.dom.TagName.SELECT);
    }));
    return !!select && bot.dom.isShown(select, /*ignoreOpacity=*/true);
  }

  // Image map elements are shown if image that uses it is shown, and
  // the area of the element is positive.
  var imageMap = bot.dom.maybeFindImageMap_(elem);
  if (imageMap) {
    return !!imageMap.image &&
           imageMap.rect.width > 0 && imageMap.rect.height > 0 &&
           bot.dom.isShown(imageMap.image, opt_ignoreOpacity);
  }

  // Any hidden input is not shown.
  if (bot.dom.isElement(elem, goog.dom.TagName.INPUT) &&
      elem.type.toLowerCase() == 'hidden') {
    return false;
  }

  // Any NOSCRIPT element is not shown.
  if (bot.dom.isElement(elem, goog.dom.TagName.NOSCRIPT)) {
    return false;
  }

  // Any element with hidden visibility is not shown.
  if (bot.dom.getEffectiveStyle(elem, 'visibility') == 'hidden') {
    return false;
  }

  // Any element with a display style equal to 'none' or that has an ancestor
  // with display style equal to 'none' is not shown.
  function displayed(e) {
    if (bot.dom.getEffectiveStyle(e, 'display') == 'none') {
      return false;
    }
    var parent = bot.dom.getParentElement(e);
    return !parent || displayed(parent);
  }
  if (!displayed(elem)) {
    return false;
  }

  // Any transparent element is not shown.
  if (!opt_ignoreOpacity && bot.dom.getOpacity(elem) == 0) {
    return false;
  }

  // Any element with the hidden attribute or has an ancestor with the hidden
  // attribute is not shown
  function isHidden(e) {
    //IE does not support hidden attribute yet
    if (goog.userAgent.IE) {
      return true;
    }
    if (e.hasAttribute) {
      if (e.hasAttribute('hidden')){
        return false;
      }
    } else {
      return true;
    }
    var parent = bot.dom.getParentElement(e);
    return !parent || isHidden(parent);
  }

  if (!isHidden(elem)) {
    return false;
  }

  // Any element without positive size dimensions is not shown.
  function positiveSize(e) {
    var rect = bot.dom.getClientRect(e);
    if (rect.height > 0 && rect.width > 0) {
      return true;
    }
    // A vertical or horizontal SVG Path element will report zero width or
    // height but is "shown" if it has a positive stroke-width.
    if (bot.dom.isElement(e, 'PATH') && (rect.height > 0 || rect.width > 0)) {
      var strokeWidth = bot.dom.getEffectiveStyle(e, 'stroke-width');
      return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);
    }
    // Zero-sized elements should still be considered to have positive size
    // if they have a child element or text node with positive size, unless
    // the element has an 'overflow' style of 'hidden'.
    return bot.dom.getEffectiveStyle(e, 'overflow') != 'hidden' &&
        goog.array.some(e.childNodes, function(n) {
          return n.nodeType == goog.dom.NodeType.TEXT ||
                 (bot.dom.isElement(n) && positiveSize(n));
        });
  }
  if (!positiveSize(elem)) {
    return false;
  }

  // Elements that are hidden by overflow are not shown.
  if (bot.dom.getOverflowState(elem) == bot.dom.OverflowState.HIDDEN) {
    return false;
  }

Not sure it really needs any more explanation, the comments are quite clear. Let me know if you want any more info added.

Mateusz Marchel
  • 772
  • 4
  • 14
Ardesco
  • 7,281
  • 26
  • 49
  • So to what scale do you think it would be wise to use it to? For example to make sure a table is displayed, would you just check the table WebElement? Or loop through the individual rows, calling isDisplayed on each one? Or just make sure that the
    in which it is contained isDisplayed?
    – Mr. Spice Aug 06 '13 at 11:48
  • 3
    It depends on how the page you are testing is implemented. Generally I wouldn't expect specific cells or rows in a table to disappear so I would just check that the table is visible. However if you are expecting the table to hide specific rows/cells I would probably go about checking each one. Knowledge of the application you are testing will tell you which approach is the correct one. – Ardesco Aug 06 '13 at 13:09
27

WebDriver has its own W3C specification.

The section about determining visibility is what you are after.

I would warn that saying something "is displayed" is such a broad term, and thus there are many scenarios to it. Therefore, there may well be situations that WebDriver does not account for.

So it's important, vital in fact, to remember that something being "displayed" or "visible" has many meanings. (In the same way a page being fully loaded, also has many meanings.)

Also remember Selenium is entirely open source. There is nothing stopping you from getting a fresh checkout of the repository and inspecting it locally.

Sorin
  • 1,965
  • 2
  • 12
  • 18
Arran
  • 24,648
  • 6
  • 68
  • 78
  • So, for example, relying on the isDisplayed() method to tell me if the rows of a table are showing up would not be an ideal way to test? Would it be better to grab the table and cycle through the rows and check that the values are as expected? – Mr. Spice Aug 05 '13 at 16:23
  • Depends -> are you wanting to verify they are displayed regardless of the content? Verifying the content is probably a better way around it. I'm not saying using `isDisplayed` is a bad thing, it's just that there are many definitions of "is this element visible to the user". It's served me well so far, but I have seen it trip a few people up. – Arran Aug 05 '13 at 16:32
  • I will be attempting to make sure that all the important elements are displayed in different browsers. So at the moment I'm not worrying about content - that will come when I start automating the functional testing. – Mr. Spice Aug 05 '13 at 16:40
0

As per the documentation isDisplayed() method determines whether the WebElement is displayed or not and returns a boolean whether or not the element is displayed or not. This method avoids the problem of having to parse an element's style attribute.

This implementation is inline with the specification with in the WebDriver Level 2 W3C Working Draft which mentions:

Although WebDriver does not define a primitive to ascertain the visibility of an element in the viewport, we acknowledge that it is an important feature for many users. Here we include a recommended approach which will give a simplified approximation of an element’s visibility, but please note that it relies only on tree-traversal, and only covers a subset of visibility checks.

The visibility of an element is guided by what is perceptually visible to the human eye. In this context, an element’s displayedness does not relate to the visibility or display style properties.

The approach recommended to implementors to ascertain an element’s visibility was originally developed by the Selenium project, and is based on crude approximations about an element's nature and relationship in the tree. An element is in general to be considered visible if any part of it is drawn on the canvas within the boundaries of the viewport.

The element displayed algorithm is a boolean state where true signifies that the element is displayed and false signifies that the element is not displayed. To compute the state on element, invoke the Call(bot.dom.isShown, null, element). If doing so does not produce an error, return the return value from this function call. Otherwise return an error with error code unknown error.

This function is typically exposed to GET requests with a URI Template of:

/session/{session id}/element/{element id}/displayed
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352