TypeScript Todas as coisas em seus testes Cypress #frontend@twiliosendgrid

Publicados: 2020-11-14

No Twilio SendGrid, escrevemos a maioria de nossos aplicativos web front-end, especialmente novas páginas e recursos, com TypeScript e React hoje para melhor verificação de tipos, manutenibilidade e documentação de nossa base de código. Quando começamos a escrever testes Cypress há mais de dois anos, a maioria de nossos objetos de página, auxiliares e arquivos de especificação ainda eram implementados em JavaScript e estávamos principalmente no Cypress versão 3.xx Testes mais recentes já foram escritos em TypeScript, mas ainda tinha um grande número de arquivos para converter e migrar para o TypeScript.

Queríamos colher os benefícios da digitação completa de nossos componentes, testes de unidade e testes Cypress E2E . O que facilitou o processo foi a migração para uma versão mais recente do Cypress para aproveitar o suporte pronto para uso do TypeScript desde o Cypress 4.4.0.

Se você quiser dar um passo para trás e aprender de forma mais geral como pensar em escrever testes E2E, fique à vontade para conferir esta postagem no blog. Se você também quiser ver uma visão geral de mil pés das coisas mais comuns que usamos ou fizemos antes ao escrever testes Cypress em diferentes ambientes, consulte esta postagem do blog antes de começar a adicionar o TypeScript aos seus testes Cypress. Esta postagem de blog pressupõe que você esteja familiarizado com os testes Cypress e tenha usado sua API antes.

Se você estiver pronto para ver como digitamos as coisas, vamos primeiro ver algumas mudanças iniciais, como adicionar suporte a TypeScript ao Cypress se você estiver vindo de uma versão mais antiga.

Migrando do Cypress 3.x para >= 4.4.0

Para aqueles que já configuraram sua infraestrutura Cypress para funcionar com TypeScript nas versões Cypress 3.xx a 4.4.3, você provavelmente experimentou algumas tentativas e erros ao configurar a configuração adequada do pré-processador Webpack em seus plugins/index.js . Para a equipe do Twilio SendGrid, havia algumas pegadinhas com certos arquivos que precisavam ser arquivos JavaScript nos plugins e pastas de support e erros Cypress difíceis de depurar apareceriam. Depois de conseguir algo funcionando, deve ser algo assim.


Após a atualização de uma versão anterior do 3.xx, conseguimos descartar nossa configuração de pré-processador Webpack, substituir nosso arquivo plugins/index.js por um arquivo index.ts , mexer um pouco com o tsconfig.json da nossa pasta Cypress e, finalmente, usando arquivos TS em nossa pasta Cypress (como some_page.spec.ts , index.d.ts ou page_object.ts ) – não há mais configuração de pré-processador Webpack e funcionou! Ficamos satisfeitos com o quão mais limpo e agradável era não gerenciar sua própria configuração de pré-processador Webpack e ter uma melhor cobertura do TypeScript sobre nossos arquivos, conforme mostrado abaixo.

Com o suporte a TypeScript coberto, analisamos o exemplo de aplicativo do mundo real da equipe Cypress, o repositório cypress-real-world-app, para aprender mais sobre como digitar melhor. Descobrimos como digitar cy.task(“pluginName”, { … }) e Cypress.env(“someEnvVar”) chamadas de função para melhor encadeamento e suporte ao tipo intellisense ao atualizar arquivos. Também analisamos a documentação do TypeScript que o acompanha. Isso nos ensinou como digitar coisas como nosso comando personalizado cy.login() e como configurar um arquivo de configuração tsconfig.json . O aplicativo de exemplo também tem um tsconfig.json para referência, que pode fornecer uma ótima configuração básica do TypeScript para você personalizar de acordo com suas preferências. Recomendamos que você mantenha-se atualizado com as versões mais recentes do Cypress, mergulhe nos recursos oficiais do Cypress e experimente seus arquivos de definição de tipo.

Digitando comandos personalizados

Criamos alguns comandos personalizados globais, como cy.login() , para lidar com o login por meio da API, para que possa ser reutilizado em todos os nossos arquivos de especificação. Aqui está um exemplo de como você pode digitar seu próprio comando personalizado de login com as credenciais do usuário e retornar um token de autenticação.

Digitando Variáveis ​​de Ambiente

Para lidar com vários ambientes de teste, como dev e staging, aproveitamos a configuração de variáveis ​​de ambiente como: “testEnv” para manter o ambiente em que estamos testando atualmente, como staging, “apiHost” para manter o host da API de back-end e outros variáveis. Você pode digitar suas chamadas Cypress.env() para digitar melhor as funções e outros objetos que dependem do uso desses valores de variáveis ​​de ambiente como este.

Plug-ins de digitação

Criamos muitas funções de plug-in cy.task() para lidar com coisas de chamadas de API, pesquisa de serviços e verificação de e-mails correspondentes em caixas de entrada de e-mail de teste. Anteriormente, ao encadear qualquer uma dessas chamadas de função como cy.task().then((data) => {}) , o assunto de dados encadeado seria digitado como any ou unknown , o que não era ótimo para nossos arquivos TypeScript. Descobrimos através dos exemplos do Cypress como digitar melhor os plugins com base no nome do plugin e nos argumentos passados ​​nas chamadas de função. Isso permitiu que nossos arquivos TypeScript detectassem qual seria o tipo de dados encadeado.

Um problema sutil que experimentamos foi que o nome e os argumentos do plug-in devem corresponder exatamente à forma como você os digitou. Descobrimos que é importante passar o mouse sobre o tipo .then() encadeado e o objeto de argumento .then() cy.task() em seu editor para dobrar verifique se os tipos estão combinando corretamente. Às vezes, se você estivesse usando um assunto encadeado de outra função Cypress, como cy.getCookie(“auth_token”).its(“value”).then((token) => { }) ou cy.wrap(data).then((data) => {}) , você precisa digitar esses argumentos de dados encadeados também antes de passá-los como um argumento de função cy.task(..., { token, data }) ou então você ainda verá o cy.task(...).then((data) => { }) parte de dados digitada como any ou unknown . É melhor ser mais explícito em muitos dos tipos de função Cypress encadeados, como .its(“value”).then((token: string) => {}) ou cy.wrap(data).then((data: DataType) => {}) antes de passá-los como parte do objeto de argumentos cy.task() para ter certeza de que os tipos estão funcionando novamente.

Criamos arquivos Typescript de plugin separados que exportariam funções para serem usadas de volta em nossos plugins/index.ts . À medida que o número de plugins cresce, recomendamos que você organize essas implementações de funções de plugin por página ou recurso para manter seu arquivo plugins/index.ts pequeno. Você deve facilitar a leitura rápida onde você define todas as suas cy.task(...) em seu arquivo plugins/index.ts . Você pode finalmente digitar essas funções de tarefa em seu index.d.ts da seguinte maneira:

Juntando tudo em um arquivo de declaração de tipo

Colocamos todos os nossos tipos para nossos comandos personalizados, variáveis ​​de ambiente e plugins em um arquivo index.d.ts na pasta de support . Recomendamos que você coloque todos os seus tipos Cypress também em um arquivo de definição principal do TypeScript para manter as coisas organizadas. Para ignorar tipos ausentes em dependências externas usadas em seu código de teste Cypress, você também pode definir arquivos de módulo como “some-lib.d.ts”, que incluiria declare module 'some-lib' , para contornar os avisos do TypeScript da biblioteca. Você pode até usar o recurso de tipos de importação do TypeScript para trazer tipos/interfaces definidos dentro de seus outros arquivos de plugins/utils para evitar duplicar suas definições de tipo em vários arquivos. Você pode adicionar esses tipos em um namespace Cypress e organizá-los da seguinte maneira:

Digitando objetos de fixação de teste, objetos de página e arquivos de especificação

Quando queremos carregar vários usuários e metadados para um ambiente de teste, ilustramos anteriormente como podemos combinar variáveis ​​de ambiente como “testEnv” com valores de “testing” ou “staging” para extrair o “testing” ou “ staging” do objeto geral de acessórios de teste. Você pode digitar esses objetos de ambiente de fixação de teste com genéricos para uma estrutura consistente para que todas as suas especificações sejam compartilhadas. Para cada ambiente de teste, você pode ter as mesmas credenciais de usuário e metacampos usando um tipo genérico para um teste adicionar quantas propriedades forem necessárias. Veja o exemplo abaixo.

Digitar os objetos da página e digitar os arquivos de especificação correspondentes depende dos comandos Cypress, plugins e outros utilitários que você está usando. Na maioria das vezes, digitar os objetos de página ou arquivos de especificação não requer muitas alterações de suas contrapartes JavaScript (assumindo que você já digitou os plugins e as chamadas de variáveis ​​de ambiente). Ocasionalmente, uma função auxiliar de objeto de página que você definiu pode precisar que alguns argumentos sejam digitados ou a resposta que retorna de uma chamada cy.request() pode precisar ser digitada as response.body as SomeType . No geral, seu editor como o VSCode pode detectar automaticamente os tipos encadeados de suas cy.task() ou cy.customCommand() sem que você precise adicionar mais tipos em seus arquivos de especificação para compensar quaisquer avisos do TypeScript.

Aqui está um exemplo de partes de um objeto de página com algumas funções auxiliares e um arquivo de especificação que usa o objeto de página, o comando personalizado de login e as tarefas de plug-in.

Conclusão

Adicionar o TypeScript aos nossos testes Cypress nos ajudou a evitar bugs e melhorou nossa experiência de desenvolvedor ao escrever testes Cypress. Ao usar as chamadas de função cy.task() , Cypress.env() e cy.customCommand() , podemos obter uma melhor verificação de tipo nos argumentos e saídas da função, bem como aproveitar o preenchimento de código em nosso IDE, como o VSCode.

A chave é criar seu próprio arquivo de declaração de tipo, como um arquivo index.d.ts no qual você pode substituir ou estender as interfaces “Cypress” e “Chainable” com base nos comandos personalizados, variáveis ​​de ambiente e funções de plugin de tarefas que você está usando. Em seu objeto de página e arquivos TypeScript de especificação, você pode utilizar essas funções Cypress e passar o mouse sobre ou seguir a definição dos tipos de entrada e saída esperados.

Além disso, tente usar o TypeScript com o Cypress, pois ele oferece suporte pronto para o TypeScript em suas versões mais recentes. Teste se isso ajuda a documentar as funções com mais clareza e evitar o uso incorreto da API. Seus testes TypeScript provavelmente ainda serão semelhantes aos seus testes JavaScript Cypress, para que você possa converter alguns testes de cada vez para comparar e contrastar as abordagens.

Se você estiver interessado em mais postagens relacionadas ao que aprendemos com nossos testes do Cypress, aqui estão alguns artigos que você deve conferir:

  • O que considerar ao escrever testes E2E
  • Visão geral de 1.000 pés para escrever testes de cipreste
  • Lidando com fluxos de e-mail em testes Cypress
  • Ideias para configurar, organizar e consolidar seus testes Cypress
  • Integrando testes Cypress com Docker, Buildkite e CICD