TL;DR
You can extend Cypress and write a runIf command like:
Cypress.Commands.add('runIf', (condition, fn) => {
if (condition !== undefined && condition === true
&& fn!==undefined && fn!==null && typeof fn === 'function') {
cy.wait(1).then(($sel) => { (fn)($sel) })
}
});
Then use it like:
cy.runIf(!queryFailed(), () => {
cy.get('.list')
.should('be.visible')
});
Read more about this below in section "2. Writing your own Cypress command".
Detailed answer
Generally, to run tests conditionally, I see two use cases:
- Temporarily disable parts of the test (for debugging)
- Run Cypress statements conditionally
The following sections will address both topics.
Temporarily disable parts of your code (for debugging)
There is a simple way cypress allows this directly in your code:
If you want to temporarily run just one part of a test, you can use
.only(...) // will only execute this part
Note: There may be more than one .only in your code, in that case Cypress will run all of them (and skip the ones that aren't "decorated" with .only).
if you want to skip a test, use
.skip(...) // will skip this part
You can use them after describe or after it. Insert them like:
describe.skip("Test 1", () => {
// ...
})
describe.only("Test 1", () => {
// ...
})
or:
it.only("Navigate to a web page", () => { ... })
it.skip("Change data", () => { ... })
After you're done, just remove the .skip and .only and the complete tests will run again as designed. Note this is useful while debugging the tests and skip / exclude / run them while testing. It is not meant for conditional execution (i.e. let the test itself decide which parts to run).
NOTE: describe.skip still shows the titles of all the sub-descriptions and iterations. If you don't want this, you can use your own function, like:
function describe_skip(str, fn) {
// to skip a test, use describe_skip instead of describe
describe('skipped "' + str + '"', () => {});
}
Now if you use describe_skip("test suite", () => { /* your test code */}) it will only print the title like skipped "test suite", and ignore the entire function (your test code). This way, you can really disable the entire function temporatily (until you need it again).
Run statements conditionally
1. Using if statements
A different way is to use JavaScript features to control the flow of your test (if conditions, putting functions around your test parts & comment out the function calls you want to skip etc).
For example:
if (Cypress.config('viewportWidth') > 350) {
it('does not run on mobile viewports', () => {
// ...
});
}
As described here. For this purpose (i.e. conditional statements) also a plugin exists (see this answer), but is "archived", meaning no longer maintained.
You may also use this.skip(); inside an iteration (it). It will immediately exit the iteration. This can also be combined with an if statement, e.g.
it("Some test iteration", () => {
if (Cypress.config('viewportWidth') > 350) {
this.skip();
}
});
2. Writing your own Cypress command
Last, not least, you can write your own extension named runIf like so:
Cypress.Commands.add('runIf', (condition, fn) => {
if (condition !== undefined && condition === true
&& fn!==undefined && fn!==null && typeof fn === 'function') {
cy.wait(1).then(($sel) => { (fn)($sel) })
}
})
and copy it into the file command.js (which is in the support folder).
Then you can use it in your code like:
cy.runIf(a===5, () => {
cy.log("The variable a has the value 5")
})
Note that constants or variables you defined outside are easily accessible as closure, like
const user_name = "Sarah Connor"
cy.runIf(user_name!=="", () => {
cy.log(`Hello ${user_name}!`)
})
cy.runIf(user_name==="", () => {
cy.log("No user defined")
})
Regarding the case you've shown in the question, you could use it like:
cy.runIf(!queryFailed(), () => {
cy.get('.list')
.should('be.visible')
})
Notes:
If you have trouble accessing the keyword this inside such a function, consider to use the old syntax instead (i.e. function() { /* ... your code ... */ } instead of () => { /* ... your code ... */ }), because arrow functions don't have all features of a "real" function. See this comparison between arrow functions and normal functions.
Regarding this keyword, you might also want to check out .call() and .bind(). Both accept thisArg, arg1, arg2, ... as arguments allowing to specify a different object for this. Use .call() to invoke the function immediately, and .bind() to return a function that you may call later.
You can use cy.pause() to pause, step next or resume your code. Also available is cy.debug(). Between it iterations, you can easily place a pause if you declare this little function:
function it_pause() {
it("Pause", function () { cy.pause(); });
}
// Usage (inside a describe block):
// describe("Section 1", function () {
// it("Iteration 1", function () { ... });
// it_pause();
// it("Iteration 2", function () { ... });
// });
Temporarily disable entire test files: If you have configured for example a spec pattern like specPattern: "cypress/e2e/*.cy.js" in the e2e section of cypress.config.js, you can simply rename the files you want to deactivate, e.g. my_test_suite.cy.js would run, but my_test_suite.cy-disabled.js would not.