Nightwatchjs: how to check if element exists without creating an error/failure/exception
Asked Answered
F

6

23

In the page I'm testing, two buttons may be displayed: BASIC or ADVANCED.

I want to be able to tell if the ADVANCED button is showing -- and if so, click it.

If the BASIC button is showing, I want to do nothing and continue with my test.

All of the Nightwatchjs options I've experimented with generate a failure message. For example if I "waitforpresent" or "waitforvisible" and the button is not there, it generates an error/failure. I just want to know which button is present so I can make a decision in my code.

Here is what I have tried:

try { 
    browser.isVisible('#advanced-search', function(result) {console.log(result.state); }) 
} catch (myError) 
{ 
    console.log(myError); 
}

Thoughts?

Fillip answered 28/7, 2015 at 21:2 Comment(4)
Shouldn't it be a simple if/else logic on top of Nightwatchjs to determine the visibility of the element? You generally do not expect the test to look for some element that may not present(for the sake of performance at least). I would, in that case, see if the element exists or not and do otherwise(possibly try/catch) as wellMysia
I have tried that sort of construct...but as soon as issuing the Nightwatchjs "wait" or "isvisible" statement, the error is thrown.Fillip
How about some code you have tried with? Can you share?Mysia
Here is an example: try { browser.isVisible('#advanced-search', function(result) { console.log(result.state); }) } catch (myError) { console.log(myError); }Fillip
B
19

You can achieve this by using the Selenium protocol "element" and a callback function to check the result status to determine if the element was found. For example:

browser.element('css selector', '#advanced-search', function(result){
    if(result.status != -1){
        //Element exists, do something
    } else{
        //Element does not exist, do something else
    }
});
Bronchi answered 10/4, 2016 at 17:16 Comment(3)
I tried out a couple .elements commands on a simple google homepage example and Saifur's answer is actually the correct one. If an element is not present, the status code will be set to 0, but the elements array will be empty. Thus your answer is wrong :(Kristinkristina
You are correct, when you use .elements(), the status is 0 with an empty array of elements. However, my example uses .element(). It's easy to confuse the two, they only differ by s, but they behave very differently.Bronchi
I tried to add this to my above comment but I was too slow :( Please check out my Gist if you're not convinced, you can download the file to your tests directory and run it like normalBronchi
K
9

You seem to be on the right track with isVisible. From the nightwatch documentation, we see that in the callback you can check the result.value property to see whether or not the element was visible, i.e:

browser.isVisible('#advanced-search', results => {
  if (results.value) { /* is visible */ }
  else { /* is not visible */ }
});

Alternatively, you could use the approach suggested by Saifur. Call the selenium api's .elements command and then check the result array's length:

browser.elements('css selector', '#advanced-search', results => {
  if (results.value.length > 0) { /* element exists */ }
  else { /* element does not exist */ }
});

This in fact could be wrapped into a custom command:

// isPresent.js
module.exports.command = function (selector, callback) {
  return this.elements('css selector', selector, results => {
    if (results.status !== 0) { // some error occurred, handle accordingly
    }

    callback(results.value.length > 0);
  });
};

then in normal code you could call it like this:

browser.isPresent('#advanced-search', advancedSearchPresent => {
  // make decisions here
}

If you are going to be making additional api calls in the callback, it may be wise to wrap it all in a .perform call:

browser.perform((_, done) => {
  browser.isPresent('#advanced-search', advancedSearchPresent => {

    // ...do more stuff...

    done();
  });
});

As to why the .perform is necessary, this might be helpful.

Kristinkristina answered 21/7, 2016 at 21:54 Comment(0)
K
2

Nightwatch offers isPresent command which waits for an element to be present. To suppress element not found errors, specify the selector argument as an object and pass the suppressNotFoundErrors = true option. Documentation here

Your Implementation could look this

browser
  .isPresent({
      suppressNotFoundErrors: true,
      selector: '#advanced-search'
    },
    function(isPresent) {
      if (isPresent.value) {
        //click the button
      } else {
        //#basic-search button shown, do nothing
      }
    })
Kipkipling answered 22/11, 2022 at 16:6 Comment(1)
the abortOnFailure: false must also be necesaryCodd
M
1

The syntax could be little off. Not very familiar with NightWatchJS. However the concept remains same.

//I would not wait for a element that should not exist
//rather I would find the list of the element and see if the count is greater than 0
//and if so, we know the element exists
browser.findElements(webdriver.By.css('#advanced-search')).then(function(elements){
    if(elements.length> 0){
        console.log(elements.length);
    }
});

See some more example here

Mysia answered 28/7, 2015 at 21:41 Comment(0)
A
1

My team uses a single function to authenticate with a few different signin forms, and we utilize a custom command called ifElementExists to accomplish the branching logic for understanding which form we're on. We also use this on a few other pages that don't have a better method for determining current state.

import { CustomCommandShorthand } from './customCommands';
import { isFunction } from 'lodash';

exports.command = function ifElementExists(this: CustomCommandShorthand, selector: string, ifTrue: Function, ifFalse?: Function) {
    this.perform(() => {
        if (!isFunction(ifTrue)) {
            throw new Error(`The second argument must be callable. You passed a ${typeof ifTrue} instead of a function.`);
        }

        this.element('css selector', selector, function ifElementExistsCallback({ status }) {
            if (status !== -1) {
                return ifTrue();
            }

            if (isFunction(ifFalse)) {
                ifFalse();
            }
        });
    })
}
Animality answered 11/1, 2018 at 20:35 Comment(0)
A
0

You can simply use the element.findAll with the params suppressNotFoundErrors & abortOnFailure:

let count = await browser.element.findAll({
    suppressNotFoundErrors: true,
    abortOnFailure: false,
    selector: '...'
  }).count()
Ambroseambrosi answered 8/5, 2024 at 9:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.