Open any test file in your project and count how many times you see this:
cy.visit('/');
cy.get('[data-testid="email-input"]').type('demo@nimbus.app');
cy.get('[data-testid="password-input"]').type('secure123');
cy.get('[data-testid="login-button"]').click();
In a real project, twenty or thirty times. This causes two problems.
First, your tests are hard to read. Someone opens the file and sees four lines of login before the part that matters.
Second, and worse: when the login form changes, you edit every one of those files. If you miss one, a test fails and the reason has nothing to do with what it tests.
A developer once added a "Remember me" checkbox to our login page. It was a fifteen-minute change for him. It took me most of a day to fix the tests, because the login steps were copied into thirty files.
Cypress lets you make your own command. You write it once, in the file cypress/support/commands.js:
Cypress.Commands.add('login', (email = 'demo@nimbus.app', password = 'secure123') => {
cy.visit('/');
cy.get('[data-testid="email-input"]').type(email);
cy.get('[data-testid="password-input"]').type(password);
cy.get('[data-testid="login-button"]').click();
cy.get('[data-testid="welcome-message"]').should('be.visible');
});
Let's read it together. Cypress.Commands.add creates a new command. 'login' is the name, so you will call it with cy.login(). The two values in brackets are defaults, so you can call cy.login() with no arguments for a normal user, or pass a different email when you need another one.
Now look at the last line inside the command. That check is not decoration. Always put a check at the end of a setup command. Without it, a broken login makes twenty tests fail with a confusing message about a missing contact row. With it, the failure says "welcome message not visible", and you know immediately where to look.
Your tests now show what they are about:
beforeEach(() => cy.login());
it('shows the contact list after login', () => {
cy.get('[data-testid="contact-row"]').should('have.length', 8);
});
Two mistakes to avoid.
Do not use cy.login() in the test that checks login itself. That test must show every step, because those steps are the thing you are testing.
Do not build one giant command that does six unrelated jobs. If you cannot say what a command does in one short sentence, split it.
Remember this: repeated setup goes into a command, and every setup command ends with its own check.
Reference: Custom commands.