Cypress произвел революцию в области e2e-тестирования. Он успешно устранил многие болевые точки, с которыми сталкивались разработчики при использовании решений для e2e-тестирования. В этой статье мы рассмотрим, как вывести его на новый уровень, научившись использовать Cypress в рабочем пространстве Nx, в частности, как совместно использовать общие пользовательские команды Cypress в монорепо Nx.
Nx ❤️ Cypress
Одним из главных преимуществ использования Nx является то, что он устанавливает в вашу среду разработки некоторые лучшие инструменты, снимая с вас всю тяжесть работы с конфигурациями, чтением документации и самостоятельной настройкой.
При создании нового приложения с помощью Nx вы получите
- TypeScript установлен и настроен;
- Jest для модульного тестирования;
- ESLint для линтинга;
- Prettier настроен так, что вам не придется размышлять о табуляции и пробелах;
- и, наконец, Cypress для e2e тестирования (и тестирования компонентов в ближайшее время!).
Каждое приложение (если вы активно не отказываетесь от участия) получает соответствующую настройку Cypress в виде <app-name>-e2e
(вы можете изменить это имя, если хотите).
Это делает Nx довольно привлекательным в качестве CLI для разработки по умолчанию, даже если вы не рассчитываете полностью использовать многоприкладное рабочее пространство Nx monorepo.
Предпочитаете видеоверсию?
Вот, пожалуйста! Пожалуйста.
Настройка нового рабочего пространства Nx
Если вы уже настроили рабочее пространство, можете пропустить этот раздел. Здесь нет ничего нового для вас ?. Вместо этого, если вы не совсем понимаете, что такое Nx, пожалуйста, следуйте дальше.
Давайте сгенерируем новое рабочее пространство Nx на основе React.
npx create-nx-workspace nxlovescypress --preset=react --appName=happynrwl
Это сгенерирует новую установку с приложением React happynrwl
. Для стилей и настройки Nx Cloud выбирайте все, что вам нравится. Для данной статьи это не имеет значения.
Обратите внимание, вы также можете использовать настройку рабочего пространства Nx на основе Angular. Для данной статьи это не имеет значения. Вы можете обратиться к репозиторию примера на GitHub, ссылка на которую приведена в конце этой статьи, где используется рабочее пространство Nx, содержащее приложение Angular и React.
В итоге у вас должно получиться новое рабочее пространство Nx со следующей ситуацией:
Давайте рассмотрим нашу установку.
Запуск Cypress в рабочем пространстве Nx
happynrwl-e2e
— это приложение Cypress, которое было сгенерировано для нашего React-приложения happynrwl
.
Вы можете запустить тесты Cypress e2e без головы, используя следующие команды
npx nx e2e happynrwl-e2e
Вы также можете передать --watch
, чтобы запустить его интерактивно с программой Cypress test runner, чтобы тесты выполнялись заново каждый раз, когда мы меняем исходный код.
Самое замечательное в этом то, что вам вообще не нужно ничего настраивать. Не нужно
- сначала вручную раскрутить наш сервер разработки, который будет обслуживать наше React-приложение, чтобы мы могли загрузить его в среду тестов Cypress
- настроить линтинг для нашего проекта e2e (да, написание качественного тестового кода не менее важно).
Пользовательские команды Cypress
В Cypress вы обычно взаимодействуете через глобальный объект cy
, например, вы можете написать
cy.get('[data-testid="some-link"]').click();
…чтобы захватить некоторый элемент DOM и затем взаимодействовать с ним. В данном случае щелчком по ссылке.
К счастью, Cypress является расширяемым, что позволяет вам добавлять собственные команды к объекту cy
, чтобы их можно было легко вызывать в вашей тестовой реализации.
В вашей установке Cypress откройте файл support/commands.ts
, чтобы увидеть пример объявления такой пользовательской команды:
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
}
}
//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Подробнее об этом можно прочитать в официальной документации Cypress: https://docs.cypress.io/api/cypress-api/custom-commands.
Совместное использование пользовательских команд Cypress
Рассмотрим простую пользовательскую команду Cypress с именем getEl
, которая следует лучшей практике Cypress для захвата элементов через специальное свойство data-testid
.
Вот как выглядит чистая реализация.
Cypress.Commands.add('getEl', (identifier: string) => {
return cy.get(`[data-testid=${identifier}]`);
});
В идеале мы хотим повторно использовать этот тип команды во всех наших e2e тестах. Давайте рассмотрим, как Nx может существенно помочь нам в этом начинании.
Совместное использование функциональности в рабочем пространстве Nx
Когда вы создаете новое рабочее пространство Nx, вы получаете установку, включающую папки apps
и libs
. Это различие позволяет нам иметь более модульную архитектуру, следуя методологии разделения проблем, стимулируя организацию нашего исходного кода и логики в более мелкие, более сфокусированные и очень цельные единицы.
Именно здесь также происходит повторное использование и обмен функциями в рабочем пространстве Nx. Nx автоматически создает сопоставления путей TypeScript в файле tsconfig.base.json
, чтобы они могли быть легко использованы другими приложениями или библиотеками. Таким образом, функциональность, организованная в библиотеках, может быть импортирована так же легко, как и
import { Button } from '@my-organization/ui';
Нет необходимости перестраивать библиотеку, публиковать ее или что-либо еще (хотя вы все равно можете это сделать).
В этой статье мы используем этот тип настройки, чтобы организовать наши пользовательские команды Cypress в библиотеке Nx для последующего повторного использования, не в других приложениях или библиотеках, а во всех наших тестах Cypress e2e, которые потенциально могут находиться в рабочем пространстве Nx monorepo.
Подробнее о ментальной модели приложений и библиотек можно прочитать в официальной документации Nx: https://nx.dev/structure/applications-and-libraries.
Генерация новой библиотеки для размещения пользовательских команд
Как уже говорилось, нам нужна библиотека для совместного использования функциональности в рабочем пространстве Nx. Поэтому давайте сгенерируем ее:
npx nx generate @nrwl/js:library --name=cypress-commands --directory=shared --buildable=false
Пакет
@nrwl/js
используется для создания чистых пакетов TypeScript, не зависящих от фреймворка. Подробнее: https://nx.dev/getting-started/nx-and-typescript
Давайте изучим, что мы получили. Только что созданная библиотека (как и все библиотеки на основе Nx) имеет файл index.ts
, который является «публичным API» вашей библиотеки (часто также называемый «файлом бочки»). Эта точка входа позволяет прекрасно контролировать, что должно быть открыто для других библиотек и приложений, а что должно оставаться закрытым внутри самой библиотеки.
Создание нашей пользовательской команды getEl
Cypress
В нашей библиотеке мы бы хотели, чтобы наши пользовательские команды были заданы в отдельных файлах. Поэтому давайте создадим get-el-command.ts
в нашей библиотеке:
// libs/shared/cypress-commands/src/lib/get-el-command.ts
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
getEl: (identifier: string) => Chainable<JQuery<HTMLElement>>;
}
}
Cypress.Commands.add('getEl', (identifier: string) => {
return cy.get(`[data-testid=${identifier}]`);
});
Для того чтобы экспортировать его, нам нужно также экспортировать его из файла index.ts
нашей библиотеки:
// libs/shared/cypress-commands/src/index.ts
import './lib/get-el-command';
? Минутку, мы сделали импорт, а не экспорт в index.ts
. Правильно. Это немного особый случай, поскольку команды Cypress создаются путем регистрации функции в глобальном объекте Cypress.Commands
. Таким образом, все, что нам нужно сделать, это импортировать файл, чтобы get-el-command.ts
был выполнен и, следовательно, наша функция Cypress.Commands.add('getEl',...
была вызвана и зарегистрирована.
Добавление поддержки Cypress Type в нашу библиотеку
Вы можете заметить, что TypeScript не распознает глобальный объект Cypress cy
в нашей сгенерированной библиотеке cypress-commands
.
Каждая библиотека Nx уже настроена и сконфигурирована для работы с TypeScript. Существует
Чтобы заставить типы Cypress работать, нам нужно добавить cypress
и node
в свойство types
свойства compilerOptions
в tsconfig.lib.json
:
{
"extends": "./tsconfig.json",
"compilerOptions": {
...
"types": ["cypress", "node"]
},
...
}
Потребление команд Cypress
Мы готовы использовать нашу пользовательскую команду в нашем тесте e2e, а точнее в happynrwl-e2e
. Для этого нам нужно импортировать наши пользовательские команды, чтобы убедиться, что они зарегистрированы в Cypress. Перейдите в apps/happynrwl-e2e/src/support/index.ts
и импортируйте нашу библиотеку, содержащую общие команды Cypress:
// apps/happynrwl-e2e/src/support/index.ts
...
import '@nxlovescypress/shared/cypress-commands';
Обратите внимание, как удобно мы можем использовать @nxlovescypress/...
. Это работает, потому что Nx автоматически создает сопоставление путей для каждой сгенерированной библиотеки в tsconfig.base.json
:
{
"compileOnSave": false,
"compilerOptions": {
...
"paths": {
"@nxlovescypress/shared/cypress-commands": [
"libs/shared/cypress-commands/src/index.ts"
]
}
},
"exclude": ["node_modules", "tmp"]
}
Чтобы проверить команду, давайте откроем главный компонент nx-welcome.tsx
в приложении happynrwl
и изменим область заголовка на текст с надписью Nx ❤️ Cypress
:
// apps/happynrwl/src/app/nx-welcome.tsx
<div id="welcome">
<h1 data-testid="message">
<span> Hello there, </span>
Nx ❤️ Cypress
</h1>
</div>
Далее, в соответствующем e2e тесте в apps/happynrwl-e2e/src/integration/app.spec.ts
мы изменим его на следующий:
// apps/happynrwl-e2e/src/integration/app.spec.ts
describe('happynrwl', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
cy.getEl('message').should('contain', 'Nx ❤️ Cypress');
});
});
Обратите внимание, что поддержка автозаполнения TypeScript подхвачена правильно и показывает нашу пользовательскую команду Cypress:
Наконец, запуск теста в режиме просмотра должен дать следующее, что демонстрирует успешное использование нашей команды.
npx nx e2e happynrwl-e2e --watch
Повторное использование команд Cypress в нашем рабочем пространстве Nx
Повторное использование теперь очень просто. Поскольку наша пользовательская команда Cypress теперь заключена в библиотеку в рабочем пространстве Nx, мы можем легко использовать ее в других e2e тестах на базе Cypress. Все, что нужно сделать, это импортировать ее в support/index.ts
нашей установки Cypress:
import '@nxlovescypress/shared/cypress-commands';
Я оставлю на ваше усмотрение
- сгенерировать новое приложение с соответствующим тестом e2e
- импортировать наши общие команды
- использовать их в e2e тестах Cypress.
Заключение
Эта статья должна была дать вам хорошее понимание того, как Nx помогает устанавливать и настраивать Cypress и как использовать библиотеки в рабочем пространстве Nx не только для обмена функциональностью между приложениями, но и между e2e-тестами.
Вот репозиторий GitHub, который я использовал для этой статьи: https://github.com/nrwl/nx-cypress-command-sharing.
Подробнее
? Nx Docs
?? Nx GitHub
? Nrwl Community Slack
? Канал Nrwl Youtube
? Бесплатный курс Egghead
? Нужна помощь с Angular, React, Monorepos, Lerna или Nx? Поговорите с нами ?
Также, если вам понравилось это, нажмите ❤️ и не забудьте следить за Юри и Nx в Twitter, чтобы узнать больше!
#nx