Cypress Custom Commands
Custom commands let you package repeated steps into a single reusable command. Instead of typing the same login sequence in every test file, you create one command that handles it. This topic covers how to build and use custom commands.
Where Custom Commands Live
Custom commands get defined inside the commands.js file within the support folder. Cypress automatically loads this file before running any test.
A Simple Way to Picture It
Think of a custom command as a preset button on a coffee machine. Instead of pressing five separate buttons for water, grind, brew, sugar, and milk, you press one preset button. The machine performs all five steps automatically behind that single button.
Creating a Custom Command
Cypress.Commands.add('login', (username, password) => {
cy.visit('/login')
cy.get('#username').type(username)
cy.get('#password').type(password)
cy.get('#login-button').click()
})This command bundles four separate actions into one reusable step.
Using the Custom Command
Once defined, the command becomes available inside any test file as cy.login.
describe('Dashboard', () => {
it('shows the welcome message', () => {
cy.login('john_doe', 'secret123')
cy.contains('Welcome').should('be.visible')
})
})This test logs in using a single line instead of repeating four separate commands.
Custom Command Flow Diagram
Define command once (commands.js)
|
v
cy.login('john', 'pass123') called in a test
|
v
Cypress runs the bundled steps internally
|
v
Test continues with the user already logged in
Custom Commands with Return Values
A custom command can return a value using the wrap function, allowing chaining afterward.
Cypress.Commands.add('getPrice', () => {
return cy.get('.price').invoke('text')
})cy.getPrice().then((price) => {
cy.log(price)
})Overwriting Existing Commands
Cypress allows overwriting a built-in command using Commands.overwrite, useful for adding extra logging or validation to a standard action.
Cypress.Commands.overwrite('type', (originalFn, element, text, options) => {
cy.log('Typing: ' + text)
return originalFn(element, text, options)
})Why Custom Commands Improve Test Files
Repeated code across many test files creates a maintenance burden when the underlying page changes. Updating one custom command fixes every test using it at once. This approach follows the same principle as reusing a function in regular programming.
Naming Custom Commands Clearly
A command name should describe its purpose in plain words, such as login, addToCart, or fillCheckoutForm. Vague names like doStuff make test files harder to understand for other team members.
Organizing Many Custom Commands
Large projects sometimes split commands into multiple files grouped by feature, such as commands/auth.js or commands/cart.js. The main commands.js file then imports each grouped file, keeping the codebase organized as the project grows.
import './commands/auth'
import './commands/cart'Avoiding Overly Complex Commands
A custom command should stay focused on one clear task. Packing too many unrelated steps into a single command makes it harder to reuse across different test scenarios. Splitting complex flows into smaller commands keeps each one flexible.
Key Points
- Custom commands bundle repeated steps into one reusable command.
- Commands get defined inside the commands.js file in the support folder.
- Cypress.Commands.add creates a new command available across all test files.
- Custom commands can return values for further chaining.
- Clear naming and focused logic keep custom commands easy to maintain.
