TypeScript Tutte le cose nei tuoi test Cypress #frontend@twiliosendgrid

Pubblicato: 2020-11-14

In Twilio SendGrid, scriviamo la maggior parte delle nostre applicazioni Web frontend, in particolare nuove pagine e funzionalità, con TypeScript e React oggi per un migliore controllo del tipo, manutenibilità e documentazione della nostra base di codice. Quando abbiamo iniziato a scrivere i test Cypress più di due anni fa, la maggior parte dei nostri oggetti di pagina, helper e file delle specifiche erano ancora implementati in JavaScript e siamo stati principalmente su Cypress versione 3.xx Test più recenti erano già stati scritti in TypeScript ma noi aveva ancora un gran numero di file da convertire e migrare su TypeScript.

Volevamo sfruttare i vantaggi della digitazione completa dei nostri componenti, dei test unitari e dei test Cypress E2E . Ciò che ha semplificato il processo è stata la migrazione a una versione più recente di Cypress per sfruttare il supporto di TypeScript pronto all'uso da Cypress 4.4.0.

Se desideri fare un passo indietro e imparare più in generale come pensare alla scrittura di test E2E, sentiti libero di dare un'occhiata a questo post del blog. Se desideri anche vedere una panoramica di mille piedi delle cose più comuni che abbiamo usato o fatto in precedenza con la scrittura di test Cypress in ambienti diversi, puoi fare riferimento a questo post del blog prima di iniziare ad aggiungere TypeScript ai tuoi test Cypress. Questo post sul blog presuppone che tu abbia familiarità con i test Cypress e che tu abbia già utilizzato la loro API in precedenza.

Se sei pronto per vedere come abbiamo digitato le cose, diamo prima un'occhiata ad alcune modifiche iniziali come l'aggiunta del supporto TypeScript a Cypress se provieni da una versione precedente.

Migrazione da Cypress 3.x a >= 4.4.0

Per coloro che hanno già configurato la propria infrastruttura Cypress per funzionare con TypeScript nelle versioni Cypress da 3.xx a 4.4.3, molto probabilmente si sono verificati alcuni tentativi ed errori nell'impostazione della corretta configurazione del preprocessore Webpack nel proprio plugins/index.js . Per il team di Twilio SendGrid, c'erano alcuni problemi con alcuni file che dovevano essere file JavaScript nei plugins e nelle cartelle di support e sarebbero emersi errori Cypress difficili da eseguire il debug. Una volta che qualcosa funziona, dovrebbe assomigliare a questo.


Dopo l'aggiornamento da una versione precedente di 3.xx, siamo stati in grado di eliminare la nostra configurazione del preprocessore Webpack, sostituire il nostro file plugins/index.js con un file index.ts , armeggiare un po' con tsconfig.json della nostra cartella Cypress e, infine, usando i file TS nella nostra cartella Cypress (come some_page.spec.ts , index.d.ts o page_object.ts ) – niente più configurazione del preprocessore Webpack e ha funzionato! Siamo stati contenti di quanto fosse più pulito e piacevole non gestire la propria configurazione del preprocessore Webpack e avere una migliore copertura TypeScript sui nostri file, come mostrato di seguito.

Con il supporto di TypeScript coperto, abbiamo quindi esaminato l'applicazione del mondo reale di esempio del team Cypress, il repository cypress-real-world-app, per saperne di più su come digitare meglio le cose. Abbiamo scoperto come digitare le chiamate di cy.task(“pluginName”, { … }) e Cypress.env(“someEnvVar”) per un migliore concatenamento e tipo di supporto intellisense durante l'aggiornamento dei file. Abbiamo anche esaminato la documentazione di accompagnamento di TypeScript. Questo ci ha insegnato come digitare cose come il nostro comando personalizzato cy.login() e come impostare un file di configurazione tsconfig.json . L'applicazione di esempio ha anche un tsconfig.json a cui fare riferimento, che può fornirti un'ottima configurazione TypeScript di base da personalizzare in base alle tue preferenze. Ti consigliamo di rimanere aggiornato con le ultime versioni di Cypress, tuffarti nelle risorse ufficiali di Cypress e sperimentare con i tuoi file di definizione del tipo.

Digitazione di comandi personalizzati

Abbiamo creato alcuni comandi personalizzati globali come cy.login() per gestire l'accesso tramite l'API in modo che possa essere riutilizzato in tutti i nostri file di specifiche. Ecco un esempio di come è possibile digitare il proprio comando di accesso personalizzato date le credenziali dell'utente e restituendo un token di autenticazione.

Digitazione di variabili d'ambiente

Per gestire più ambienti di test come dev e staging, abbiamo sfruttato la configurazione di variabili di ambiente come: "testEnv" per contenere l'ambiente su cui stiamo attualmente testando come staging, "apiHost" per contenere l'host dell'API di back-end e altro variabili. Puoi digitare le tue chiamate Cypress.env() per digitare meglio funzioni e altri oggetti che si basano sull'utilizzo di quei valori di variabili di ambiente come questo.

Plugin di digitazione

Abbiamo creato molte funzioni del plug-in cy.task() per gestire le cose dalle chiamate API, il polling per i servizi e il controllo delle e-mail corrispondenti nelle caselle di posta di prova. In precedenza, durante il concatenamento di una di queste chiamate di funzione come cy.task().then((data) => {}) , l'oggetto dei dati concatenato veniva digitato come any o unknown , il che non era eccezionale per i nostri file TypeScript. Attraverso gli esempi di Cypress abbiamo scoperto come digitare meglio i plugin in base al nome del plugin e agli argomenti passati nelle chiamate di funzione. Ciò ha consentito ai nostri file TypeScript di rilevare quale sarebbe il tipo di dati concatenato.

Un sottile problema che abbiamo riscontrato è che il nome del plugin e gli argomenti devono corrispondere esattamente a come lo hai digitato. Abbiamo scoperto che è importante passare il mouse sopra il tipo concatenato .then() e l'oggetto argomento cy.task() nel tuo editor per raddoppiare controlla che i tipi corrispondano correttamente. A volte, se stavi utilizzando un soggetto concatenato da un'altra funzione Cypress come cy.getCookie(“auth_token”).its(“value”).then((token) => { }) o cy.wrap(data).then((data) => {}) , devi digitare anche quegli argomenti di dati concatenati prima di passarli come argomento della funzione cy.task(..., { token, data }) altrimenti vedrai ancora il cy.task(...).then((data) => { }) parte di dati digitata come any o unknown . È meglio che tu sia più esplicito in molti dei tipi di funzione Cypress concatenati come .its(“value”).then((token: string) => {}) o cy.wrap(data).then((data: DataType) => {}) prima di passarli come parte dell'oggetto argomenti cy.task() per essere sicuri che i tipi funzionino di nuovo.

Abbiamo creato file Typescript di plugin separati che esporterebbero le funzioni da riutilizzare nei nostri plugins/index.ts . Con l'aumentare del numero di plug-in, ti consigliamo di organizzare queste implementazioni delle funzioni dei plug-in per pagina o funzionalità per mantenere piccolo il tuo file plugins/index.ts . Dovresti rendere più facile la lettura a colpo d'occhio dove definisci tutte le tue cy.task(...) nel tuo file plugins/index.ts . Puoi finalmente digitare queste funzioni delle attività nel tuo index.d.ts nel modo seguente:

Mettendo tutto insieme in un file di dichiarazione del tipo

Abbiamo inserito tutti i nostri tipi per i nostri comandi personalizzati, variabili di ambiente e plug-in in un file index.d.ts nella cartella di support . Ti consigliamo di inserire anche tutti i tuoi tipi Cypress in un file di definizione TypeScript principale per mantenere le cose organizzate. Per aggirare i tipi mancanti nelle dipendenze esterne utilizzate nel codice di test Cypress, puoi anche definire file di modulo come "some-lib.d.ts", che includerebbe declare module 'some-lib' , per aggirare gli avvisi TypeScript della libreria. Puoi anche utilizzare la funzione dei tipi di importazione di TypeScript per importare tipi/interfacce definiti all'interno dei tuoi altri plugin/file di utilità per evitare di duplicare le definizioni dei tipi in più file. Puoi aggiungere questi tipi all'interno di uno spazio dei nomi Cypress e organizzarli nel modo seguente:

Digitazione di oggetti dispositivo di prova, oggetti pagina e file di specifiche

Quando vogliamo caricare utenti e metadati variabili per un ambiente di test, abbiamo precedentemente illustrato come combinare variabili di ambiente come "testEnv" con valori di "testing" o "staging" per estrarre il "testing" o " staging” oggetti dall'oggetto complessivo dei dispositivi di prova. Puoi digitare questi oggetti dell'ambiente del dispositivo di prova con i generici per una struttura coerente da condividere con tutte le tue specifiche. Per ogni ambiente di test, puoi avere le stesse credenziali utente e i metacampi utilizzando un tipo generico per un test per aggiungere tutte le proprietà necessarie. Vedi l'esempio qui sotto.

La digitazione degli oggetti della pagina e la digitazione dei file delle specifiche corrispondenti dipendono dai comandi, dai plug-in e dalle altre utilità di Cypress che stai utilizzando. Per la maggior parte, la digitazione degli oggetti della pagina o dei file delle specifiche non richiede molte modifiche dalle loro controparti JavaScript (supponendo che tu abbia già digitato i plug-in e le chiamate delle variabili di ambiente). Occasionalmente, una funzione di supporto dell'oggetto pagina che hai definito potrebbe richiedere la digitazione di alcuni argomenti o potrebbe essere necessario digitare la risposta che ritorna da una chiamata cy.request() as ad esempio response.body as SomeType . Nel complesso, il tuo editor come VSCode può rilevare automaticamente i tipi concatenati delle tue cy.task() o cy.customCommand() senza che tu debba aggiungere altri tipi nei file delle specifiche per compensare eventuali avvisi di TypeScript.

Ecco un esempio di parti di un oggetto pagina con alcune funzioni di supporto e un file di specifiche che utilizza l'oggetto pagina, il comando personalizzato di accesso e le attività del plug-in.

Conclusione

L'aggiunta di TypeScript ai nostri test Cypress ci ha aiutato a evitare bug e ha migliorato la nostra esperienza di sviluppo durante la scrittura di test Cypress. Quando si utilizzano chiamate di funzione cy.task() , Cypress.env() e cy.customCommand() , è possibile ottenere un migliore controllo del tipo sugli argomenti e sugli output della funzione, nonché sfruttare il completamento del codice nel nostro IDE come VSCode.

La chiave è creare il tuo file di dichiarazione del tipo come un file index.d.ts in cui puoi sovrascrivere o estendere le interfacce "Cypress" e "Concatenabile" in base ai comandi personalizzati, alle variabili di ambiente e alle funzioni del plug-in delle attività che sei usando. Nell'oggetto della tua pagina e nei file TypeScript delle specifiche, puoi quindi utilizzare quelle funzioni Cypress e passare il mouse sopra o seguire la definizione dei tipi di input e output previsti.

Inoltre, prova a utilizzare TypeScript con Cypress poiché ha il supporto predefinito per TypeScript nelle sue versioni più recenti. Verifica se ti aiuta a documentare le funzioni in modo più chiaro ed evitare un utilizzo errato dell'API. È probabile che i tuoi test TypeScript sembreranno ancora simili ai tuoi test JavaScript Cypress, quindi puoi convertire costantemente alcuni test alla volta per confrontare e contrastare gli approcci.

Se sei interessato ad altri post relativi a ciò che abbiamo imparato dai nostri test Cypress, ecco alcuni articoli che dovresti controllare:

  • Cosa considerare quando si scrivono test E2E
  • Panoramica di 1.000 piedi sulla scrittura di test di cipresso
  • Gestire i flussi di posta elettronica nei test Cypress
  • Idee per configurare, organizzare e consolidare i test Cypress
  • Integrazione dei test Cypress con Docker, Buildkite e CICD