Compare commits
11 Commits
b76a0caa16
...
145383d416
Author | SHA1 | Date |
---|---|---|
Fabrice PENHOËT | 145383d416 | |
Fabrice PENHOËT | 8f236e8854 | |
Fabrice PENHOËT | ae5da88348 | |
Fabrice PENHOËT | 7d85e375c5 | |
Fabrice PENHOËT | a7ca9c2e1a | |
Fabrice PENHOËT | ce1356f10a | |
Fabrice PENHOËT | 0dd6fe833d | |
Fabrice PENHOËT | 596504d398 | |
Fabrice PENHOËT | d5c2df824c | |
Fabrice PENHOËT | 40b65d1b24 | |
Fabrice PENHOËT | 100bdf8afe |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "freedatas2html",
|
||||
"version": "1.3.0",
|
||||
"version": "1.5.0",
|
||||
"description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -49,7 +49,7 @@
|
|||
<li>Des champs (=colonnes) permettant de <b>classer les données</b> : un 1ᵉʳ clic lance un classement par ordre croissant, le 2ᵉ pour ordre décroissant et ainsi de suite. Remarquez que le champ « Abondance… » utilise une fonction spécifique, car un classement par ordre alphabétique n’aurait pas convenu ici.</li>
|
||||
<li>Un <b>compteur affichant le nombre total de résultats</b>, avec prise en compte des éventuels filtres utilisés.</li>
|
||||
<li>Des <b>options de pagination</b>.</li>
|
||||
<li>Un <b>moteur de recherche</b>, permettant de filtrer les données, cette fois sur l’ensemble des champs, contrairement aux listes.</li>
|
||||
<li>Un <b>moteur de recherche</b>, permettant de filtrer les données, cette fois sur l’ensemble des champs, contrairement aux listes. Par défaut, ce <b>moteur de recherche est insensible à la casse, aux accents et ignore les caractères spéciaux</b>. Par ailleurs, chaque mot de l’expression saisie est cherché séparément. Tout cela est paramétrable pour permette une recherche stricte.</li>
|
||||
</ul>
|
||||
|
||||
<p>Enfin, il est possible d’<b>afficher les données autrement que sous forme de tableau HTML</b>. Si vous visualisez cette page sur un écran large de moins de 800 px, les données seront simplement listées. Si vous avez un grand écran, vous pouvez simuler cet affichage en faisant « Ctrl+Maj+M », puis « F5 ». Appuyez de nouveau sur « F5 », une fois de retour sur grand écran pour revoir le tableau.</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const errors=require("./errors.js");
|
||||
import { DOMElement, Filters } from "./interfaces";
|
||||
import { DOMElement, Filters, SearchModeSettings } from "./interfaces";
|
||||
import { FreeDatas2HTML } from "./FreeDatas2HTML";
|
||||
|
||||
export class SearchEngine implements Filters
|
||||
|
@ -13,6 +13,14 @@ export class SearchEngine implements Filters
|
|||
public placeholder: string="";
|
||||
public automaticSearch: boolean=false;
|
||||
private _inputValue: string="";
|
||||
public searchMode:SearchModeSettings= // par défaut, recherche lâche, mais peut devenir stricte en passant tout à false
|
||||
{
|
||||
accentOff: true,
|
||||
caseOff: true,
|
||||
separatedWords: true, // doit-on chercher l'expression en entier ou chacun des mots ?
|
||||
specialCharsOff: true,
|
||||
specialCharsWhiteList: "",
|
||||
}
|
||||
|
||||
// Injection de la classe principale, mais uniquement si des données ont été importées
|
||||
constructor(converter: FreeDatas2HTML, elt: DOMElement, fields?: number[])
|
||||
|
@ -55,6 +63,8 @@ export class SearchEngine implements Filters
|
|||
{
|
||||
if(txt.trim() !== "" && txt.length <= 30)
|
||||
this._btnTxt=txt;
|
||||
else
|
||||
console.error(errors.searchBtnTxtFail);
|
||||
}
|
||||
|
||||
get btnTxt(): string
|
||||
|
@ -75,7 +85,7 @@ export class SearchEngine implements Filters
|
|||
// Création du champ de recherche dans le DOM.
|
||||
public filter2HTML() : void
|
||||
{
|
||||
if(this.nbCharsForSearch >0 && this.placeholder === "")
|
||||
if(this.nbCharsForSearch > 0 && this.placeholder === "")
|
||||
this.placeholder="Please enter at least NB characters."
|
||||
// Pas de minlength ou de required, car l'envoi d'une recherche vide doit permettre d'annuler le filtre.
|
||||
let html=`<form id="freeDatas2HTMLSearch">`;
|
||||
|
@ -89,43 +99,83 @@ export class SearchEngine implements Filters
|
|||
html+=`> <input type="submit" id="freeDatas2HTMLSearchBtn" value="${this._btnTxt}"></form>`;
|
||||
this. _datasViewElt.eltDOM!.innerHTML=html;// "!" car l'existence de "eltDOM" est testé par le constructeur
|
||||
|
||||
// L'affichage est actualisé quand l'éventuel nombre de caractères est atteint ou quand le champ est vide, car cela permet d'annuler ce filtre.
|
||||
// L'affichage est actualisé quand l'éventuel nombre de caractères est atteint ou quand le champ est vidé, car cela permet d'annuler ce filtre.
|
||||
const searchInput=document.getElementById("freeDatas2HTMLSearchTxt") as HTMLInputElement, mySearch=this;
|
||||
searchInput.addEventListener("input", function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
mySearch._inputValue=searchInput.value;
|
||||
let searchLength=searchInput.value.length;
|
||||
mySearch._inputValue=searchInput.value.trim();
|
||||
const searchLength=mySearch._inputValue.length;
|
||||
if(mySearch.automaticSearch && (mySearch.nbCharsForSearch === 0 || ( searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch)))
|
||||
mySearch._converter.refreshView();
|
||||
});
|
||||
|
||||
/// Afficher un message quand le nombre de caractères n'est pas atteint ?
|
||||
// Lorsque le bouton est cliqué, la recherche est lancée, quelque soit le nombre de caractères saisis.
|
||||
const searchBtn=document.getElementById("freeDatas2HTMLSearchBtn") as HTMLInputElement;
|
||||
searchBtn.addEventListener("click", function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
let searchLength=searchInput.value.length;
|
||||
if((mySearch.nbCharsForSearch === 0 || ( searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch)))
|
||||
mySearch._converter.refreshView();
|
||||
mySearch._converter.refreshView();
|
||||
});
|
||||
}
|
||||
|
||||
// Pré-traitement des chaînes de caractères à comparer, suivant le mode de recherche
|
||||
private searchPreProcessing(searchElement: string) : string
|
||||
{
|
||||
let finalString=searchElement;
|
||||
if(this.searchMode.accentOff) // caractères accentués remplacés (exemple : "é" -> "e")
|
||||
finalString=finalString.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); // cf. https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript
|
||||
if(this.searchMode.caseOff)
|
||||
finalString=finalString.toLowerCase();
|
||||
if(this.searchMode.specialCharsOff)
|
||||
{
|
||||
// Suppression de tous les caractères "spéciaux", c'est-à-dire n'étant ni une lettre, ni un chiffre, ni un espace
|
||||
// ! Doit être exécuté après "accentOff", sans quoi les caractères accentués seront supprimés avant d'être remplacés
|
||||
const validChars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "+this.searchMode.specialCharsWhiteList;
|
||||
let validString="";
|
||||
for(const letter of finalString)
|
||||
{
|
||||
if(validChars.indexOf(letter) !== -1)
|
||||
validString+=letter;
|
||||
}
|
||||
finalString=validString;
|
||||
}
|
||||
return finalString;
|
||||
}
|
||||
|
||||
public dataIsOk(data: {[index: string]:string}) : boolean
|
||||
{
|
||||
// Pas de valeur sélectionnée = pas de filtre sur ce champ
|
||||
if(this._inputValue.length === 0)
|
||||
const realSearch=this.searchPreProcessing(this._inputValue.trim());
|
||||
// Pas de valeur saisie = pas de filtre sur ce champ
|
||||
if(realSearch.length === 0)
|
||||
return true;
|
||||
// Sinon, on cherche la valeur saisie dans les champs définis :
|
||||
for(let field in data)
|
||||
|
||||
// L'expression saisie doit-elle être séparée en plusieurs "mots" ?
|
||||
let searchedWords: string[]=[];
|
||||
if(this.searchMode.separatedWords)
|
||||
searchedWords=realSearch.split(" ");
|
||||
else
|
||||
searchedWords[0]=realSearch;
|
||||
|
||||
// Chacun des "mots" doit être trouvé au moins une fois :
|
||||
let nbFound=0;
|
||||
for(const word of searchedWords)
|
||||
{
|
||||
if(this._fields2Search.indexOf(field) !== -1)
|
||||
{
|
||||
// Attention, recherche insensible à la casse, mais aux accents, etc.
|
||||
if(data[field].toLowerCase().indexOf(this._inputValue.toLowerCase()) !== -1)
|
||||
return true;
|
||||
for(let field in data)
|
||||
{
|
||||
if(this._fields2Search.indexOf(field) !== -1)
|
||||
{
|
||||
if(this.searchPreProcessing(data[field]).indexOf(word.trim()) !== -1)
|
||||
{
|
||||
nbFound++;
|
||||
break; // ! sinon, on peut trouver +sieurs fois le même "mot" dans les différents champs.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if(nbFound < searchedWords.length) // tous les mots doivent être trouvés
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,13 @@ var SearchEngine = (function () {
|
|||
this.placeholder = "";
|
||||
this.automaticSearch = false;
|
||||
this._inputValue = "";
|
||||
this.searchMode = {
|
||||
accentOff: true,
|
||||
caseOff: true,
|
||||
separatedWords: true,
|
||||
specialCharsOff: true,
|
||||
specialCharsWhiteList: "",
|
||||
};
|
||||
if (converter.fields.length === 0 || converter.datas.length === 0)
|
||||
throw new Error(errors.filterNeedDatas);
|
||||
else {
|
||||
|
@ -49,6 +56,8 @@ var SearchEngine = (function () {
|
|||
set: function (txt) {
|
||||
if (txt.trim() !== "" && txt.length <= 30)
|
||||
this._btnTxt = txt;
|
||||
else
|
||||
console.error(errors.searchBtnTxtFail);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
|
@ -83,29 +92,60 @@ var SearchEngine = (function () {
|
|||
var searchInput = document.getElementById("freeDatas2HTMLSearchTxt"), mySearch = this;
|
||||
searchInput.addEventListener("input", function (e) {
|
||||
e.preventDefault();
|
||||
mySearch._inputValue = searchInput.value;
|
||||
var searchLength = searchInput.value.length;
|
||||
mySearch._inputValue = searchInput.value.trim();
|
||||
var searchLength = mySearch._inputValue.length;
|
||||
if (mySearch.automaticSearch && (mySearch.nbCharsForSearch === 0 || (searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch)))
|
||||
mySearch._converter.refreshView();
|
||||
});
|
||||
var searchBtn = document.getElementById("freeDatas2HTMLSearchBtn");
|
||||
searchBtn.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
var searchLength = searchInput.value.length;
|
||||
if ((mySearch.nbCharsForSearch === 0 || (searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch)))
|
||||
mySearch._converter.refreshView();
|
||||
mySearch._converter.refreshView();
|
||||
});
|
||||
};
|
||||
SearchEngine.prototype.searchPreProcessing = function (searchElement) {
|
||||
var finalString = searchElement;
|
||||
if (this.searchMode.accentOff)
|
||||
finalString = finalString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||
if (this.searchMode.caseOff)
|
||||
finalString = finalString.toLowerCase();
|
||||
if (this.searchMode.specialCharsOff) {
|
||||
var validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " + this.searchMode.specialCharsWhiteList;
|
||||
var validString = "";
|
||||
for (var _i = 0, finalString_1 = finalString; _i < finalString_1.length; _i++) {
|
||||
var letter = finalString_1[_i];
|
||||
if (validChars.indexOf(letter) !== -1)
|
||||
validString += letter;
|
||||
}
|
||||
finalString = validString;
|
||||
}
|
||||
return finalString;
|
||||
};
|
||||
SearchEngine.prototype.dataIsOk = function (data) {
|
||||
if (this._inputValue.length === 0)
|
||||
var realSearch = this.searchPreProcessing(this._inputValue.trim());
|
||||
if (realSearch.length === 0)
|
||||
return true;
|
||||
for (var field in data) {
|
||||
if (this._fields2Search.indexOf(field) !== -1) {
|
||||
if (data[field].toLowerCase().indexOf(this._inputValue.toLowerCase()) !== -1)
|
||||
return true;
|
||||
var searchedWords = [];
|
||||
if (this.searchMode.separatedWords)
|
||||
searchedWords = realSearch.split(" ");
|
||||
else
|
||||
searchedWords[0] = realSearch;
|
||||
var nbFound = 0;
|
||||
for (var _i = 0, searchedWords_1 = searchedWords; _i < searchedWords_1.length; _i++) {
|
||||
var word = searchedWords_1[_i];
|
||||
for (var field in data) {
|
||||
if (this._fields2Search.indexOf(field) !== -1) {
|
||||
if (this.searchPreProcessing(data[field]).indexOf(word.trim()) !== -1) {
|
||||
nbFound++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (nbFound < searchedWords.length)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
};
|
||||
return SearchEngine;
|
||||
}());
|
||||
|
|
|
@ -54,7 +54,7 @@ var initialise = function () { return __awaiter(void 0, void 0, void 0, function
|
|||
return 0;
|
||||
};
|
||||
converter = new FreeDatas2HTML("CSV");
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/elements-chimiques.csv" });
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/elements-chimiques.csv" });
|
||||
return [4, converter.run()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
|
|
|
@ -42,7 +42,7 @@ var initialise = function () { return __awaiter(void 0, void 0, void 0, function
|
|||
case 0:
|
||||
_a.trys.push([0, 2, , 3]);
|
||||
converter = new FreeDatas2HTML("JSON");
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/posts2.json", withCredentials: true, headers: [{ key: "Authorization", value: "Token YWxhZGRpbjpvcGVuc2VzYW1l" }] });
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/posts2.json" });
|
||||
return [4, converter.run()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
|
|
|
@ -43,7 +43,7 @@ var initialise = function () { return __awaiter(void 0, void 0, void 0, function
|
|||
case 0:
|
||||
_a.trys.push([0, 2, , 3]);
|
||||
converter = new FreeDatas2HTML("JSON");
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/links.json" });
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/links.json" });
|
||||
return [4, converter.run()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
|
|
|
@ -43,7 +43,7 @@ var initialise = function () { return __awaiter(void 0, void 0, void 0, function
|
|||
case 0:
|
||||
_a.trys.push([0, 2, , 3]);
|
||||
converter = new FreeDatas2HTML("CSV");
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/elements-chimiques.csv" });
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/elements-chimiques.csv" });
|
||||
return [4, converter.run()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
|
|
|
@ -29,6 +29,7 @@ module.exports =
|
|||
remoteSourceNeedUrl: "Merci de fournir une url valide pour la source distante de données.",
|
||||
remoteSourceUrlFail: "L'url fournie ne semble pas valide.",
|
||||
renderNeedFields: "Les noms de champs doivent être fournis avant de demander l'affichage des données.",
|
||||
searchBtnTxtFail: "Le texte du bouton du moteur de recherche doit contenir au maximum 30 caractères.",
|
||||
searchFieldNotFound: "Au moins un des champs devant être utilisés par le moteur de recherche n'existe pas dans les données.",
|
||||
selector2HTMLFail: "Le création d'un filtre dans le DOM nécessite l'initialisation de l'élément HTML et du numéro du champs à filter.",
|
||||
selectorFieldIsEmpty: "Aucune donnée trouvée pour le champ du filtre",
|
||||
|
|
|
@ -20,7 +20,7 @@ const initialise=async () =>
|
|||
|
||||
// Création d'un convertisseur parsant les données d'un fichier CSV "distant"
|
||||
const converter=new FreeDatas2HTML("CSV");
|
||||
converter.parser.setRemoteSource({ url:"http://localhost:8080/datas/elements-chimiques.csv" });
|
||||
converter.parser.setRemoteSource({ url:"https://freedatas2html.le-fab-lab.com/datas/elements-chimiques.csv" });
|
||||
// Parsage des données, qui ne sont pas encore affichées :
|
||||
await converter.run();
|
||||
|
||||
|
@ -78,7 +78,7 @@ const initialise=async () =>
|
|||
mySearch.filter2HTML();
|
||||
|
||||
// Injection des filtres dans le convertisseur :
|
||||
converter.datasFilters=[filtre1,filtre2,filtre3, mySearch];
|
||||
converter.datasFilters=[filtre1,filtre2,filtre3,mySearch];
|
||||
|
||||
// Désignation des champs permettant de classer les données :
|
||||
// Uniquement avec un rendu sous forme de tableau (grand écran), car des en-têtes de colonne sont nécessaires.
|
||||
|
|
|
@ -6,8 +6,9 @@ const initialise=async () =>
|
|||
{
|
||||
// Création d'un convertisseur parsant des données transmises en JSON :
|
||||
const converter=new FreeDatas2HTML("JSON");
|
||||
// Exemple d'utilisation de headers (bien qu'inutiles ici) :
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/posts2.json", withCredentials:true, headers: [{ key:"Authorization", value:"Token YWxhZGRpbjpvcGVuc2VzYW1l" }] });
|
||||
// Exemple d'utilisation de headers (inutiles ici) :
|
||||
// converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/posts2.json", withCredentials:true, headers: [{ key:"Authorization", value:"Token YWxhZGRpbjpvcGVuc2VzYW1l" }] });
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/posts2.json" });
|
||||
// Parsage des données, qui ne sont pas encore affichées :
|
||||
await converter.run();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ const initialise=async () =>
|
|||
{
|
||||
// Création d'un convertisseur parsant des données transmises en JSON :
|
||||
const converter=new FreeDatas2HTML("JSON");
|
||||
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/links.json"});
|
||||
converter.parser.setRemoteSource({ url: "https://freedatas2html.le-fab-lab.com/datas/links.json"});
|
||||
// Parsage des données, qui ne sont pas encore affichées :
|
||||
await converter.run();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ const initialise=async () =>
|
|||
{
|
||||
// Création d'un convertisseur parsant les données d'un fichier CSV "distant"
|
||||
const converter=new FreeDatas2HTML("CSV");
|
||||
converter.parser.setRemoteSource({ url:"http://localhost:8080/datas/elements-chimiques.csv" });
|
||||
converter.parser.setRemoteSource({ url:"https://freedatas2html.le-fab-lab.com/datas/elements-chimiques.csv" });
|
||||
// Parsage des données, qui ne sont pas encore affichées :
|
||||
await converter.run();
|
||||
// On affiche que certains champs :
|
||||
|
|
|
@ -28,6 +28,7 @@ module.exports =
|
|||
remoteSourceNeedUrl: "Merci de fournir une url valide pour la source distante de données.",
|
||||
remoteSourceUrlFail: "L'url fournie ne semble pas valide.",
|
||||
renderNeedFields: "Les noms de champs doivent être fournis avant de demander l'affichage des données.",
|
||||
searchBtnTxtFail: "Le texte du bouton du moteur de recherche doit contenir au maximum 30 caractères.",
|
||||
searchFieldNotFound: "Au moins un des champs devant être utilisés par le moteur de recherche n'existe pas dans les données.",
|
||||
selector2HTMLFail: "Le création d'un filtre dans le DOM nécessite l'initialisation de l'élément HTML et du numéro du champs à filter.",
|
||||
selectorFieldIsEmpty: "Aucune donnée trouvée pour le champ du filtre",
|
||||
|
|
|
@ -81,6 +81,14 @@ export interface RemoteSources extends RemoteSourceSettings
|
|||
{
|
||||
getFetchSettings() : {};
|
||||
}
|
||||
export interface SearchModeSettings
|
||||
{
|
||||
accentOff: boolean;
|
||||
caseOff: boolean;
|
||||
separatedWords: boolean;
|
||||
specialCharsOff: boolean;
|
||||
specialCharsWhiteList: string;
|
||||
}
|
||||
export interface Selectors extends Filters
|
||||
{
|
||||
datasFieldNb: number;
|
||||
|
|
|
@ -70,14 +70,13 @@ describe("Test du moteur de recherche.", () =>
|
|||
|
||||
it("Si tous numéros des champs sur lesquels effectuer les recherches existent dans les données, ils doivent être acceptés.", () =>
|
||||
{
|
||||
expect(() => { return new SearchEngine(converter, { id:"mySearch" }, [0,2,3]); }).not.toThrowError();
|
||||
mySearch=new SearchEngine(converter, { id:"mySearch" }, [0,2,3]);
|
||||
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 new SearchEngine(converter, { id:"mySearch" }, []); }).not.toThrowError();
|
||||
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)"]);
|
||||
});
|
||||
});
|
||||
|
@ -157,26 +156,7 @@ describe("Test du moteur de recherche.", () =>
|
|||
searchBtn.click();
|
||||
expect(converter.refreshView).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("Le clic sur le bouton SUBMIT doit appeler la fonction actualisant l'affichage, pour peu que le nombre de caractères défini soit saisi.", () =>
|
||||
{
|
||||
spyOn(converter, "refreshView");
|
||||
mySearch.nbCharsForSearch=3;
|
||||
searchInput.value="z";
|
||||
searchBtn.click();
|
||||
expect(converter.refreshView).not.toHaveBeenCalled();
|
||||
searchInput.value="zz";
|
||||
searchBtn.click();
|
||||
expect(converter.refreshView).not.toHaveBeenCalled();
|
||||
searchInput.value="zzz";
|
||||
searchBtn.click();
|
||||
expect(converter.refreshView).toHaveBeenCalledTimes(1);
|
||||
// Il est toujours possible d'annuler la recherche :
|
||||
searchInput.value="";
|
||||
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");
|
||||
|
@ -213,6 +193,16 @@ describe("Test du moteur de 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.", () =>
|
||||
|
@ -227,6 +217,10 @@ describe("Test du moteur de recherche.", () =>
|
|||
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", () =>
|
||||
|
@ -245,12 +239,12 @@ describe("Test du moteur de recherche.", () =>
|
|||
|
||||
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur cherchée.", async () =>
|
||||
{
|
||||
searchInput.value="Halogène";
|
||||
searchInput.value="Hallogène";
|
||||
searchInput.dispatchEvent(new Event("input"));
|
||||
expect(mySearch.dataIsOk({ "Famille": "Halogene" })).toBeFalse();// sensible aux accents
|
||||
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeFalse();
|
||||
});
|
||||
|
||||
it("Doit retourner true, si la valeur recherchée est retrouvée dans la donnée recherchée, sans prendre en compte la casse.", () =>
|
||||
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";
|
||||
|
@ -260,11 +254,75 @@ describe("Test du moteur de recherche.", () =>
|
|||
searchInput.value="gène";
|
||||
searchInput.dispatchEvent(new Event("input"));
|
||||
expect(mySearch.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
|
||||
// Insensible à casse :
|
||||
searchInput.value="halo";
|
||||
// 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();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue