Useful Cypress bits, recipes, and cheat sheets for end-to-end testing.
Miscellaneous
Official documentation: docs.cypress.io
Must read: Best practices in the official Cypress documentation. There is a great number of examples and useful do’s and don’ts. Some of them will pop up below.
Always use data-*
attributes for selecting, e.g. <input data-test='password' />
. I use data-test
together with custom getBySel and functions.
Difference between Cypress commands:
-
.get()
and.find()
:get
unless specifically constrained always searches for the selector in the whole document,find
’s scope is limited by the scope of the preceding call:cy.get('#selector').find('input')
-
.parent()
and.parents()
:.parent()
travels only one level up, i.e. returns the immediate parent,.parents()
travels many levels up and can return multiple parents, depending on the selector.
Match made in heaven: first use getBySel and/or findBySel to limit the scope, then .contains(text)
to check that the text in question exists.
cy.getBySel('sign-up-form').findBySel('submit-btn').contains('Sign up')
How to’s
Emulate global key presses
How to simulate a “global” key press, for example, to test shortcuts:
cy.get('body').type('{ctrl}q')
Browser confirm modal
How to work with browser confirm modal:
// Test contents of an alert/confirm modal
cy.on('window:alert', (str) => {
expect(str).to.include('your text')
})
// Accept confirm
cy.on('window:confirm', () => true)
Environment variables and secrets
How to store secrets. Use environment variables in cypress.env.json
file in the project root:
{
"email": "...",
"password": "..."
}
To access these variables in the tests:
Cypress.env('email')
More on environment variables in the documentation.
Custom commands
Cypress is easily extensibleCustom cypress commands I always use. They must be added to /cypress/support/commands.js
file.
Commands can be either standalone (see getBySel) or chainable. To make a chainable command you need to:
-
Add options object with
prevSubject: true
after the command name. -
Accept in the callable
subject
as the first argument:(subject, selector, ...args)
. -
Wrap the subject before chaining the desired action:
cy.wrap(subject).[command()]
.
For full example see findBySel.
getBySel
To reduce boilerplate code when using get
with data-test
selectors:
Cypress.Commands.add('getBySel', (selector, ...args) => {
return cy.get(`[data-test=${selector}]`, ...args)
})
// Example
cy.getBySel('sign-up-form')
findBySel
To reduce boilerplate code when using find
with data-test
selectors:
Cypress.Commands.add(
'findBySel',
{ prevSubject: true },
(subject, selector, ...args) => {
return cy.wrap(subject).find(`[data-test=${selector}]`, ...args)
}
)
// Example
cy.findBySel('submit-btn')
// Works well in combination with getBySel
cy.getBySel('sign-up-form').findBySel('submit-btn').click()
Click outside
Often you need to click anywhere outside an element, for example to close a modal or blur an element.
Cypress.Commands.add('clickOutside', () => {
return cy.get('body').click(0, 0)
})
Global delay to fight test flakiness
Cypress end-to-end tests can be flaky. This may have nothing to do with the underlying system being tested. For example, some elements may not (dis)appear fast enough or clash due to slow hardware the tests are run on, general “heaviness” of the system, etc.
One way to overcome it is to introduce a global delay for certain Cypress actions. Some 200-400 milliseconds should be enough. This can be done in the commands.js
file (preferably at the very end, since it overwrites the commands):
// The implementation from:
// https://github.com/cypress-io/cypress/issues/249
// Skip the delay if it is not set in the environment
// or set the default value other than 0
const COMMAND_DELAY = Cypress.env('COMMAND_DELAY') || 0
if (COMMAND_DELAY > 0) {
// List of the commands to slow down
for (const command of ['click', 'contains']) {
Cypress.Commands.overwrite(command, (originalFn, ...args) => {
const origVal = originalFn(...args)
return new Promise((resolve) => {
setTimeout(() => {
resolve(origVal)
}, COMMAND_DELAY)
})
})
}
}
This will make your tests running a bit longer, but we are not in a hurry, right?