В последние пару лет 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