Cypress Tips 😃

Niluka Sripali Monnankulama
Many Minds
Published in
5 min readMay 31, 2020

At the outset, it is noted that there are many of the following that I found when trying to manage my work.

How to check if element exists using Cypress.io

it has been questioned before: Conditional statement in cypress

cypress all steps are async so that you should make a common function in commands file or page object file

export function checkIfEleExists(ele){
return new Promise((resolve,reject)=>{
/// here if ele exists or not
cy.get('body').find( ele ).its('length').then(res=>{
if(res > 0){
//// do task that you want to perform
cy.get(ele).select('100').wait(2000);
resolve();
}else{
reject();
}
});
})
}


// here check if select[aria-label="rows per page"] exists
cy.checkIfEleExists('select[aria-label="rows per page"]')
.then(e=>{
// now do what if that element is in ,,..
})
.catch(e=>{
// if not exists...
})

Compare two values and make decision in Cypress

If we have two values on a page that I need to compare and as per the result perform some actions.

cy.get('selctor1').invoke('text').should(someValue => {
const $el = Cypress.$('selector2');

if ($el.text() ==== someValue) {
// positive
expect()....
} else {
// negative
expect()....
}
})

You can use should with a callback. This callback (and the previous invoke command) is executed as long as the timeout is reached or no assertion fails. You always can use the raw jQuery object to work with. This depends on whether you need all the checks cypress is executing during a get().

Then vs Should methods in Cypress

Try to avoid then where possible. then Is not repeatable and will introduce unexpected behavior. But also will should introduce unexpected behavior.

Example of a bad usage of then:

describe("asd", () => {
it("fails but retries", () =>{
console.log("######### first test")
cy.wrap({ fn: () => console.log(new Date())})
.invoke("fn")
.should(r => expect(r).to.eq(true));
})
it("fails but retries not", () =>{
console.log("######### next test")
cy.wrap({ fn: () => console.log(new Date())})
.invoke("fn")
.then(r => {
expect(r).to.eq(true)
});
})
})

In this example, you see the same code twice but the first block uses should while the second block uses then. The assertion must fail but in the first block, the assertion is repeated. Open the DEV Console to see many retries for the first block but no retry in the second.

This is what I mean by “unexpected” behavior. Let’s say, you wrap a object that is dynamically extended (maybe by a UI action) and you are expecting a property on this object. In the second block (then) the UI acton must be executed very fast and before thethenis executed so that theexpect` does not fail.

In the should case, you have 4 seconds (in case of `defaultCommandTimeout is not overwritten) left until the assert will finally fail.

Bad usage of should:

describe("ad", () => {
it("test", () => {
cy.visit("https://www.cypress.io/")
cy.get("*[aria-label='pricing']")
.invoke('text').should(someValue => {
cy.get("asdad", {timeout: 5000}).should("not.exist");
})
})
})

What would you expect? A green test? No, this test fails:

Why is this the case? Because get introduces an implicit assert "should exist" (see: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Default-Assertions ).

Should with callback skips the default assertion (see: https://docs.cypress.io/api/commands/should.html#Notes ).I think they skip it by toggling it by flag. This could have the effect of reversing the flag again and thus forces cypress to check if "asdad" does exist even though we use should not exist.

There is an issue for this stuff: https://github.com/cypress-io/cypress/issues/5963

I do not know why cy.log has the behavior you mentioned in your case. So either you use then if you want to use cy commands within then callback or you avoid the usage of cy commands and use should with explicit assertions (expect). Maybe after that issue is fixed, cy.log also can be used.

How do I remove items for unpredictable ’n’ times while redesigning the cypress.io list?

Before showing one possible solution, I’d like to preface with a recommendation that tests should be predictable. You should create a defined number of items every time so that you don’t have to do hacks like these.

You can also read more on conditional testing, here: https://docs.cypress.io/guides/core-concepts/conditional-testing.html#Definition

That being said, maybe you have a valid use case (some fuzz testing perhaps?), so let’s go.

What I’m doing in the following example is (1) set up a rendering/removing behavior that does what you describe happens in your app. The actual solution (2) is this: find out how many items you need to remove by querying the DOM and checking the length, and then enqueue that same number of cypress commands that query the DOM every time so that you get a fresh reference to an element.

Caveat: After each remove, I’m waiting for the element (its remove button to be precise) to not exist in DOM before continuing. If your app re-renders the rest of the items separately, after the target item is removed from DOM, you'll need to assert on something else --- such as that a different item (not the one being removed) is removed (detached) from DOM.

describe('test', () => {
it('test', () => {
// -------------------------------------------------------------
// (1) Mock rendering/removing logic, just for the purpose of this
// demonstration.
// -------------------------------------------------------------
cy.window().then( win => {
let items = ['one', 'two', 'three'];
win.remove = item => {
items = items.filter( _item => _item !== item );
setTimeout(() => {
render();
}, 100 )
};
function render () {
win.document.body.innerHTML = items.map( item => {
return `
<div class="item">
${item}
<button class="remove" onclick="remove('${item}')">Remove</button>
</div>
`;
}).join('');
}
render();
});
// -------------------------------------------------------------
// (2) The actual solution
// -------------------------------------------------------------
cy.get('.item').then( $elems => {
// using Lodash to invoke the callback N times
Cypress._.times($elems.length, () => {
cy.get('.item:first').find('.remove').click()
// ensure we wait for the element to be actually removed from DOM
// before continuing
.should('not.exist');
});
});
});
});

Hide password in the log

Cypress Doc

cy.get("#id").type( password, { log: false });

The element that does not contain value in an array in cypress

const selector = ".parent-element li";
const array = ["string1", "string2", "string3"];
cy.get(selector)
.then($els => {
return $els.filter((i, el) => {
// Cypress.$ is jQuery
const textContent = Cypress.$(el).text();
const result = array.filter(str => textContent.includes(str));
// if noone string is been found... we have found the desired element
return !result.length;
});
})
.then($el => {
// $el is your searched element

// some assertions about it
expect($el.text()).not.includes("string1");
expect($el.text()).not.includes("string2");
expect($el.text()).not.includes("string3");
});
  • specify the selector to find the children (I appended li to the selector)
  • the .not().contains you were using are assertions, not filters
  • the cy.get function returns some jquery elements
  • use Cypress.$ (that is jQuery) to filter out manually the various children

NOTE:
some information obtained from stackoverflow.com

--

--

Niluka Sripali Monnankulama
Many Minds

An IT professional with over 7+ years of experience. Member of the WSO2 Identity & Access Management Team.