Начало работы с TypeScript в Cypress

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

Содержание:

Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ

Простое использование Typescript в Cypress

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

/// <reference types="cypress" />

it('creating a board', () => {

  cy
    .visit('/')

  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type('new board{enter}');

})

Поскольку создание новой доски – довольно частое действие в написании тестов, вынесем строки 8-14 в отдельную функцию. Эта функция будет принимать аргумент, чтобы мы могли настраивать имя доски по мере использования функции.

/// <reference types="cypress" />

const addBoard = (input) => {
  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type(`${input}{enter}`);
}

it('creating a board', () => {

  cy
    .visit('/')

  addBoard()

})

Просматривая код, можно заметить, что мы оставили функцию .addBoard() пустой. Поскольку не было введено никакого текста, этот тест будет провален. Данное приложение не позволяет создать доску с пустым именем. Давайте теперь просто поменяем имя расширения с .js на .ts и посмотрим, что произойдет в нашем текстовом редакторе.

Как видите, теперь наша функция выдает ошибку в редакторе. На приведенном скриншоте показано уведомление, которое отображается при наведении курсора на нашу подчеркнутую функцию. VS Code предоставляет следующее объяснение – наша функция ожидает, что у нее должен быть хотя бы один аргумент, но ни одного не было предоставлено.

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

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

const addBoard = (input: string) => {
  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type(`${input}{enter}`);
}

it('creating a board', () => {

  cy
    .visit('/')

  addBoard('new board')

})

Теперь, когда мы указали тип нашего аргумента, мы будем получать ошибку каждый раз, когда попытаемся передать другой тип данных, например boolean или number. Для данной функции это не так уж и много, но представьте себе функцию, которая будет вызывать конечную точку API с кучей различных элементов в полезной нагрузке, таких как массивы, объекты, строки и числа. Для всего этого мы тоже можем иметь типы.

Эксперименты с типами

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

const addBoard = (input: 'new board' | 'my board') => {
  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type(`${input}{enter}`);
}

Добавив это, можно указать, какие типы входных данных будет принимать эта функция. Что еще более приятно, VS Code предоставляет возможности автозаполнения:

возможности автозаполнения в VS Code

Для этого всего можно придумать несколько очень интересных вариантов использования. Почему-то на ум сразу же приходят селекторы атрибутов data-cy. Существует такая команда getDataCy, которая является простой оберткой для команды Cypress .get(). Она будет выбирать элемент на основе атрибута data-cy, так что нам не придется постоянно набирать весь текст [data-cy=selector].

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

const addTodo = (titles: string[]) => {

  titles.forEach(title => {

    cy
      .get('[data-cy="create-board"]')
      .click();

    cy
      .get('[data-cy=new-board-input]')
      .type(`${title}{enter}`);

  })

}

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

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

Использование JSDoc

Есть еще одна замечательная возможность TypeScript, которую можно использовать даже в чистом JavaScript. С помощью JSDoc вы можете добавлять документацию к своим функциям. В VS Code вы можете добавить документацию, набрав /** */. Это создаст специальный комментарий, который будет всплывать при наведении курсора на функцию.

Существует множество различных флагов, например @param для пояснения параметров, @example для предоставления целого примера использования данной команды или @deprecated для пометки этой команды как устаревшей. Вы также можете увидеть, как JSDoc используется с командами Cypress, где они даже показывают ссылку на документацию. Представьте себе, что вы используете их для своих пользовательских команд или объектов страницы, предоставляя им мгновенный контекст.

Настройка TypeScript в проекте

Давайте немного вернемся назад. Пока что мы просто изменили расширение файла с .js на .ts. Но прежде чем мы сможем запустить эти тесты в Cypress, необходимо сделать еще две вещи. Подведя итог, нам необходимо установить TypeScript через npm или yarn, а затем создать tsconfig.json.

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es5", "dom"],
    "types": ["cypress"]
  },
  "include": [
    "**/*.ts"
  ]
}

Просто скопировав и вставив этот файл в свой проект, вы сможете начать работу, однако существует масса возможностей по настройке этого файла. Обратите внимание, что в строке 5 мы определяем типы. Это то же самое, что добавить /// <reference types="cypress" /> в начало файла, чтобы запустить работу автозаполнения. В TypeScript этого делать не нужно, поскольку автозаполнение будет включено глобально.

Создание пользовательской команды

Теперь вытащим функцию addBoard() из нашего файла и создадим пользовательскую команду Cypress. После этого мы сможем использовать ее в нашем проекте.

Cypress.Commands.add('addBoard', (input: string) => {
  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type(`${input}{enter}`);
})

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

Это можно сделать двумя способами. Первый способ можно найти в упомянутой документации. Достаточно создать файл определений с расширением .d.ts и объявить в нем свою команду:

declare namespace Cypress {
  interface Chainable {
    addBoard(value: string): void
  }
}

Здесь есть много интересного, но не будем так сильно вдаваться в подробности. Проще говоря, мы добавляем нашу команду addBoard Cypress в интерфейс Cypress. Символ void в конце нашей функции означает, что она не будет ничего передавать. При желании она может вернуть выбранный элемент или тело ответа от вызова API – все зависит от того, что мы будем делать с функцией.

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

declare global {
  namespace Cypress {
    interface Chainable {
      addBoard: typeof addBoard;
    }
  }
}

export const addBoard = (input: string) => {
  cy
    .get('[data-cy="create-board"]')
    .click();

  cy
    .get('[data-cy=new-board-input]')
    .type(`${input}{enter}`);
}

Поскольку здесь мы не используем api Cypress.Commands.add, а экспортируем функцию, нужно добавить ее в файл support/index.ts или же прямо в тест. Обычно это выглядит примерно так:

import { addBoard } from './commands/addBoard';

Cypress.Commands.add('addBoard', addBoard);

Это достаточно распространенный подход. В нем все хранится в одном файле, что очень удобно. Хотя все зависит от личных предпочтений.

Перевод статьи «Starting with TypeScript in Cypress».

1 комментарий к “Начало работы с TypeScript в Cypress”

  1. Пингбэк: Логирование пользовательских команд в Cypress

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *