TypeScript Toutes les choses dans vos tests Cypress #frontend@twiliosendgrid
Publié: 2020-11-14Chez Twilio SendGrid, nous écrivons la plupart de nos applications Web frontales, en particulier les nouvelles pages et fonctionnalités, avec TypeScript et React aujourd'hui pour une meilleure vérification des types, une maintenabilité et une documentation de notre base de code. Lorsque nous avons commencé à écrire des tests Cypress il y a plus de deux ans, la plupart de nos objets de page, aides et fichiers de spécification étaient encore implémentés en JavaScript et nous étions principalement sur Cypress version 3.xx Des tests plus récents étaient déjà écrits en TypeScript mais nous avait encore un grand nombre de fichiers à convertir et à migrer vers TypeScript.
Nous voulions récolter les bénéfices de la saisie complète de nos composants, tests unitaires et tests Cypress E2E . Ce qui a facilité le processus , c'est la migration vers une version plus récente de Cypress pour tirer parti de la prise en charge prête à l'emploi de TypeScript depuis Cypress 4.4.0.
Si vous souhaitez prendre du recul et apprendre plus généralement comment penser à écrire des tests E2E, n'hésitez pas à consulter cet article de blog. Si vous souhaitez également voir un aperçu de mille pieds des choses les plus courantes que nous avons utilisées ou faites auparavant pour écrire des tests Cypress dans différents environnements, vous pouvez vous référer à ce billet de blog avant de commencer à ajouter TypeScript à vos tests Cypress. Cet article de blog suppose que vous connaissez les tests Cypress et que vous avez déjà utilisé leur API.
Si vous êtes prêt à voir comment nous avons tapé les choses, examinons d'abord quelques changements initiaux comme l'ajout de la prise en charge de TypeScript à Cypress si vous venez d'une version plus ancienne.
Migration de Cypress 3.x vers >= 4.4.0
Pour ceux qui ont déjà configuré leur infrastructure Cypress pour fonctionner avec TypeScript dans les versions 3.xx à 4.4.3 de Cypress, vous avez très probablement rencontré des essais et des erreurs lors de la configuration de la configuration appropriée du préprocesseur Webpack dans vos plugins/index.js
. Pour l'équipe Twilio SendGrid, il y avait quelques pièges avec certains fichiers devant être des fichiers JavaScript dans les plugins
et les dossiers de support
et des erreurs Cypress difficiles à déboguer surgiraient. Une fois que vous obtenez quelque chose qui fonctionne, cela devrait ressembler à ceci.
Après la mise à niveau à partir d'une version antérieure de 3.xx, nous avons pu supprimer notre configuration de préprocesseur Webpack, remplacer notre fichier plugins/index.js
par un fichier index.ts
, bricoler un peu avec le dossier tsconfig.json
de notre dossier Cypress, et enfin, en utilisant des fichiers TS dans notre dossier Cypress (comme some_page.spec.ts
, index.d.ts
ou page_object.ts
) - plus de configuration de préprocesseur Webpack et ça a juste fonctionné ! Nous avons été ravis de constater à quel point il était plus propre et plus agréable de ne pas gérer votre propre configuration de préprocesseur Webpack et d'avoir une meilleure couverture TypeScript sur nos fichiers, comme indiqué ci-dessous.
La prise en charge de TypeScript étant couverte, nous avons ensuite examiné l'exemple d'application du monde réel de l'équipe Cypress, le référentiel cypress-real-world-app, afin d'en savoir plus sur la façon de mieux taper les choses. Nous avons découvert comment taper les appels de fonction cy.task(“pluginName”, { … })
et Cypress.env(“someEnvVar”)
pour un meilleur chaînage et la prise en charge de type intellisense lors de la mise à jour des fichiers. Nous avons également fouillé dans la documentation TypeScript qui l'accompagne. Cela nous a appris à taper des choses comme notre commande personnalisée cy.login()
et à configurer un fichier de configuration tsconfig.json
. L'exemple d'application a également un tsconfig.json
à référencer, qui peut vous fournir une excellente configuration TypeScript de base à personnaliser selon vos préférences. Nous vous recommandons de vous tenir au courant des dernières versions de Cypress, de vous plonger dans les ressources officielles de Cypress et d'expérimenter avec vos fichiers de définition de type.
Saisie de commandes personnalisées
Nous avons créé des commandes personnalisées globales telles que cy.login()
pour gérer la connexion via l'API afin qu'elles puissent être réutilisées dans tous nos fichiers de spécifications. Voici un exemple de la façon dont vous pouvez taper votre propre commande personnalisée de connexion en fonction des informations d'identification de l'utilisateur et en renvoyant un jeton d'authentification.
Saisie des variables d'environnement
Pour gérer plusieurs environnements de test tels que le développement et la mise en scène, nous avons profité de la configuration de variables d'environnement telles que : "testEnv" pour conserver l'environnement que nous testons actuellement, comme la mise en scène, "apiHost" pour contenir l'hôte de l'API backend, et d'autres variables. Vous pouvez taper vos Cypress.env()
pour mieux taper les fonctions et autres objets qui reposent sur l'utilisation de ces valeurs de variables d'environnement comme celle-ci.
Plugins de saisie
Nous avons créé de nombreuses fonctions de plug-in cy.task()
pour gérer les appels d'API, l'interrogation des services et la vérification des e-mails correspondants dans les boîtes de réception d'e-mails de test. Auparavant, lors du chaînage de l'un de ces appels de fonction comme cy.task().then((data) => {})
, le sujet de données chaîné était typé comme any
ou unknown
, ce qui n'était pas idéal pour nos fichiers TypeScript. Nous avons découvert à travers les exemples Cypress comment mieux taper les plugins en fonction du nom du plugin et des arguments passés dans les appels de fonction. Cela a permis à nos fichiers TypeScript de détecter quel serait le type de données chaînées.
Un problème subtil que nous avons rencontré était que le nom du plugin et les arguments doivent correspondre exactement à la façon dont vous l'avez saisi. Nous avons trouvé qu'il est important de survoler le type chaîné .then()
et l'objet argument cy.task()
dans votre éditeur pour doubler vérifiez que les types correspondent correctement. Parfois, si vous utilisiez un sujet chaîné d'une autre fonction Cypress telle que cy.getCookie(“auth_token”).its(“value”).then((token) => { })
ou cy.wrap(data).then((data) => {})
, vous devez également saisir ces arguments de données chaînés avant de les transmettre en tant qu'argument de fonction cy.task(..., { token, data })
sinon vous verrez toujours le cy.task(...).then((data) => { })
partie de données typée comme any
ou unknown
. Il est préférable que vous soyez plus explicite dans de nombreux types de fonctions Cypress enchaînées telles que .its(“value”).then((token: string) => {})
ou cy.wrap(data).then((data: DataType) => {})
avant de les transmettre dans le cadre de l'objet arguments cy.task()
pour être sûr que les types fonctionnent à nouveau.
Nous avons créé des fichiers Typescript de plug-in séparés qui exporteraient des fonctions à réutiliser dans nos plugins/index.ts
. Au fur et à mesure que le nombre de plugins augmente, nous vous recommandons d'organiser ces implémentations de fonction de plugin par page ou fonctionnalité pour garder votre fichier plugins/index.ts
petit. Vous devriez faciliter la lecture en un coup d'œil où vous définissez toutes vos cy.task(...)
dans votre fichier plugins/index.ts
. Vous pouvez enfin saisir ces fonctions de tâche dans votre index.d.ts
de la manière suivante :
Rassembler le tout dans un fichier de déclaration de type
Nous avons placé tous nos types pour nos commandes personnalisées, variables d'environnement et plugins dans un fichier index.d.ts
dans le dossier de support
. Nous vous recommandons de placer également tous vos types Cypress dans un fichier de définition TypeScript principal pour que les choses restent organisées. Pour contourner les types manquants dans les dépendances externes utilisées dans votre code de test Cypress, vous pouvez également définir des fichiers de module comme "some-lib.d.ts", qui incluraient declare module 'some-lib'
, pour contourner les avertissements TypeScript de la bibliothèque. Vous pouvez même utiliser la fonctionnalité d'importation de types de TypeScript pour importer des types/interfaces définis dans vos autres fichiers de plug-ins/utilitaires afin d'éviter de dupliquer vos définitions de type dans plusieurs fichiers. Vous pouvez ajouter ces types dans un espace de noms Cypress et les organiser de la manière suivante :
Saisie d'objets de montage de test, d'objets de page et de fichiers de spécifications
Lorsque nous voulons charger différents utilisateurs et métadonnées pour un environnement de test, nous avons précédemment illustré comment nous pouvons combiner des variables d'environnement telles que "testEnv" avec des valeurs de "testing" ou "staging" afin d'extraire le "testing" ou " staging" objets de l'objet global de montages de test. Vous pouvez taper ces objets d'environnement de montage de test avec des génériques pour une structure cohérente pour toutes vos spécifications à partager. Pour chaque environnement de test, vous pouvez avoir les mêmes informations d'identification utilisateur et champs méta en utilisant un type générique pour un test afin d'ajouter autant de propriétés que nécessaire. Voir l'exemple ci-dessous.
La saisie des objets de la page et la saisie des fichiers de spécifications correspondants dépendent des commandes, des plug-ins et des autres utilitaires Cypress que vous utilisez. Pour la plupart, taper les objets de page ou les fichiers de spécification ne nécessite pas beaucoup de modifications de leurs homologues JavaScript (en supposant que vous ayez déjà tapé les plugins et les appels de variables d'environnement). Parfois, une fonction d'assistance d'objet de page que vous avez définie peut nécessiter la saisie de certains arguments ou la réponse provenant d'un appel cy.request()
peut nécessiter une saisie telle que response.body en as
response.body as SomeType
. Dans l'ensemble, votre éditeur tel que VSCode peut détecter automatiquement les types chaînés de vos cy.task()
ou cy.customCommand()
sans que vous ayez besoin d'ajouter plus de types dans vos fichiers de spécifications pour compenser les avertissements TypeScript.
Voici un exemple de parties d'un objet de page avec certaines fonctions d'assistance et un fichier de spécification qui utilise l'objet de page, la commande personnalisée de connexion et les tâches de plug-in.
Conclusion
L'ajout de TypeScript à nos tests Cypress nous a permis d'éviter les bogues et d'améliorer notre expérience de développeur lors de l'écriture de tests Cypress. Lors de l'utilisation des appels de fonction cy.task()
, Cypress.env()
et cy.customCommand()
, nous pouvons obtenir une meilleure vérification de type sur les arguments et les sorties de la fonction ainsi que tirer parti de l'achèvement du code dans notre IDE tel que VSCode.
La clé est de créer votre propre fichier de déclaration de type, tel qu'un fichier index.d.ts
dans lequel vous pouvez remplacer ou étendre les interfaces "Cypress" et "Chainable" en fonction des commandes personnalisées, des variables d'environnement et des fonctions de plug-in de tâche que vous utilisez. en utilisant. Dans votre objet de page et vos fichiers TypeScript de spécification, vous pouvez ensuite utiliser ces fonctions Cypress et survoler ou suivre la définition des types d'entrée et de sortie attendus.
De plus, essayez d'utiliser TypeScript avec Cypress car il prend en charge TypeScript dans ses versions les plus récentes. Testez si cela vous aide à documenter les fonctions plus clairement et à éviter une utilisation incorrecte de l'API. Vos tests TypeScript ressembleront probablement toujours à vos tests JavaScript Cypress, vous pouvez donc convertir régulièrement certains tests à la fois pour comparer et contraster les approches.
Si vous êtes intéressé par d'autres articles liés à ce que nous avons appris de nos tests Cypress, voici quelques articles que vous devriez consulter :
- Éléments à prendre en compte lors de la rédaction de tests E2E
- Vue d'ensemble de 1 000 pieds des tests d'écriture Cypress
- Gestion des flux de messagerie dans les tests Cypress
- Idées pour configurer, organiser et consolider vos tests Cypress
- Intégration des tests Cypress avec Docker, Buildkite et CICD