import { FreeDatas2HTML, SearchEngine } from "../src/FreeDatas2HTML"; const errors=require("../src/errors.js"); const fixtures=require("./fixtures.js"); describe("Test du moteur de recherche.", () => { let converter: FreeDatas2HTML; let mySearch: SearchEngine; let searchElement : HTMLInputElement; beforeEach( async () => { document.body.insertAdjacentHTML("afterbegin", fixtures.datasViewEltHTML); converter=new FreeDatas2HTML("CSV"); converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1.csv" }); converter.datasViewElt={ id:"datas" }; await converter.run(); mySearch=new SearchEngine(converter, { id:"mySearch" }); }); afterEach( () => { document.body.removeChild(document.getElementById("fixture")); }); describe("Test des données de configuration.", () => { it("Doit avoir créé une instance de SearchEngine", () => { expect(mySearch).toBeInstanceOf(SearchEngine); }); it("Doit générer une erreur, si initialisé sans avoir au préalable chargé des données.", async () => { converter=new FreeDatas2HTML("CSV"); expect(() => { return new SearchEngine(converter, { id:"mySearch" }); }).toThrowError(errors.filterNeedDatas); converter.parser.datas2Parse="Z (numéro atomique),Élément,Symbole,Famille,Abondance des éléments dans la croûte terrestre (μg/k)"; await converter.run(); expect(() => { return new SearchEngine(converter, { id:"mySearch" }); }).toThrowError(errors.filterNeedDatas); }); it("Si une chaîne vide est transmise pour le texte du bouton, elle doit être ignorée.", () => { mySearch.btnTxt=""; expect(mySearch.btnTxt).toEqual("Search"); mySearch.btnTxt=" "; expect(mySearch.btnTxt).toEqual("Search"); }); it("Si une chaîne de + de 30 caractères est transmise pour le texte du bouton, elle doit être ignorée.", () => { mySearch.btnTxt="Si une chaîne de + de 30 caractères est transmise pour le texte du bouton, elle doit être ignorée."; expect(mySearch.btnTxt).toEqual("Search"); }); it("Toute chaîne de caractères valide doit être acceptée comme texte pour le bouton.", () => { mySearch.btnTxt="a"; expect(mySearch.btnTxt).toEqual("a"); mySearch.btnTxt=" aaa "; expect(mySearch.btnTxt).toEqual(" aaa "); }); it("Doit générer une erreur, si au moins un des numéros des champs sur lesquels effectuer les recherches n'existe pas dans les données.", () => { expect(() => { return new SearchEngine(converter, { id:"mySearch" }, [-1,0,2]); }).toThrowError(errors.searchFieldNotFound); expect(() => { return new SearchEngine(converter, { id:"mySearch" }, [0,1,10]); }).toThrowError(errors.searchFieldNotFound); expect(() => { return new SearchEngine(converter, { id:"mySearch" }, [0,1.1,10]); }).toThrowError(errors.searchFieldNotFound); }); it("Si tous numéros des champs sur lesquels effectuer les recherches existent dans les données, ils doivent être acceptés.", () => { expect(() => { return mySearch=new SearchEngine(converter, { id:"mySearch" }, [0,2,3]); }).not.toThrowError(); expect(mySearch.fields2Search).toEqual(["Z (numéro atomique)","Symbole","Famille"]); }); it("Un tableau vide pour les champs sur lesquels effectuer les recherche doit être accepté.", () => { expect(() => { return mySearch=new SearchEngine(converter, { id:"mySearch" }, []); }).not.toThrowError(); expect(mySearch.fields2Search).toEqual(["Z (numéro atomique)","Élément","Symbole","Famille","Abondance des éléments dans la croûte terrestre (μg/k)"]); }); }); describe("Création du champ de recherche.", () => { it("Doit générer un élement et un bouton dans l'élément HTML indiqué avec les propriétés de base.", () => { mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit prendre en compte l'éventuel label fourni pour le champ de recherche.", () => { mySearch.label="Qui cherche trouve ?"; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit prendre en compte l'éventuel texte personnalisé du bouton de recherche.", () => { mySearch.btnTxt="Qui cherche trouve ?"; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit indiquer l'éventuel nombre de caractères requis pour lancer la recherche.", () => { mySearch.nbCharsForSearch=2; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit indiquer l'éventuel nombre de caractères requis pour lancer la recherche en utilisant un texte personnalisé.", () => { mySearch.nbCharsForSearch=3; mySearch.placeholder="Saisir NB caractères pour lancer votre recherche."; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit accepter un texte d'indication libre, même quand il n'y a pas de nombre de caractères requis.", () => { mySearch.placeholder="Bonne chance !"; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); it("Doit prendre en compte l'ensemble des attributs renseignés.", () => { mySearch.label="Qui cherche trouve ?"; mySearch.btnTxt="Qui cherche trouve ?"; mySearch.nbCharsForSearch=3; mySearch.placeholder="Saisir NB caractères pour lancer votre recherche."; mySearch.filter2HTML(); expect(document.getElementById("mySearch").innerHTML).toEqual(`
 
`); }); }); describe("Lancement de la recherche.", () => { let searchInput: HTMLInputElement, searchBtn: HTMLInputElement; beforeEach( async () => { mySearch.filter2HTML(); searchInput=document.getElementById("freeDatas2HTMLSearchTxt") as HTMLInputElement; searchBtn=document.getElementById("freeDatas2HTMLSearchBtn") as HTMLInputElement; }); it("Le clic sur le bouton SUBMIT doit appeler la fonction actualisant l'affichage.", () => { spyOn(converter, "refreshView"); searchBtn.click(); expect(converter.refreshView).toHaveBeenCalledTimes(1); searchInput.value="z"; searchBtn.click(); expect(converter.refreshView).toHaveBeenCalledTimes(2); }); it("Si demandé, l'actualisation est lancée à chaque saisie, y compris si le champ est vide.", () => { spyOn(converter, "refreshView"); mySearch.automaticSearch=true; searchInput.value="z"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(1); searchInput.value="zz"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(2); searchInput.value=""; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(3); }); it("Si demandé, l'actualisation est lancée à chaque saisie, mais avec un minimum de caractères défini.", () => { spyOn(converter, "refreshView"); mySearch.nbCharsForSearch=3; mySearch.automaticSearch=true; searchInput.value="z"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).not.toHaveBeenCalled(); searchInput.value="zz"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).not.toHaveBeenCalled(); searchInput.value="zzz"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(1); searchInput.value="zz"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(1); // Il est toujours possible d'annuler la recherche : searchInput.value=""; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(2); // Les espaces entourant la valeur saisie doivent être ignorés dans le décompte des caractères : searchInput.value=" zz"; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(2); searchInput.value="zz "; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(2); searchInput.value=" zz "; searchInput.dispatchEvent(new Event("input")); expect(converter.refreshView).toHaveBeenCalledTimes(2); }); it("Doit toujours retourner true si le champ de recherche est vide.", () => { mySearch.automaticSearch=true; // Le champ est vide par défaut : searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "nom" : "oui" })).toBeTrue(); // Même comportement après un retour : searchInput.value="z"; searchInput.dispatchEvent(new Event("input")); searchInput.value=""; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "nom" : "oui" })).toBeTrue(); // Y compris si il y a seulement des espaces : searchInput.value=" "; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "nom" : "oui" })).toBeTrue(); }); describe("Filtre des données", () => { beforeEach( async () => { mySearch.automaticSearch=true; }); it("Doit retourner false, si la donnée testée ne possède aucun des champs sur lesquels est lancée la recherche.", () => { searchInput.value="lithium"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "nom" : "lithium" })).toBeFalse(); }); it("Doit retourner false, si une donnée testée ne correspond pas à la valeur cherchée.", async () => { searchInput.value="Hallogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse(); }); it("Doit retourner true, si la valeur recherchée est trouvée dans la donnée recherchée, sans prendre en compte la casse, ni les espaces entourant la saisie.", () => { // Expression exacte : searchInput.value="Halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); // Expression partielle : searchInput.value="gène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); // Espace entourant la saisie ignorés : searchInput.value=" halo "; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); // Par défaut, la recherche doit être tolérante à la casse, à la présence ou non d'accent et ignorer les caractères n'étant ni des lettres, ni des chiffres searchInput.value="Halogene"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); searchInput.value="halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); searchInput.value="#Halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); }); it("Si demandé doit traiter les données avant de les comparer de manière à prendre en compte les accents, majuscules ou caractères spéciaux.", () => { // Sensible à casse : mySearch.searchMode.caseOff=false; searchInput.value="halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse(); searchInput.value="Halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "halogène" })).toBeFalse(); // Sensible aux accents : mySearch.searchMode.accentOff=false; searchInput.value="Halogene"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse(); searchInput.value="Halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogene" })).toBeFalse(); // Prise en compte des caractères spéciaux : mySearch.searchMode.specialCharsOff=false; searchInput.value="Halogène^"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse(); searchInput.value="Halogène"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Ha+logène" })).toBeFalse(); // Ignore les caractères spéciaux, sauf ceux en liste blanche : mySearch.searchMode.specialCharsOff=true; mySearch.searchMode.specialCharsWhiteList="^+"; expect(mySearch.dataIsOk({ "Famille": "Ha+logène" })).toBeFalse(); searchInput.value="Halogène^"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse(); }); it("Si demandé doit cherché l'expression en entier et non chacun des mots séparément.", async () => { // Cas normal, ok si tous les mots sont trouvés séparément : searchInput.value="gaz noble"; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "le gaz est noble" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "noble est le gaz" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "gaz", "Symbole":"noble" })).toBeTrue(); // Il faut trouvé au moins une fois l'expression entière dans un des champs : mySearch.searchMode.separatedWords=false; searchInput.dispatchEvent(new Event("input")); expect(mySearch.dataIsOk({ "Famille": "gaz noble" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "gaz noble", "Symbole":"He" })).toBeTrue(); expect(mySearch.dataIsOk({ "Famille": "le gaz est noble" })).toBeFalse(); expect(mySearch.dataIsOk({ "Famille": "gaz", "Symbole":"noble" })).toBeFalse(); }); }); }); });