63

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.

1
  • There are various downsides to attempting to do conditional testing on DOM elements and also various workarounds in Cypress. All of this is explained in depth in the Cypress documentation in Conditional Testing. Since there are multiple ways to do what you are trying to do, I suggest you read through the entire document and decide what is best for your application and your testing needs. Commented Dec 13, 2017 at 14:51

11 Answers 11

42
    export function clickIfExist(element) {
        cy.get('body').then((body) => {
            if (body.find(element).length > 0) {
                cy.get(element).click();
            }
        });
    }
Sign up to request clarification or add additional context in comments.

2 Comments

Is there a way to set a timeout for finding the element?
@dewey The find method is a JQuery method, not a Cypress one, so it doesn't have any built-in timeouts. Here is a question regarding waiting until an element exists using JQuery or plain Javascript: stackoverflow.com/questions/5525071/…. I guess you could do something like this: if (body.find(element).length == 0) {cy.wait(timeout);} if (body.find(element).length > 0) {cy.get(element).click();}
12

Use element polling to check for the element 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
      .then(() => 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()
  }
})

Comments

4
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')})}

1 Comment

Cypress.$() does the same thing as doc.querySelectorAll() in fewer lines.
1

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");
}

2 Comments

Wouldn't this will throw error if the element is not found?
Fails to handle asynchronous elements.
1

  cy.get('#warn-dialog-submit')
        .should(()=>{}) //this disable the failure if the element does not exist
        .then(($el) =>{
            if($el && $el.length){
               cy.wrap($el).click()
            }
        })


1 Comment

It's not optimal, .should(()=>{}) stops the timeout from working correctly. So only useful for a static page.
0

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 => {

Comments

0

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);
        });
    })
}

6 Comments

What if it does not appear - how to use Promise.reject() in that case?
The question was how to detect if elements exists, without failing the test and without exception. So, you should not reject. If the element does not exist, clickIfElementExists resolves with a value of false resolve(false), and false is returned to the awaiter, where you can check if (!await clickIfElementExists('element'))
I clarified the example a bit @Giacomo
While that may technically work, you can get rid of the function, the promise, the async iify and just use cy.wait(5000).then(() => { const exists = Cypress.$(elementSelector)...
But don't do it - Cypress was invented to get rid of hard wait times in tests. You will set 5 seconds, then find it occasionally fails so you bump to 10s, then you have to do the same in the next test and before long you are adding several minutes to the suite run time.
|
0

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")
  }
});

1 Comment

Why would you add a promise when it's not needed?
0

In Cypress there is no built in error recovery from a failed command. A command eventually passes, or if it fails, all remaining commands are not executed, and the test as a whole fails.

You might be wondering:

How do I create conditional control flow, using if/else? So that if an element does (or doesn't) exist, I choose what to do?

Cypress does not support this type of conditional control flow because it leads to non-deterministic tests - different runs may behave differently, which makes them less consistent and useful for verifying your application's correctness. In general, there are only a handful of very specific situations where you can or should create control flow using Cypress commands.

With that said, as long as you are aware of the potential pitfalls with control flow, it is possible to do this in Cypress!

I did it this way and was able to do it with success:

 cy.get('body')
.then(($body)=>{
  if($body.find('Here, replace with CSS selector of element that you want to check for').length){
    cy.get('Here, replace with CSS selector of element that you want to click after the element exists(could be a close button or save button )').click().then(()=>{
      return 'dummy';    
    })
  }
})

Comments

0

You can use this command to wait for an element to appear on the page

Cypress.Commands.add('ifElementExists', (selector, attempt = 0) => {
cy.log(`Attempt ${attempt + 1}, wait for the appearance of the element: ${selector}`);

if (attempt >= 20) {
  cy.log(`Element not found after ${attempt} attempts`);
  return cy.wrap(null);
}

return cy.get('body').then(($body) => {
  if ($body.find(selector).length > 0) {
    cy.log(`Element found: ${selector}`);
    return cy.get(selector); 
  } else {
    cy.wait(1000, { log: false }); 
    return cy.ifElementExists(selector, attempt + 1);
  }
});

Usage:

  cy.ifElementExists.then(($el) => {

     if($el) {
        .........
        }
    
    })

1 Comment

Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?
-1

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());

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.