Nightwatch: Element visible and is present but unable to click on it

Created on 4 Oct 2017  路  6Comments  路  Source: nightwatchjs/nightwatch

Morning all
I've been experiencing an issue for the passed week or two where my automated tests are no longer working when I try to click on an element which is below the fold on the screen. The element is visible and present but looks like it cant be clicked as its below the fold and requires a scroll down.
See simple test below:

browser.waitForElementPresent('.price');    //Element .price was present after 37 milliseconds.
browser.waitForElementVisible('.price');    //Element .price was visible after 49 milliseconds.
browser.click('.price');
browser.waitForElementPresent(#product-details);    //Timed out while waiting for element #product-details to be present for 30000 milliseconds.  - expected "visible" but got: "not found"

In the test above I check if element .price is visible and present, then I click on the element and wait for the next page to appear. I then wait for an element from the next page to become present (#product-details).
If I manually scroll down the page the test passes as it can click on the element, but if I don't the test fails. I know I can add a scroll before the click command, but the position of the element may change in the future, i.e. it may move to the top of the page where a scroll is no longer required.

I am currently experiencing these issues on Chrome Version 61.0.3163.100. If I roll back to an earlier version of Chrome (for example 60.0.3112.113), the test above works fine (the page automatically scrolls down to the element before clicking).

Anyone else having the same issue or know the solution?
Thanks in advance.

Most helpful comment

I updated my version of chromedriver to the latest which was released a few days ago and it all seems to work as expected now.

I came up with two different click workarounds which worked for me:

browser.execute(function() {
   document.querySelector('.price').click()
});

OR

browser.getLocationInView('.price', function(result) {
   browser.execute('scrollTo(0, ' + result.value.y +')')
});

All 6 comments

You may have to focus the element before the click will work. Have seen issues where an item is on the edge of the viewport and the click doesn't register.

you can do this via javascript with a call like this as a custom command (going off top of my head so this may not be 100%)

function focusElement(type, selector) {
  return this.execute(function click(selType, sel) {
    let ele;
    if (selType=== 'css') { 
      ele = document.querySelector(sel);
    else {
      ele = document.evaluate(sel, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }
    return ele['focus']();
  }, [type, selector], (result) =>{ console.log(result);}
  });
}

exports.command = focusElement;

and then call it from your test like

...
browser.focusElement('css', '.price');
browser.click('.price');
...

if your dealing with xpath it would looks something like this
browser.focusElement('xpath', '//*[@class="price"]');

In this particular case the element is way down on the second / third page, so its on really on the edge of the viewport. Just doesn't make sense why it would work on the older version of Chrome but not on the latest one.

@MauiMaui - if you are using the selenium docker images - possible they have changed the viewport sizes defaults. If you're running direct on your own chrome browser - could be something like a smaller header bar, font size... just guessing at this point. I know looking through screenshots of test runs over the year I have seen slight differences over time on the browser screens I get back.

I've been using a focusElement method in my nightwatch tests for awhile and is working using the newest 3.6.0 node-chrome-debug flavour from selenium. If memory serves me right - I added the focusElement due to weird issues with it not clicking awhile back... hence the reason I thought this custom command might be useful. I also wrapped the javascript click method for some stubborn controls as well - but I think I have since gotten them all back to using the default click from nightwatch.

Did focusing the element fix the issue though?

I updated my version of chromedriver to the latest which was released a few days ago and it all seems to work as expected now.

I came up with two different click workarounds which worked for me:

browser.execute(function() {
   document.querySelector('.price').click()
});

OR

browser.getLocationInView('.price', function(result) {
   browser.execute('scrollTo(0, ' + result.value.y +')')
});

you can write your command.var util = require('util');

var events = require('events');

function WaitForClick () {
  events.EventEmitter.call(this);
}

util.inherits(WaitForClick, events.EventEmitter);

WaitForClick.prototype.command = function commandFn (selector, ms, cb = function () {
}, abortOnFailure) {
  this._stackTrace = commandFn.stackTrace;
  this.selector = selector
  this.abortOnFailure = abortOnFailure
  if (!ms) {
    return this;
  }
  let self = this;
  let stime = Date.now()

  function click () {
    this.client.api.click(this.selector, (result) => {
      console.log('poll 500 for clickable')
      console.log('-------------->')
      console.log(result)

      if (result.status === 0) {
        if (typeof  cb === 'function') {
          self.client.assertion(true, 'clickable', 'clickable', this.selector + 'clickable', this.abortOnFailure, this._stackTrace);
          cb.call(self, result)
          self.emit('complete');
          return self
        }
      } else {
        let now = Date.now()
        if (now - stime > ms) {
          cb.call(self, result)
          self.client.assertion(false, 'unClickable', 'clickable', this.selector + 'element not clickable', this.abortOnFailure, this._stackTrace);
          self.emit('complete');
          return self
        }

        setTimeout(click.bind(self), 500)
      }
    })
  }

  click.call(this)
  return this;
};

module.exports = WaitForClick;

place the code in waitForClick,and config thenightwatch.json custom_commands_path to the file path.

then you can write
.waitForClick('selector',3000,cb)

The moveToElement nightwatch works very well for this with one extra line.

browser.moveToElement('.price', 10, 10); browser.waitForElementVisible('.price'); browser.click('.price');

https://nightwatchjs.org/api/moveToElement.html#apimethod-page

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aking1012 picture aking1012  路  4Comments

Zechtitus picture Zechtitus  路  4Comments

MateuszJeziorski picture MateuszJeziorski  路  3Comments

sgleonardoopitz picture sgleonardoopitz  路  3Comments

t00f picture t00f  路  3Comments