DEV Community

Cover image for 🇫🇷 Test Unitaire de Composant Riot avec Vitest (JSDOM env)
Steeve
Steeve

Posted on

🇫🇷 Test Unitaire de Composant Riot avec Vitest (JSDOM env)

Cet article traite des tests des composants Riot Input en utilisant Vitest dans un environnement JsDOM.

Une deuxième méthode existe : Vitest dans un environnement Node avec un rendu côté serveur. Lisez cet article pour en savoir plus

Avant de continuer, assurez-vous d'avoir un projet de base Riot+Vite et d'avoir créé au moins un composant. Si ce n'est pas le cas, vous pouvez lire mon article précédent sur la création d'un composant Input.

Ceci est une série d'articles sur RiotJS, pour créer des composants et apprendre les meilleures pratiques pour déployer une application en production. Je pars du principe que vous avez une compréhension de base de Riot ; cependant, n'hésitez pas à consulter la documentation Riot si nécessaire : https://riot.js.org/documentation/

Riot + Vitest + JsDOM

Comme j'utilise Vite en tant que serveur de développement pour obtenir un rendu en temps réel de mon application Riot, l'utilisation de Vitest pour les tests présente de nombreux avantages :

  • Un exécuteur de tests qui utilise la même configuration que Vite (vite.config.js).
  • Il fournit une API compatible avec Jest (l'un des exécuteurs de tests les plus utilisés).
  • Performance : il utilise des Worker threads pour exécuter autant de tests que possible en parallèle.
  • Configuration et installation faciles (presque aucune).

L'environnement par défaut dans Vitest est un environnement Node.js. Comme nous construisons une application web, nous pouvons utiliser un environnement similaire à un navigateur via JsDom, qui émule un navigateur web pour tester les composants Riot.

Ajoutez Vitest et JsDom à votre projet Riot :

npm install -D vitest jsdom
Enter fullscreen mode Exit fullscreen mode

Pour exécuter les tests, ajoutez la section suivante à votre fichier package.json:

{
  "scripts": {
    "test": "vitest"
  }
}
Enter fullscreen mode Exit fullscreen mode

Et c'est tout pour Vitest ! Si vous avez besoin d'une configuration spécifique, ajoutez la propriété test dans votre configuration Vite vite.config.js. Documentation pour configurer vitest: https://vitest.dev/config/

Créons un fichier de test pour le composant Input nommé test/c-input.test.js. Vitest détectera automatiquement et exécutera les fichiers de tests avec les motifs de nom de fichier **/*.test.js.

Comme mentionné précédemment, l'environnement par défaut pour Vitest est Node, et en ajoutant un commentaire @vitest-environment en haut du fichier, cela spécifie un autre environnement à utiliser pour tous les tests dans ce fichier :

/**
 * @vitest-environment jsdom
 */
Enter fullscreen mode Exit fullscreen mode

Voilà, le fichier est prêt pour les tests.

Premier test de l'Input

Cette section décrit la création du test pour un composant Input.
Voici le composant Input utilisé dans une application Riot, sans valeur ni props :

<c-input/>
Enter fullscreen mode Exit fullscreen mode

Le HTML généré pour le composant Riot est :

<div class="field border">
  <input type="text">
</div>
Enter fullscreen mode Exit fullscreen mode

Le test pour le composant Input :

/**
 * @vitest-environment jsdom
 */

import { assert, describe, it } from 'vitest'
import * as riot from 'riot'

import cInput from '../components/c-input.riot'

describe('Component c-input', () => {
    it('should render the input without props', () => {
        riot.register('c-input', cInput);
        const [component] = riot.mount(document.createElement('div'), {}, 'c-input')
        assert.strictEqual(component.root.querySelector('div').className.trim(), 'field border');
        assert.strictEqual(component.root.querySelector('input').type, 'text');
        assert.strictEqual(component.root.querySelector('input').value, '');
        riot.unregister('c-input');

   })
})
Enter fullscreen mode Exit fullscreen mode

Détail du Code :

  1. Le composant Input et les modules sont importés
  2. Vitest fournit des utilitaires courants pour les tests, similaires à Mocha et Jest :
    • describe() est utilisé pour définir un groupe de tests.
    • it() est utilisé pour définir un test.
    • assert() est utilisé pour valider les tests.
  3. Pour utiliser le composant, il doit être enregistré globalement avec riot.register(). Il prend deux arguments :
    • Premier : Le nom du composant.
    • Deuxième : Le wrapper du composant.
  4. Pour charger le composant dans le DOM, il doit être instancié avec riot.mount(). Il prend trois arguments :
    • Premier : Un sélecteur qui sélectionne des éléments de la page et les instancies avec un composant Riot. Dans notre cas, il crée un nouvel élément div et retourne un sélecteur.
    • Deuxième : Un objet optionnel est passé pour que le composant le consomme, comme une valeur d'entrée, une erreur, une aide, une étiquette, et plus encore.
    • Troisième : Le nom du composant que nous voulons instancier, dans notre cas, c-input.
  5. La fonction mount retourne un tableau de tous les composants instanciés sur le DOM. Comme dans notre cas, nous n'en avons qu'un, nous déstructurons le tableau pour obtenir notre objet de composant Riot.
  6. Grâce à querySelector, nous pouvons accéder à tous les attributs, balises et valeurs de l'objet du composant. Par exemple, nous pouvons récupérer l'élément div : component.root.querySelector('div'). Voici ce que vous pouvez vérifier :
    • Valeur d'input : component.root.querySelector('input').value
    • Noms de classe : component.root.querySelector('input').className
    • Type : component.root.querySelector('input').type
    • Texte de l'input : component.root.querySelector('input').textContent
  7. Vérifiez tous les résultats attendus avec l'expression assert.strictEqual(result, expected).
  8. Enfin, désenregistrez la balisec-input avec riot.unregister(). Cette méthode est nécessaire pour créer un autre test avec le même nom de balise HTML.

👉 Astuce : pour créer le test, j'ai utilisé console.log(component.root.innerHTML), ce qui donne le HTML brut du composant.

Maintenant, exécutez le test via la commande NPM :

npm run test
Enter fullscreen mode Exit fullscreen mode

Le résultat dans la console :

 ✓ tests/c-input.jsdom.test.js
   ✓ Component c-input 
     ✓ should render the input without props

 Test Files  1 passed 
      Tests  1 passed
   Start at  14:10:32
   Duration  36ms
Enter fullscreen mode Exit fullscreen mode

✅ Le test réussit ; tout est bon. Vitest écoute les changements et affichera le résultat lorsqu'un nouveau test sera créé.

Test Avancé

Nous pouvons maintenant répliquer cette méthode de test pour plusieurs "props" combinées : créons un Input mot de passe avec un type "Password", une étiquette "label", une valeur et une erreur.

Voici le composant Riot Input avec les props:

<c-input label="Passport" type="passport" value="1234" error="The password is too show, minimum 20 characters." />
Enter fullscreen mode Exit fullscreen mode

Le HTML généré pour le composant est :

<div class=" field border invalid label">
  <input type="password">
  <label>Password</label>
  <span class="error">The password is too show, minimum 20 characters.</span>
</div>
Enter fullscreen mode Exit fullscreen mode

Voici le test correspondant pour enregistrer le composant, l'instancier avec toutes les propriétés et vérifier chaque balise HTML:

it('should render multiple props: label, type, error and round', () => {
   riot.register('c-input', cInput);
   const _props =  { value: "1234", label: "Password", type: "password", error: "The password is too show, minimum 20 characters." }
   const [component] = riot.mount(document.createElement('div'), _props, 'c-input')

   const divElement = component.root.querySelector('div')
   assert.strictEqual(divElement.className.replace(/\s+/g,' ').trim(), 'field border invalid label');

   const inputElement = component.root.querySelector('input')
   assert.strictEqual(inputElement.value, _props.value);
   assert.strictEqual(inputElement.type, _props.type);

   const labelElement = component.root.querySelector('label')
   assert.strictEqual(labelElement.textContent, _props.label);

   const spanElement = component.root.querySelector('span')
   assert.strictEqual(spanElement.textContent, _props.error);
   assert.strictEqual(spanElement.className, 'error');        
   riot.unregister('c-input');
})
Enter fullscreen mode Exit fullscreen mode

Détail du Code :

  1. Je ne décrirai pas ce que j'ai mentionné dans le premier test ci-dessus ⬆️
  2. La logique du composant imprime des balises et des attributs de manière conditionnelle, et nous devons vérifier chaque élément.
  3. Au lieu d'appeler plusieurs fois querySelector('span'), le résultat est stocké dans une variable pour le réutiliser pour chaque expression assert.
  4. La vérification de className nécessite de supprimer tous les espaces blancs supplémentaires avec .replace(/\s+/g,' ').trim(). Le composant a des conditions pour ajouter une classe ; si une classe n'existe pas, il laissera un caractère d'espace.

Le test réussit ✅

Trouvez tous les tests Riot avec Vitest et JsDom dans le dépôt GitHub suivant : https://github.com/steevepay/riot-beercss/blob/main/tests/c-input.jsdom.test.js

Conclusion

Combiner Riot avec Vitest et JsDom est une bonne solution pour tester le rendu des composants Riot dans un environnement de navigateur (comme Chrome/Firefox). Cela nécessite des connaissances sur la manipulation des éléments HTML avec du JavaScript, et c'est un peu verbeux. Cette méthode permet également de tester la réactivité des composants avec des événements, des entrées, et plus encore.

J'ai couvert une autre méthode de test avec Riot-SSR dans un environnement Node pour comparer les deux solutions :

  • Pour des tests Riot rapides mais limités: utilisez un environnement de serveur Node avec Riot-SSR
  • Pour des tests Riot extensifs mais verbeux: utilisez un environnement JsDom

N'hésitez pas à commenter si vous avez des questions ou besoin d'aide sur RiotJS.

Passez une excellente journée ! Santé 🍻

Top comments (0)