TypeScript Todas las cosas en sus pruebas de Cypress #frontend@twiliosendgrid

Publicado: 2020-11-14

En Twilio SendGrid, escribimos la mayoría de nuestras aplicaciones web frontend, especialmente las nuevas páginas y características, con TypeScript y React hoy para mejorar la verificación de tipos, la capacidad de mantenimiento y la documentación de nuestro código base. Cuando comenzamos a escribir pruebas de Cypress hace más de dos años, la mayoría de nuestros objetos de página, asistentes y archivos de especificaciones todavía estaban implementados en JavaScript y hemos estado mayormente en la versión 3.xx de Cypress. Las pruebas más recientes ya estaban escritas en TypeScript, pero todavía tenía una gran cantidad de archivos para convertir y migrar a TypeScript.

Queríamos aprovechar los beneficios de escribir completamente nuestros componentes, pruebas unitarias y pruebas Cypress E2E . Lo que facilitó el proceso fue migrar a una versión más nueva de Cypress para aprovechar la compatibilidad inmediata con TypeScript desde Cypress 4.4.0.

Si desea dar un paso atrás y aprender de manera más general cómo pensar en escribir pruebas E2E, no dude en consultar esta publicación de blog. Si también desea ver una descripción general de mil pies de las cosas más comunes que hemos usado o hecho antes al escribir pruebas de Cypress en diferentes entornos, puede consultar esta publicación de blog antes de comenzar a agregar TypeScript a sus pruebas de Cypress. Esta publicación de blog asume que está familiarizado con las pruebas de Cypress y ha usado su API antes.

Si está listo para ver cómo escribimos las cosas, primero veamos algunos cambios iniciales, como agregar compatibilidad con TypeScript a Cypress si viene de una versión anterior.

Migración de Cypress 3.x a >= 4.4.0

Para aquellos que ya han configurado su infraestructura de Cypress para trabajar con TypeScript en las versiones de Cypress 3.xx a 4.4.3, lo más probable es que hayan experimentado algunas pruebas y errores al establecer la configuración adecuada del preprocesador de Webpack en sus plugins/index.js . Para el equipo de Twilio SendGrid, hubo algunos problemas con ciertos archivos que debían ser archivos JavaScript en los plugins y las carpetas de support y aparecían errores de Cypress difíciles de depurar. Una vez que haga que algo funcione, debería verse así.


Después de actualizar desde una versión anterior de 3.xx, pudimos eliminar nuestra configuración de preprocesador de Webpack, reemplazar nuestro archivo plugins/index.js con un archivo index.ts , modificar un poco el tsconfig.json de nuestra carpeta Cypress y, finalmente, usando archivos TS en nuestra carpeta Cypress (como some_page.spec.ts , index.d.ts o page_object.ts ), no más configuración del preprocesador de Webpack y ¡simplemente funcionó! Nos complació lo limpio y agradable que fue no administrar su propia configuración de preprocesador Webpack y tener una mejor cobertura de TypeScript sobre nuestros archivos, como se muestra a continuación.

Con el soporte de TypeScript cubierto, luego analizamos la aplicación del mundo real de ejemplo del equipo de Cypress, el repositorio cypress-real-world-app, para obtener más información sobre cómo escribir mejor las cosas. Descubrimos cómo escribir las llamadas de cy.task(“pluginName”, { … }) y Cypress.env(“someEnvVar”) para un mejor encadenamiento y escribir compatibilidad con intellisense al actualizar archivos. También investigamos la documentación de TypeScript que lo acompaña. Esto nos enseñó cómo escribir cosas como nuestro comando personalizado cy.login() y cómo configurar un archivo de configuración tsconfig.json . La aplicación de ejemplo también tiene un tsconfig.json para que pueda consultarlo, lo que puede proporcionarle una excelente configuración básica de TypeScript para que la personalice según sus preferencias. Le recomendamos que se mantenga actualizado con las últimas versiones de Cypress, se sumerja en los recursos oficiales de Cypress y experimente con sus archivos de definición de tipos.

Escribir comandos personalizados

Creamos algunos comandos personalizados globales como cy.login() para manejar el inicio de sesión a través de la API para que pueda reutilizarse en todos nuestros archivos de especificaciones. Aquí hay un ejemplo de cómo puede escribir su propio comando personalizado de inicio de sesión con las credenciales de usuario y devolver un token de autenticación.

Variables de entorno de escritura

Para tratar con múltiples entornos de prueba, como desarrollo y ensayo, aprovechamos la configuración de variables de entorno como: "testEnv" para contener el entorno en el que estamos probando actualmente, como el ensayo, "apiHost" para contener el host de la API de back-end y otros variables Puede escribir sus llamadas Cypress.env() para escribir mejor las funciones y otros objetos que se basan en el uso de esos valores de variables de entorno como este.

Complementos de escritura

Creamos muchas funciones de complemento cy.task() para manejar cosas de llamadas API, sondeo de servicios y verificación de correos electrónicos coincidentes en bandejas de entrada de correo electrónico de prueba. Anteriormente, al encadenar cualquiera de estas llamadas a funciones como cy.task().then((data) => {}) , el asunto de los datos encadenados se escribiría como any o unknown , lo que no era bueno para nuestros archivos TypeScript. Descubrimos a través de los ejemplos de Cypress cómo escribir mejor los complementos en función del nombre del complemento y los argumentos que se pasan en las llamadas a funciones. Esto permitió que nuestros archivos TypeScript detectaran cuál sería el tipo de datos encadenados.

Un problema sutil que experimentamos fue que el nombre del .then() y los cy.task() deben coincidir exactamente con la forma en que lo escribió. verifique que los tipos coincidan correctamente. A veces, si estaba usando un asunto encadenado de otra función de Cypress como cy.getCookie(“auth_token”).its(“value”).then((token) => { }) o cy.wrap(data).then((data) => {}) , también debe escribir esos argumentos de datos encadenados antes de pasarlos como un argumento de función cy.task(..., { token, data }) o, de lo contrario, seguirá viendo el cy.task(...).then((data) => { }) parte de datos escrita como any o unknown . Es mejor que sea más explícito en muchos de los tipos de funciones encadenadas de Cypress, como .its(“value”).then((token: string) => {}) o cy.wrap(data).then((data: DataType) => {}) antes de pasarlos como parte del objeto de argumentos cy.task() para asegurarse de que los tipos vuelvan a funcionar.

Creamos archivos Typescript de complemento separados que exportarían funciones para volver a usarlas en nuestros plugins/index.ts . A medida que aumenta la cantidad de complementos, le recomendamos que organice estas implementaciones de funciones de complemento por página o característica para mantener pequeño su archivo de plugins/index.ts . Debería facilitar la lectura de un vistazo donde define todas sus cy.task(...) en su archivo plugins/index.ts . Finalmente, puede escribir estas funciones de tareas en su index.d.ts de la siguiente manera:

Poniéndolo todo junto en un archivo de declaración de tipo

Colocamos todos nuestros tipos para nuestros comandos personalizados, variables de entorno y complementos en un archivo index.d.ts en la carpeta de support . Le recomendamos que coloque todos sus tipos de Cypress también en un archivo principal de definición de TypeScript para mantener las cosas organizadas. Para omitir los tipos que faltan en las dependencias externas utilizadas en su código de prueba de Cypress, también puede definir archivos de módulo como "some-lib.d.ts", que incluiría declare module 'some-lib' , para solucionar las advertencias de TypeScript de la biblioteca. Incluso puede usar la función de tipos de importación de TypeScript para traer tipos/interfaces definidos dentro de sus otros complementos/archivos de utilidades para evitar duplicar sus definiciones de tipo en varios archivos. Puede agregar estos tipos dentro de un espacio de nombres de Cypress y organizarlos de la siguiente manera:

Escribir objetos de dispositivo de prueba, objetos de página y archivos de especificaciones

Cuando queremos cargar diferentes usuarios y metadatos para un entorno de prueba, ilustramos previamente cómo podemos combinar variables de entorno como "testEnv" con valores de "testing" o "staging" para extraer el "testing" o " staging” objetos del objeto de accesorios de prueba general. Puede escribir estos objetos de entorno de dispositivo de prueba con genéricos para obtener una estructura coherente para compartir todas sus especificaciones. Para cada entorno de prueba, puede tener las mismas credenciales de usuario y metacampos usando un tipo genérico para que una prueba agregue tantas propiedades como necesite. Vea el ejemplo a continuación.

Escribir los objetos de la página y escribir los archivos de especificaciones correspondientes depende de los comandos, complementos y otras utilidades de Cypress que esté utilizando. En su mayor parte, escribir los objetos de la página o los archivos de especificaciones no requiere muchos cambios con respecto a sus contrapartes de JavaScript (suponiendo que ya haya escrito los complementos y las llamadas a las variables de entorno). Ocasionalmente, una función auxiliar de objeto de página que definió puede necesitar que se escriban algunos argumentos o que la respuesta que regresa de una llamada cy.request() deba escribirse con, por ejemplo, response.body as response.body as SomeType . En general, su editor, como VSCode, puede detectar automáticamente los tipos encadenados de sus cy.task() o cy.customCommand() sin necesidad de agregar más tipos en sus archivos de especificaciones para compensar las advertencias de TypeScript.

Este es un ejemplo de partes de un objeto de página con algunas funciones auxiliares y un archivo de especificaciones que usa el objeto de página, el comando personalizado de inicio de sesión y tareas de complemento.

Conclusión

Agregar TypeScript a nuestras pruebas de Cypress nos ayudó a evitar errores y mejoró nuestra experiencia de desarrollador al escribir pruebas de Cypress. Cuando usamos llamadas de función cy.task() , Cypress.env() y cy.customCommand() , podemos obtener una mejor verificación de tipos en los argumentos y resultados de la función, así como aprovechar la finalización del código en nuestro IDE, como VSCode.

La clave es crear su propio archivo de declaración de tipo, como un archivo index.d.ts en el que puede anular o ampliar las interfaces "Cypress" y "Chainable" en función de los comandos personalizados, las variables de entorno y las funciones del complemento de tareas que está utilizando. utilizando. En su objeto de página y archivos TypeScript de especificación, puede utilizar esas funciones de Cypress y pasar el cursor sobre o seguir la definición de los tipos de entrada y salida esperados.

Además, intente usar TypeScript con Cypress, ya que tiene soporte listo para usar para TypeScript en sus versiones más recientes. Pruebe si le ayuda a documentar las funciones con mayor claridad y evitar el uso incorrecto de la API. Es probable que sus pruebas de TypeScript sigan siendo similares a sus pruebas de Cypress de JavaScript, por lo que puede convertir constantemente algunas pruebas a la vez para comparar y contrastar los enfoques.

Si está interesado en más publicaciones relacionadas con lo que aprendimos de nuestras pruebas de Cypress, aquí hay algunos artículos que debe consultar:

  • Qué considerar al escribir pruebas E2E
  • Descripción general de 1,000 pies de escritura de pruebas de ciprés
  • Manejo de flujos de correo electrónico en Cypress Tests
  • Ideas para configurar, organizar y consolidar sus pruebas de Cypress
  • Integración de pruebas Cypress con Docker, Buildkite y CICD