Test Js avec Mocha et Chai

Deux outils bien utiles pour réaliser des fichiers de tests. Mocha est framework de test écrit en Javascript et une bibliothèque d’assertion pour Node

Avec Mocha, on peut facilement décrire sa situation de test puis vérifier l’état des différents tests liés à celle-ci.
Plusieurs dispositions sont disponibles pour l’affichage des résultats des tests. Pour ma part, j’utilise le reporter « spec ».
Il est également possible de préparer l’environnement de test en utilisant before ou after par exemple, pour effectuer des actions avant les situations de test.

Chai nous permet quant à lui de vérifier et comparer la valeur de variables ou de propriétés avec la valeur attendue dans le cas d’un fonctionnement « normal ».
Différentes possibilités d’écriture, le style assert ou le style BDD décliné en deux colorations: expect et should.

Pour l’utilisation d’assert: var assert = require(‘chai’).assert;

Un petit exemple de test combinant Mocha et Chai.

var assert = require('chai').assert,
question = undefined,
answer = 42;

describe('Question of Life and the Universe', function() {
  it('answer to this undefined question should be 42', function(done) {
    assert.isUndefined(question, 'Answer is 42, but what is the question');
    assert.equal(answer, 42);
    done();
  });
});

Sur cette exemple, il est possible d’enchaîner les it dans un même describe et les describe dans un fichier de test.
Notons qu’il est également possible de faire un describe dans un describe.

Chaque test possède un temps max pour être réalisé, par défaut 200ms il me semble.
Il est possible de modifier ce temps de timeout à l’appel de la commande mocha via le paramètre dédié.
Sinon, on peut spécifier le timeout pour chaque it grâce à this.timeout(tpsEnMs); .

Autre point intéressant, la présence de done comme paramètre de function au niveau du it change la caractère synchrone ou asynchrone de votre test.
Avec done, mocha ne passe au it suivant que lorsque le it en cours à renvoyé done.
Inversement, omettre done pour un it permet donc à mocha de continuer automatiquement sur le test suivant.

Pour conclure, le duo Mocha/Chai permet d’avoir des fichiers de test avec une hiérarchie lisible. Le fonctionnement et la syntaxe viennent rapidement.
Enfin, avoir transformé une batterie de test écrit en vows en tests utilisant Mocha et Chai, m’a montré qu’utiliser le duo que je viens de présenter simplifie beaucoup les choses.

JavaScript: Enlever les doublons d’un tableau

La fonction suivante permet de se débarrasser des doublons présents dans
un tableaux avec élégance.

//cleanArray removes all duplicated elements
function cleanArray(array) {
  var i, j, len = array.length, out = [], obj = {};
  for (i = 0; i < len; i++) {
    obj[array[i]] = 0;
  }
  for (j in obj) {
    out.push(j);
  }
  return out;
}

On va créer un tableau associatif avec chaque valeur présente dans le
tableau passé en paramètre. Ainsi, lorsqu’un doublon est rencontré,
l’ajout d’un champ avec le même index n’a pas d’effet. On parcourt
ensuite ce tableau et l’on stocke les indexes dans le tableau out qui
sera retourné.

Exemple:

var nbr = [42, 101010, 7, 42, 7, 42, 101010];
var newNbr = cleanArray(nbr);
console.log(newNbr);

Qui donnera:

["42", "101010", "7"]

 

API Google Maps

J’ai eu l’occasion la semaine dernière, de jouer avec l’API Google Maps. Récupération de données en json auprès d’un serveur en lui demandant de filtrer les résultats sur la valeur d’un champ. Je présente ici, une version simplifiée, j’ai placé des données en dur dans mon fichier .js, quelques gares: Nom, Région, Latitude, Longitude, et je m’arrange pour afficher leur position avec l’API de Google.

On prépare d’abord la page Html qui va recevoir le tout. On ajoute le JS de l’API:

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=VOTRE_CLE&sensor=false">
</script>

En remplaçant VOTRE_CLE par la cle d’API générée par Google.

On place une balise <div> pour accueillir la map une fois celle-ci générée.

Initialisation de la Google Maps

var mapOptions = {
  center: new google.maps.LatLng(47.2, 2),
  zoom: 5,
  mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
  mapOptions);

Pour créer un marqueur:

var latlng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
  position: latlng,
  title: "Titre du marqueur"
});
marker.setMap(map);

Concernant la gestion des marqueurs, depuis la dernière version de Google Maps, il est nécessaire de les gérer soit même. Il faut donc garder une trace des marqueurs pour pouvoir les supprimer par la suite, si besoin. On stockera les marqueurs dans un tableau.

var markersArray = [];

On ajoute les marqueurs au tableau après le placement sur la carte.

markersArray.push(marker);

La fonction pour supprimer tous les marqueurs du tableau de la carte et nettoyer la carte est deleteOverlays().

Pour créer une OpenWindow:

var infowindow = new google.maps.InfoWindow({
  content: contentString
});

//Ajout d'un Listener sur le marqueur pour réagir en cas de clic
google.maps.event.addListener(marker, 'click', function() {
  infowindow.open(map,marker);
});

Obtenir votre clé d’API Google Maps
Aller sur https://code.google.com/apis/console et se connecter avec son compte Google.
Cliquer sur Services dans le menu à gauche.
Activer le service Google Maps API v3.
Cliquer sur API Access dans le menu à gauche.
La clé est visible dans la page API Access, section Simple API Access, celle-ci est indiqué sous le nom de Key for browser apps.

Le code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="style.css" />
        <title>Gare de France</title> 

        <script src="jquery.js"></script>
    <script src="getdata.js"></script>
    <script type="text/javascript"
        src="http://maps.googleapis.com/maps/api/js?key=VOTRE_CLE&sensor=false">
        </script>
    </head> 

 <body>
    <div id="map_canvas"></div> 

    <div id="gareTab">
        <h1>Liste de Gares Françaises</h1> 

        <table><tr>  <th>Nom de la gare</th> <th>Région</th> <th>Latitude</th>   <th>Longitude</th>  </tr></table>
    </div>
 </body>
</html>

Côté CSS, on aura:

html {
      height: 100%
}
body {
    height: 100%;
    margin: 0;
    padding: 0;
    margin-left: 2em;
    margin-top: 2em;
}
table {
    border-collapse: collapse;
}
td, th {
    border: 1px solid black;
    padding-left: 5px;
    padding-right: 5px;
}
#map_canvas {
     display: inline-block;
     position: absolute;
     margin-top: 6em;
     width: 50%;
     height: 55%;
     left: 45%
 }
#gareTab {
     position: absolute;
     width: 60%;
     margin-top: 6em;
 }

Et enfin, passons au côté JavaScript proprement dit:

//Contient tous les marqueurs présents sur notre carte
var markersArray = [];
//On retient l'InfoWindow ouverte pour pouvoir la fermer au besoin
var openedInfoWindow;
//Nos données
var data = [
            {
                "Nom de la gare": "Amiens",
                "Région": "Picardie",
                "Latitude": "49.88993025",
                "Longitude": "2.30981909"},
            {
                "Nom de la gare": "Lyon-Part-Dieu",
                "Région": "Rhône-Alpes",
                "Latitude": "45.76032897",
                "Longitude": "4.86050906"},
            {
                "Nom de la gare": "Bas-Monistrol",
                "Région": "Auvergne",
                "Latitude": "45.29796638",
                "Longitude": "4.13958253"},
            {
                "Nom de la gare": "Strasbourg",
                "Région": "Alsace",
                "Latitude": "48.58496713",
                "Longitude": "7.734584"}, 

            {   "Nom de la gare": "Quiberon",
                "Région": "Bretagne",
                "Latitude": "47.48552763",
                "Longitude": "-3.11816733"},
            {
                "Nom de la gare": "Carcassonne",
                "Région": "Languedoc-Roussillon",
                "Latitude": "43.21816508",
                "Longitude": "2.35242959"}
            ]; 

//Suppression des marqueurs Google Maps
function deleteOverlays() {
  if (markersArray) {
    for (i in markersArray) {
      markersArray[i].setMap(null);
    }
    markersArray.length = 0;
  }
} 

//Suppression du contenu du tableau de gare
function deleteTab() {
    for(var i = 0; i < $('.ligne').length; ++i) {
        $('.ligne').remove();
    }
} 

//S'exécute lorsque le document est prêt
function readyFn() { 

    //Initialisation de la Google Maps
    var mapOptions = {
        center: new google.maps.LatLng(47, 2),
        zoom: 6,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    }; 

    map = new google.maps.Map(document.getElementById("map_canvas"),
    mapOptions); 

    //Suppression de tout ce qui pourrait préexister
    deleteOverlays();
    deleteTab(); 

    var tab = [];//On construit le contenu du tableau HTML dans tab
    data.forEach(function(element, index, array) {
        //Construction d'une ligne du tableau
        tab.push('<tr><td> ' +
            element["Nom de la gare"] + ' </td><td>' +
            element["Région"] + ' </td><td>  ' +
            element["Latitude"] + '  </td><td>  ' +
            element["Longitude"] + '  </td></tr>'
        ); 

        //Construction du marqueur Google Maps
        var latlng = new google.maps.LatLng(element["Latitude"], element["Longitude"]);
        var marker = new google.maps.Marker({
            position: latlng,
            title: element["Nom de la gare"]
         });
        marker.setMap(map);//Ajout du marqueur à la carte
        markersArray.push(marker); 

        //Ajout d'une InfoWindow pour chaque marqueur
        //Ce qui s'affichera:
        var contentString = '<b>' + element["Nom de la gare"]
            + '</b><br />' + element["Région"]
            + '<br />Latitude: ' + element["Latitude"]
            + '<br />Longitude: ' + element["Longitude"]; 

        //Création des fenêtres d'info (ou InfoWindow)
        var infowindow = new google.maps.InfoWindow({
            content: contentString
        }); 

        //Ajout d'un Listener sur le marqueur pour réagir en cas de clic
        google.maps.event.addListener(marker, 'click', function() {
            if(openedInfoWindow != undefined) {//Au cas où il n'y a pas d'info window à fermer
                openedInfoWindow.close();
            }
            infowindow.open(map,marker);
            openedInfoWindow = infowindow;
        });
    }); 

    //concaténation du tableau en string et ajout dans la balise table
    $('table').append(tab.join(''));
} 

$(document).ready(readyFn);

 

Pour tout savoir sur l’API Google Maps, je vous conseille la doc de Google qui est très claire et détaillée:
https://developers.google.com/maps/documentation/javascript/tutorial

Pour le zip contenant les fichiers ci-dessus, c’est par ici:

GareOnGoogleMaps

Maj 25.03.2013: En fait, il n’est pas nécessaire de générer une clé d’API pour pouvoir utiliser la Google Maps. Il suffit simplement d’utiliser l’url suivante http://maps.googleapis.com/maps/api/js?&sensor=false correspondant à l’adresse utilisée dans cette exemple sans le paramètre key. Ajout du zip comprenant les sources.