Работа с промисами

Cypress построен на принципах асинхронного выполнения команд, что делает работу с промисами одной из ключевых концепций при автоматизации тестирования на JavaScript. Несмотря на то, что Cypress скрывает большую часть асинхронности под цепочкой команд, понимание промисов необходимо для корректного написания сложных сценариев.

Асинхронная природа Cypress

Каждая команда Cypress, например cy.get(), cy.click() или cy.request(), выполняется асинхронно и возвращает объект цепочки команд, а не сам результат напрямую. Это значит, что попытка получить результат сразу после вызова метода приведет к неожиданным результатам.

Пример неправильного подхода:

const element = cy.get('#submit-button');
console.log(element); // Это не HTMLElement, а объект цепочки команд Cypress

Правильный подход — использование встроенной функции .then(), которая принимает колбэк и передает результат после завершения команды:

cy.get('#submit-button').then(($button) => {
    console.log($button.text()); // Получаем текст кнопки после её поиска в DOM
});

Взаимодействие с обычными промисами

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

function fetchData() {
    return fetch('https://api.example.com/data')
        .then(response => response.json());
}

cy.wrap(fetchData()).then((data) => {
    expect(data).to.have.property('id');
});

Метод cy.wrap() позволяет обернуть любой промис или значение и добавить его в цепочку Cypress. Это обеспечивает синхронизацию асинхронных действий и правильное управление временем ожидания.

Обработка ошибок промисов

Cypress автоматически обрабатывает ошибки в своих командах, но при работе с внешними промисами важно учитывать возможные исключения. Для этого используют .catch() или встроенную конструкцию cy.wrap() с обработкой ошибок:

cy.wrap(fetchData())
  .then((data) => {
      expect(data).to.have.property('name');
  })
  .catch((err) => {
      throw new Error('Ошибка при загрузке данных: ' + err.message);
  });

Последовательное выполнение асинхронных действий

Для выполнения нескольких асинхронных действий последовательно важно помнить, что Cypress формирует очередь команд, а не выполняет их сразу. Любые асинхронные функции внутри .then() будут ждать завершения текущей команды:

cy.get('#input')
  .type('Тест')
  .then(() => {
      return cy.get('#submit').click();
  })
  .then(() => {
      cy.get('#result').should('contain', 'Успех');
  });

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

Использование async/await с Cypress

Прямое использование async/await с командами Cypress не рекомендуется, так как команды возвращают цепочку Cypress, а не промис. Однако, при работе с внешними асинхронными функциями допустимо:

async function getData() {
    const response = await fetch('https://api.example.com/data');
    return response.json();
}

cy.wrap(getData()).then((data) => {
    expect(data).to.have.property('status', 'ok');
});

Важно сохранять разделение между Cypress-командами и внешними промисами, чтобы не нарушать внутренний механизм очереди.

Советы по работе с промисами

  • Не смешивать Cypress команды и обычные промисы напрямую без cy.wrap().
  • Использовать .then() для обработки результатов Cypress команд, а не ожидать синхронный результат.
  • Оборачивать внешние промисы через cy.wrap(), чтобы сохранить корректное поведение таймаутов и ожиданий.
  • Не использовать async/await внутри Cypress цепочек команд, чтобы не нарушить последовательность выполнения.

Практические сценарии

  1. Ожидание данных из API перед взаимодействием с DOM:
cy.request('GET', '/users/1')
  .then((response) => {
      expect(response.status).to.eq(200);
      cy.get('#username').type(response.body.name);
  });
  1. Комбинирование нескольких промисов в цепочке:
cy.wrap(fetchData())
  .then((user) => {
      return fetchUserPosts(user.id);
  })
  .then((posts) => {
      expect(posts).to.have.length.greaterThan(0);
  });
  1. Обработка ошибок при асинхронной загрузке ресурсов:
cy.wrap(fetchData())
  .then((data) => {
      if (!data.success) {
          throw new Error('Не удалось получить данные');
      }
      cy.get('#output').text(data.result);
  });

Понимание работы с промисами в Cypress является основой для создания надёжных и предсказуемых тестов, особенно при сложных сценариях, включающих взаимодействие с API, асинхронную загрузку данных и последовательное выполнение действий в DOM.