Compare commits

...

11 Commits

Author SHA1 Message Date
Fabrice PENHOËT 145383d416 Nouvelle compilation. 2022-02-21 17:19:09 +01:00
Fabrice PENHOËT 8f236e8854 Appel local aux fichiers distants (site démo) pour faciliter le déploiement. 2022-02-21 17:16:23 +01:00
Fabrice PENHOËT ae5da88348 Adaptation d'une des pages de démonstration pour expliquer le fonctionnement du moteur de recherche. 2022-02-21 17:04:19 +01:00
Fabrice PENHOËT 7d85e375c5 Ajout de la possibilité de chercher séparément (ou pas) chacun des mots saisis. 2022-02-21 16:56:19 +01:00
Fabrice PENHOËT a7ca9c2e1a Ajout possibilité ou non de rendre le moteur de recherche sensible à la casse, aux accents ou encore à ignorer ou pas certains caractères spéciaux. 2022-02-21 15:51:53 +01:00
Fabrice PENHOËT ce1356f10a Mise à jour du numéro de version. 2022-02-21 11:50:14 +01:00
Fabrice PENHOËT 0dd6fe833d Relecture et adaptation du script testant le moteur de recherche. 2022-02-21 11:49:27 +01:00
Fabrice PENHOËT 596504d398 Ajout d'un trim() sur la valeur saisie avant de rechercher la valeur dans les données. 2022-02-21 11:30:48 +01:00
Fabrice PENHOËT d5c2df824c Suppression du contrôle du nombre de caractères saisis lors de l'activation du bouton devant lancer la recherche. 2022-02-21 11:26:49 +01:00
Fabrice PENHOËT 40b65d1b24 Ajout d'un trim() sur la valeur saisie avant de compter le nombre de caractères saisis. 2022-02-21 11:23:33 +01:00
Fabrice PENHOËT 100bdf8afe Ajout d'un message d'erreur dans la console, lorsque le texte fourni pour le bouton lançant une recherche est trop long. 2022-02-21 11:07:43 +01:00
22 changed files with 235 additions and 76 deletions

View File

@ -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

View File

@ -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 naurait 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 lensemble des champs, contrairement aux listes.</li>
<li>Un <b>moteur de recherche</b>, permettant de filtrer les données, cette fois sur lensemble 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 lexpression 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 800px, 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>

View File

@ -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+=`>&nbsp;<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;
}
}

View File

@ -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;
}());

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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",

View File

@ -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.

View File

@ -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();

View File

@ -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();

View File

@ -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 :

View File

@ -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",

View File

@ -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;

View File

@ -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();
});
});
});