Conditional Commands For Cypress

How to write conditional commands using the cypress-if plugin.

Let's say you are visiting a page and sometimes the "agree to the terms & conditions" checkbox is already checked. If the checkbox is unchecked, the Submit button is disabled. Here is a typical markup and application code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div>
<input type="checkbox" id="agreed" />
<span>I agree to the terms & conditions.</span>
</div>
<button id="submit" disabled>Submit</button>
<script>
document.getElementById('agreed').addEventListener('change', (e) => {
document.getElementById('submit').disabled = !e.target.checked
})

if (Math.random() < 0.5) {
// in 50% of the cases, the checkbox is checked
// in other cases, the checkbox is unchecked
document.getElementById('agreed').checked = true
document.getElementById('submit').disabled = false
}
</script>
</body>

You are trying to write the test code to click the Submit button. If you assume the checkbox is checked, the code is this:

1
2
3
4
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('button#submit').click()
})

Ughh, if the checkbox is clear, the test fails, because the cy.click command refuses to click the disabled button by default.

The button is disabled if the checkbox is unchecked

Ok, fine. Let's check the "agree" checkbox using the cy.click command.

1
2
3
4
5
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('#agreed').click()
cy.get('button#submit').click()
})

The click flips the already checked agreed box and fails the test

If we inspect the "CLICK" command DOM snapshot, we see the "Agreed" checkbox was already checked, and our click flipped it to unchecked state.

The click flipped the already checked input box

Conditional logic using cy.then

We need a way to click the input box IF it is unchecked. One solution is to grab the input element and check ourselves using the cy.then command.

1
2
3
4
5
6
7
8
9
10
11
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('#agreed').then(($input) => {
if ($input.is(':checked')) {
cy.log('The user already agreed')
} else {
cy.wrap($input).click()
}
})
cy.get('button#submit').click()
})

If / else logic depending on the element inside cy.then callback

Ughh, the code is less clear. It requires knowing the jQuery selectors and commands, and using the cy.wrap command to get th element back into a Cypress chain and use .click() command.

Tip: for more examples of writing conditional commands, see my Cypress examples site.

Conditional logic using cypress-if

Instead of writing .then callbacks, you could use my cypress-if plugin. This plugin lets you write if / else commands right inside a chain of commands without using cy.then and cy.wrap. If you need an introduction to the plugin, watch this video below

Let's install the plugin as a dev dependency and simplify our test

1
2
3
$ npm i -D cypress-if
# or install the plugin using Yarn
$ yarn add -D cypress-if
1
2
3
4
5
6
7
8
9
10
import 'cypress-if'
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('#agreed')
.if('not.checked')
.click() // IF path
.else()
.log('The user already agreed') // ELSE path
cy.get('button#submit').click()
})

The child commands .if() and .else() come from the cypress-if plugin. You can use any Cypress assertion as the .if(assertion) condition; by default it is element existence. If the input button is unchecked, the test takes the "IF" path and skips the "ELSE" portion of the command chain:

The input element was unchecked, the test clicked on it

In other test runs, the input element was checked already, and the test took the "ELSE" portion, skipping the .click() command:

The input element was checked already, the test logged a message but did not click

You can have multiple commands in the "IF" and "ELSE" portions, you can use cy.then, cy.within commands, etc. See the plugin's README for more examples of writing conditional command chains.

🎓 I have recorded several lessons showing the cypress-if plugin in action via hands-on exercises, check out my course Cypress Plugins. There is even a free lesson about cypres-if specifically!

Warning: not every situation requires conditional commands

In our situation the best solution is to use the cy.check command. This command does nothing if the checkbox is already checked!

1
2
3
4
5
6
7
8
9
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('#agreed')
// check the input box
// if it is not checked already
// https://on.cypress.io/check
.check()
cy.get('button#submit').click()
})

For a similar lesson on using the built-in Cypress commands instead of conditional testing, see the free lesson "Lesson d4: How to avoid conditional test logic" in my course Cypress Plugins. Deterministic tests are the best, use the conditional test commands only if you really cannot control the application in any other way.

Bonus 1: conditional type into the input field

Sometimes, the test starts typing into the input element before the element is ready to process the input events. For example, this input element clears itself at the start and sometimes loses the first letters.

The input element loses the first entered character

Using cypress-if we can clear the input element and type the word again.

1
2
3
4
5
6
7
8
9
cy.get('#name')
.type('Cypress', { delay: 20 })
.if('not.have.value', 'Cypress')
.clear()
.type('Cypress')
.else()
.log('Input has expected value')
.finally()
.should('have.value', 'Cypress')

The command repeats the cy.type command if the input element has the wrong value

What if the input element again loses part of the entered text? You could probably look at the application design, or use the cypress-recurse plugin that repeats Cypress commands until a certain condition is met.