Cypress Tips 😃
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 the
thenis executed so that the
expect` 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
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