How to check for an element that may not exist using Cypress

83.3k Views Asked by At

I am writing a Cypress test to log in to a website. There are username and password fields and a Submit button. Mostly logins are straightforward, but sometimes a warning dialog appears first that has to be dismissed.

I tried this:

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

// Check for a possible warning dialog and dismiss it
if (cy.get('.warning')) {
  cy.get('#warn-dialog-submit').click();
}

Which works fine, except that the test fails if the warning doesn't appear:

CypressError: Timed out retrying: Expected to find element: '.warning', but never found it.

Then I tried this, which fails because the warning doesn't appear fast enough, so Cypress.$ doesn't find anything:

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

// Check for a possible warning dialog and dismiss it
if (Cypress.$('.warning').length > 0) {
  cy.get('#warn-dialog-submit').click();
}

What is the correct way to check for the existence of an element? I need something like cy.get() that doesn't complain if the element can't be found.

8

There are 8 best solutions below

2
Ran Adler On
    export function clickIfExist(element) {
        cy.get('body').then((body) => {
            if (body.find(element).length > 0) {
                cy.get(element).click();
            }
        });
    }
0
Patryk Janik On

I have done it with pure js.

cy.get('body').then((jqBodyWrapper) => {

               if (jqBodyWrapper[0].querySelector('.pager-last a')) {
                   cy.get('.pager-last a').then(jqWrapper => {
                       // hardcoded due to similarities found on page

                       const splitLink = jqWrapper[0].href.split("2C");
                       AMOUNT_OF_PAGES_TO_BE_RETRIEVED = Number(splitLink[splitLink.length - 1]) + 1;
                   })
               } else {
                   AMOUNT_OF_PAGES_TO_BE_RETRIEVED = 1;
               }
           });

I'm trying to check if element exists on body

cy.get('body').then((jqBodyWrapper) => {

With a pure js querySelector

if (jqBodyWrapper[0].querySelector('.pager-last a')) {

Then I fire my cy.get

cy.get('.pager-last a').then(jqWrapper => {
1
ogulcan On

The hasClass() or for CSS selector has() is an inbuilt method in jQuery which checks whether the elements with the specified class name exists or not. You can then return a boolean to perform assertion control.

Cypress.Commands.add('isExistElement', selector => {
  cy.get('body').then(($el) => {
    if ($el.has(selector)) {
      return true
    } else {
      return false
    }
  })
});

Then, it can be made into a special cypress method with TypeScript file (index.d.ts) file and can be in the form of a chainable.

declare namespace Cypress {
    interface Chainable {
        isExistElement(cssSelector: string): Cypress.Chainable<boolean>
    }
}

As in the example below:

shouldSeeCreateTicketTab() {  
  cy.isExistElement(homePageSelector.createTicketTab).should("be.true");
}
1
mykola kozub On
export const getEl = name => cy.get(`[data-cy="${name}"]`)

export const checkIfElementPresent = (visibleEl, text) => {
   cy.document().then((doc) => {
     if(doc.querySelectorAll(`[data-cy=${visibleEl}]`).length){
      getEl(visibleEl).should('have.text', text)

      return ;
   }
getEl(visibleEl).should('not.exist')})}
0
Fody On

Use element polling and to check without failing the test.

Within a maximum wait time, either the dialog never arrives or this code dismisses it.

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

const ifElementExists = (selector, attempt = 0) => {
  if (attempt === 100) return null           // no appearance, return null
  if (Cypress.$(selector).length === 0) {
    cy.wait(100, {log:false})                // wait in small chunks
    ifElementExists(selector, ++attempt)           // try again
  }
  return cy.get(selector, {log:false})       // done, exit with the element
}

ifElementExists('.warning').then($el => {
  if ($el?.length) {
    $el.find('#warn-dialog-submit').click()
  }
})
6
Lorraine Ram-El On

Note that you might need to wait enough time until the element is added to the DOM, and should do this asynchronously:

describe(`App content suite`, () => {
    it('app logs in correctly', () => {         
        console.log('Checking if warning needs to be closed');

        const enoughTimeUntilWarningIsAddedToDom = 5000;
        cy.wait(enoughTimeUntilWarningIsAddedToDom);

        (async () => {
            const elementExists = await clickIfElementExists('.warning');

            if (elementExists) {
                console.log('element exists');
            } else {
                console.log('element does not exist');
            }
        })();
    }
}

function clickIfElementExists(elementSelector: string) {
    return new Promise(resolve => {
        cy.get('body').then((body) => {
            resolve(body.find(elementSelector).length > 0);
        });
    })
}
1
Satrio Ponco On

As all we know cypress use promise to handle the test. So we can use a promiseFail to return if there's no specific element. I use a little modification of @Fody answer here.

const ifElementExists = (selector, attempt = 0) => {
  const promiseFail = new Promise((resolve) => {
    resolve([])
  })
  if (attempt === 10) return promiseFail;
  if (Cypress.$(selector).length === 0) {
    cy.wait(100, { log: false });
    return ifElementExists(selector, ++attempt);
  }
  return cy.get(selector, { log: false }); // done, exit with the element
};

ifElementExists(".todo-list").then($el => {
  if ($el?.length) {
    cy.get(".todo-list li").should("have.length", 2);
  } else {
    cy.log("NOT FOUND 404")
  }
});
0
Master DJon On

I would suggest https://github.com/bahmutov/cypress-if which is also recommended by Cypress.

cy.get('.warning').if().then(() => cy.get('#warn-dialog-submit').click());