# Custom Commands and cy.session — Playwright & Cypress

Source: https://www.skillbyai.com/en/e2e-testing/cy-session

> Log in once per spec run, not once per test.

## Extending cy and caching login

`Cypress.Commands.add('login', (email, password) => { ... })`, usually in `cypress/support/commands.ts`, adds `cy.login()` to every test. In TypeScript, declare the new method on the `Cypress.Chainable` interface so the editor knows about it. Inside, wrap the login steps in `cy.session(id, setup, options)`: the first time an id is seen, Cypress runs `setup` and caches the resulting cookies, localStorage and sessionStorage; later calls with the same id restore that cache instead of logging in again. A `validate` function checks the restored session is still valid, and `cacheAcrossSpecs: true` shares it between spec files in one run. Because test isolation clears state between tests, call `cy.visit` after `cy.login()`.

## Reusable commands, cached sessions, components in isolation

Larger Cypress suites lean on custom commands, cy.session, component tests and well-organised test data.

![Three ideas: custom commands and cy.session, component testing, fixtures and test data.](assets/figures/e2e-testing/section-5-map.svg) — Figure 5.1 — Custom commands, sessions, components and fixtures.

## A typed login command with cy.session

API login, cached and validated.

```typescript
// cypress/support/commands.ts
declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>;
    }
  }
}

Cypress.Commands.add('login', (email: string, password: string) => {
  cy.session(
    ['login', email],
    () => {
      cy.request('POST', '/api/login', { email, password })
        .its('status')
        .should('eq', 200);
    },
    {
      validate() {
        cy.request('/api/me').its('status').should('eq', 200);
      },
      cacheAcrossSpecs: true,
    },
  );
});

export {};

// cypress/e2e/settings.cy.ts
beforeEach(() => {
  cy.login(Cypress.env('E2E_USER'), Cypress.env('E2E_PASSWORD'));
  cy.visit('/settings');
});
```

## Commands for intent, not for every click

Good custom commands capture repeated multi-step setup such as login or seeding data. Wrapping single clicks in commands hides what the test is doing.

**Quiz:** What does cy.session do on the second call with the same id?

- [ ] Always runs the login form again
- [x] Restores the cached cookies and storage instead of running setup again
- [ ] Deletes all cookies permanently
- [ ] Skips the test

*Answer:* Restores the cached cookies and storage instead of running setup again. Setup runs once per id; later calls restore and optionally validate.
