Skip to content

Cypress Custom Command Not Working Fix

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Cypress Custom Command Not Working Fix. We cover key concepts, practical examples, and best practices.

Your custom Cypress command is undefined — calling cy.login() throws "cy.login is not a function" even though you defined it.

The Problem

// WRONG — command defined in test file
it('logs in', () => {
  Cypress.Commands.add('login', () => {
    cy.get('#email').type('user@example.com');
    cy.get('#password').type('secret123');
    cy.get('button').click();
  });

  cy.login(); // Error — command definition is queued, not executed yet
});

<a href="/testing-qa/cypress/">Cypress</a>.Commands.add() must be called before the test runs, not during it. It's a synchronous registration, not a queued command.

Step-by-Step Fix

1. Define commands in support file

// RIGHT — cypress/support/commands.js
Cypress.Commands.add('login', (email = 'user@example.com', password = 'secret123') => {
  cy.get('#email').type(email);
  cy.get('#password').type(password);
  cy.get('button[type="submit"]').click();
  cy.url().should('include', '/dashboard');
});

2. Import the support file

// RIGHT — cypress/support/e2e.js
import './commands';

// Now available in all test files:
it('logs in', () => {
  cy.login();
  cy.get('.dashboard').should('be.visible');
});

3. Create commands with options

// RIGHT — command with options object
Cypress.Commands.add('login', { prevSubject: false }, (email, password, options = {}) => {
  cy.get('#email').type(email);
  cy.get('#password').type(password);
  cy.get('button[type="submit"]').click();
});

4. Create child commands (chained)

// RIGHT — child command
Cypress.Commands.add('shouldHaveText', { prevSubject: true }, (subject, text) => {
  cy.wrap(subject).should('have.text', text);
  return subject;
});

cy.get('.message').shouldHaveText('Success').and('be.visible');

Expected output:

  ✓ logs in successfully
  ✓ uses custom assertion command

Prevention Tips

  • Define custom commands in <a href="/testing-qa/cypress/">cypress</a>/support/commands.js
  • Use { prevSubject } option for child commands
  • Add TypeScript declarations for custom commands
  • Keep commands focused on single responsibilities
  • Import commands in <a href="/testing-qa/cypress/">cypress</a>/support/e2e.js

Common Mistakes with custom command

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world CYPRESS code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Where should I define custom Cypress commands?

In <a href="/testing-qa/cypress/">cypress</a>/support/commands.js. This file is loaded before every test file, making commands available everywhere. Don't define commands inside test files.

How do I add TypeScript types for custom commands?

Declare the command type: declare namespace <a href="/testing-qa/cypress/">Cypress</a> { interface Chainable { login(email?: string, password?: string): Chainable<void> } }.

What's the difference between parent and child commands?

Parent commands start a new chain (e.g., cy.login()). Child commands operate on a previous subject (e.g., cy.get('div').shouldHaveText('hello')). Use prevSubject: true for child commands.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro