Типы для кастомных команд

Cypress предоставляет мощный механизм создания кастомных команд, позволяющий расширять стандартный API тестирования и создавать собственные, повторно используемые функции. В TypeScript важно корректно описывать типы этих команд, чтобы обеспечить безопасность кода, автодополнение и ясность при разработке тестов.


Подключение TypeScript к Cypress

Для начала необходимо убедиться, что проект настроен на работу с TypeScript. Основные шаги включают:

  1. Установку Cypress и TypeScript:
npm install cypress typescript --save-dev
  1. Создание файла tsconfig.json с необходимыми настройками:
{
  "compilerOptions": {
    "target": "ES6",
    "lib": ["ES6", "DOM"],
    "types": ["cypress"]
  },
  "include": ["cypress/**/*.ts"]
}
  1. Расширение типов Cypress с помощью кастомных команд требует создания файла с типами, обычно cypress/support/index.d.ts.

Основы добавления кастомной команды

Cypress позволяет расширять объект cy через метод Cypress.Commands.add. Простейший пример:

Cypress.Commands.add('login', (username: string, password: string) => {
  cy.get('#username').type(username);
  cy.get('#password').type(password);
  cy.get('button[type="submit"]').click();
});

Чтобы TypeScript понимал, что cy.login существует, необходимо описать соответствующий тип.


Расширение интерфейса Chainable

Cypress использует интерфейс Chainable, который описывает все команды, возвращающие объект для дальнейшей цепочки (cy.get().click() и т.д.). Для кастомной команды создаётся расширение:

declare namespace Cypress {
  interface Chainable<Subject = any> {
    login(username: string, password: string): Chainable<null>;
  }
}
  • Subject – тип предыдущего объекта в цепочке. По умолчанию any.
  • Chainable<null> – команда не возвращает новый объект для цепочки, поэтому указываем null. Если команда возвращает элемент DOM, можно указать Chainable<JQuery<HTMLElement>>.

После этого TypeScript будет корректно проверять вызовы cy.login('user', 'pass').


Кастомные команды с возвратом значений

Некоторые команды могут возвращать данные для использования в цепочке:

Cypress.Commands.add('getUserData', (userId: number) => {
  return cy.request(`/api/users/${userId}`).then((response) => response.body);
});

Соответствующий тип:

declare namespace Cypress {
  interface Chainable<Subject = any> {
    getUserData(userId: number): Chainable<{ id: number; name: string; email: string }>;
  }
}

Теперь cy.getUserData(1).then(user => ...) будет типизировано с полями id, name и email.


Использование опций команд

Cypress поддерживает опции для кастомных команд, например { prevSubject: true | 'element' }. Это важно для команд, которые должны работать с предыдущим элементом цепочки:

Cypress.Commands.add(
  'highlight',
  { prevSubject: 'element' },
  (subject) => {
    subject.css('border', '2px solid red');
    return subject;
  }
);

Тип для такой команды:

declare namespace Cypress {
  interface Chainable<Subject = any> {
    highlight(): Chainable<JQuery<HTMLElement>>;
  }
}
  • Здесь prevSubject: 'element' означает, что команда вызывается на элементе, полученном из предыдущей команды (cy.get('button').highlight()).
  • Возврат Chainable<JQuery<HTMLElement>> позволяет продолжить работу с элементом.

Команды с различными типами prevSubject

  1. prevSubject: false – команда независима от предыдущего элемента.
  2. prevSubject: true – команда получает предыдущий объект и возвращает что-то.
  3. prevSubject: ['element', 'optional'] – команда может быть вызвана с элементом или без него.

Пример с необязательным объектом:

Cypress.Commands.add(
  'customAction',
  { prevSubject: ['optional', 'element'] },
  (subject, option?: string) => {
    if (subject) {
      subject.css('background', option || 'yellow');
      return subject;
    }
    cy.log('No element found');
    return null;
  }
);

declare namespace Cypress {
  interface Chainable<Subject = any> {
    customAction(option?: string): Chainable<JQuery<HTMLElement> | null>;
  }
}

Модульная организация типов

Для крупных проектов рекомендуется хранить типы кастомных команд отдельно:

cypress/
  support/
    commands.ts
    index.d.ts
  • commands.ts – реализация команд
  • index.d.ts – расширение интерфейса Chainable
  • Подключение через /// <reference types="cypress" /> в index.d.ts гарантирует автодополнение.

Советы по типизации

  • Для команд, возвращающих элементы DOM, использовать JQuery<HTMLElement>.
  • Для асинхронных операций, возвращающих данные, указывать точный интерфейс результата.
  • Не оставлять any без необходимости – это снижает пользу TypeScript.
  • Использовать prevSubject для цепочек с DOM-элементами, чтобы команды были более гибкими и безопасными.

Правильная типизация кастомных команд позволяет создавать надёжные, читаемые и легко поддерживаемые тесты, а также использовать все преимущества TypeScript: автодополнение, проверку типов и предотвращение ошибок на этапе компиляции.