Reputation: 151
I read the caveats in the docs on Cypress conditional testing, but nevertheless need to apply it to a particular test for certain reasons.
I have a function to do it, but there are certain selectors that do not work due to lack of retry in this function.
How can I implement retry in conditional testing and avoid flaky tests?
Is it even possible, or does one thing cancel out the other?
export function elementExists(selector: string): boolean {
try {
return Cypress.$(selector).length > 0;
} catch (error) {
return false;
}
Upvotes: 7
Views: 2341
Reputation: 859
Based on @Fody answer, I have created a custom cy command that checks for an element given 3 parameters , selector , number of retries and the retry interval time. leaving it here in case someone needs something similar.
Cypress.Commands.add('waitForElementToExist', (selector, numRetries, retryInterval) => {
const interval = retryInterval || 100; // Default interval of 100ms between retries
const elementExists = (selector, attempt) => {
cy.log('Attempt', attempt, 'of', numRetries, 'to find', selector);
if (attempt >= numRetries) {
cy.log(selector, 'not found');
return cy.wrap(false, { log: false });
}
return cy.get('body').then(($body) => {
const element = $body.find(selector);
if (element.length) {
cy.log(selector, 'found');
return cy.wrap(true, { log: false });
} else {
cy.wait(interval);
elementExists(selector, attempt + 1);
}
});
};
return elementExists(selector, 0);
});
Upvotes: 0
Reputation: 10555
It's even easier now with the cypress-if package.
But retry is implemented asynchronously, so you will have to return a Chainable.
export function elementExists(selector: string): Chainable<boolean> {
return cy.get(selector)
.if('exist')
.then(true)
.else()
.then(false)
}
elementExists('span#123').then((result: boolean) =>
if (result) {
...
}
})
The above uses the full API and is very readable, but this should also work for you
export function elementExists(selector: string): Chainable<JQuery<HTMLElement>|undefined> {
return cy.get(selector).if()
}
elementExists('span#123').then((result: JQuery<HTMLElement>|undefined) =>
if(result.length) {
...
}
})
Upvotes: 5
Reputation: 31944
The "standard" way to test existence of an element is pretty simple, but it does not return true/false. It fails the test if element is not found.
cy.get(selector).should('exist')
Internally the .should()
retries the element until command timeout is finished - then fails the test.
If you make your function recursive, you can do the same but instead of failing, return true/false.
function elementExists(selector, attempt = 0) {
const interval = 100; // 100ms between tries
if (attempt * interval > Cypress.config('defaultCommandTimeout')) {
cy.log(selector, 'not found')
return cy.wrap(false, {log:false})
}
return cy.get('body', {log:false}).then(($body) => {
const element = $body.find(selector)
if (element.length) {
cy.log(selector, 'found')
return cy.wrap(true, {log:false})
} else {
cy.wait(interval, {log:false})
return elementExists(selector, ++attempt)
}
})
}
elementExists(selector).then(exists => {
if (exists) {
...
}
})
Upvotes: 8