В последние пару лет TypeScript набирает популярность, и не зря. Он позволяет разработчикам создавать свои собственные типы. Это помогает совершать меньше ошибок и писать самодокументирующийся код. В этой статье мы рассмотрим основы TypeScript.
Содержание:
- Простое использование Typescript в Cypress
- Эксперименты с типами
- Использование JSDoc
- Настройка 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 предоставляет возможности автозаполнения:

Для этого всего можно придумать несколько очень интересных вариантов использования. Почему-то на ум сразу же приходят селекторы атрибутов 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».
Пингбэк: Логирование пользовательских команд в Cypress