Nouvelle version classe des filtres/sélecteurs + revue des tests associés.

This commit is contained in:
Fabrice PENHOËT 2021-10-21 17:09:57 +02:00
parent 8a129d169b
commit a58c7da008
6 changed files with 256 additions and 195 deletions

View File

@ -1,6 +1,6 @@
{
"name": "freedatas2html",
"version": "0.8.9",
"version": "0.9.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": {

View File

@ -1,40 +1,40 @@
const { compare }= require('natural-orderby');
const errors = require("./errors.js");
const { compare }=require('natural-orderby');
const errors=require("./errors.js");
import { DOMElement, Selectors } from "./interfaces";
import { FreeDatas2HTML } from "./freeDatas2HTML";
export class Selector implements Selectors
{
_converter: FreeDatas2HTML;
_datasViewElt: DOMElement= { id:"", eltDOM:undefined }; // élément du DOM dans lequel afficher le "select"
_datasFieldNb: number; // numéro du champ dont les données serviront au filtre
_separator: string|undefined; // séparateur éventuel pour les données du champ
name: string = ""; // nom à afficher dans le DOM comme "label" du "select"
values: string[]=[]; // données proposées par le filtre, après traitement des données reçues
private _converter: FreeDatas2HTML;
private _datasFieldNb: number;
private _datasViewElt: DOMElement={ id: "", eltDOM: undefined };
private _selectedValue: number|undefined=undefined;
private _separator: string|undefined;
private _values: string[]=[];
private _name: string="";
// Injection de la classe principale, mais uniquement si les données ont été importées
constructor(converter: FreeDatas2HTML, datasFieldNb: number, elt: DOMElement)
// Injection de la classe principale, mais uniquement si des données ont été importées
// Le champ duquel le sélecteur tire ses données doit exister ?
constructor(converter: FreeDatas2HTML, datasFieldNb: number, elt: DOMElement, separator?: string)
{
if(converter.fields === undefined || converter.datas.length === 0)
throw new Error(errors.selectorNeedDatas);
throw new Error(errors.filterNeedDatas);
else if(! converter.checkFieldExist(Number(datasFieldNb)))
throw new Error(errors.selectorFieldNotFound);
else
{
this._datasViewElt=FreeDatas2HTML.checkInDOMById(elt); // provoque une erreur, si élement non trouvé dans DOM
this._datasViewElt=FreeDatas2HTML.checkInDOMById(elt);
this._converter=converter;
this._datasFieldNb=datasFieldNb;
// Pas de trim(), car l'espace peut être le séparateur :
if(separator !== undefined && separator !== "")
this._separator=separator;
}
}
// Ignore un séparateur de données vide
// Attention : pas de trim(), car l'espace peut être un séparateur
set separator(separator: string|undefined)
get converter() : FreeDatas2HTML
{
if(separator === "")
this._separator=undefined;
else
this._separator=separator;
return this._converter;
}
get datasViewElt() : DOMElement
@ -47,91 +47,102 @@ export class Selector implements Selectors
return this._datasFieldNb;
}
get name() : string
{
return this._name;
}
get selectedValue() : number|undefined
{
return this._selectedValue;
}
get separator() : string|undefined
{
return this._separator;
}
// Création du <select> dans le HTML correspondant au filtre
public filter2HTML() : void
get values(): string[]
{
if(this._converter === undefined || this._datasViewElt.eltDOM === undefined || this._datasFieldNb === undefined)
throw new Error(errors.filter2HTMLFail);
else
{
this.name=this._converter.fields![this._datasFieldNb]; // this._converter.parse... ne peuvent être indéfinis si this._converter existe (cf constructeur)
for (let row of this._converter.datas)
{
if(this._separator === undefined)
{
if(row[this.name] !== "" && this.values.indexOf(row[this.name]) === -1)
this.values.push(row[this.name]);
}
else
{
let checkedValues=row[this.name].split(this._separator);
for(let value of checkedValues)
{
if(value !== "" && this.values.indexOf(value) === -1)
this.values.push(value);
}
}
}
if(this.values.length > 0)
{
// Classement des données à l'aide (ou non) d'une fonction spécifique :
if(this._converter.getSortingFunctionForField(this._datasFieldNb) !== undefined)
this.values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // sans le "!" : TS2532: Object is possibly 'undefined' ??
else
this.values.sort(compare());
return this._values;
}
// Création et injection du SELECT dans le DOM
let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+this.name+" : </label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'><option value='0'>----</option>"; // l'option zéro permet d'actualiser l'affichage en ignorant ce filtre
for(let i=0; i< this.values.length; i++)
selectorsHTML+="<option value='"+(i+1)+"'>"+this.values[i]+"</option>";
selectorsHTML+="</select>";
this. _datasViewElt.eltDOM.innerHTML=selectorsHTML;
const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement, mySelector=this;
selectElement.addEventListener("change", function(e)
{
mySelector._converter.refreshView();
});
// Création du <select> dans le DOM correspondant au filtre
public filter2HTML(label:string="") : void
{
this._name=this._converter.fields![this._datasFieldNb]; // "!" car l'existence du champ est testé par le constructeur
for (let row of this._converter.datas)
{
let checkedValue;
if(this._separator === undefined)
{
checkedValue=row[this._name].trim(); // trim() évite des problèmes de classement des éléments du SELECT
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
this._values.push(checkedValue);
}
else
{
let checkedValues=row[this._name].split(this._separator);
for(let value of checkedValues)
{
checkedValue=value.trim();
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
this._values.push(checkedValue);
}
}
}
}
// Vérifie si une valeur est sélectionnée dans la liste et, si oui, la retourne
public getSelectionnedId() : number
{
const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement;
if(selectElement === undefined)
return 0;
if(this._values.length === 0) // possible si uniquement des valeurs vides pour ce champ
throw new Error(errors.selectorFieldIsEmpty);
else
return parseInt(selectElement.value,10);
{
// Classement des données à l'aide (ou non) d'une fonction spécifique :
if(this._converter.getSortingFunctionForField(this._datasFieldNb) !== undefined)
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // sans le "!" : TS2532: Object is possibly 'undefined' ???
else
this._values.sort(compare());
// Création et injection du SELECT dans le DOM
label=(label === "") ? this._name : label;
let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+label+" :</label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'><option value='0'>----</option>"; // l'option zéro permet d'actualiser l'affichage en ignorant ce filtre
for(let i=0; i< this._values.length; i++)
selectorsHTML+="<option value='"+(i+1)+"'>"+this._values[i]+"</option>";
selectorsHTML+="</select>";
this. _datasViewElt.eltDOM!.innerHTML=selectorsHTML;// "!" car l'existence de "eltDOM" est testé par le constructeur
const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement, mySelector=this;
selectElement.addEventListener("change", function(e)
{
if(selectElement.value === "0")
mySelector._selectedValue=undefined;
else
mySelector._selectedValue=parseInt(selectElement.value,10)-1;
mySelector._converter.refreshView();
});
}
}
// Vérifie sur l'enregistrement passé correspond à la valeur sélectionnée par l'utilisateur dans le filtre
public dataIsOk(data: any) : boolean
public dataIsOk(data: {[index: string]:string}) : boolean
{
if(this.name === undefined || this.name === "")
throw new Error(errors.selectorCheckIsOkFail);
let selectedValue = this.getSelectionnedId();
if(selectedValue === 0) // = pas de valeur sélectionnée = pas de filtre sur ce champ
// Permet de vérifier que filter2HTML() a été préalablement appelée :
if(this._name === "")
throw new Error(errors.filterCheckIsOkFail);
// Pas de valeur sélectionnée = pas de filtre sur ce champ
if(this._selectedValue === undefined)
return true;
else
selectedValue=selectedValue-1;
if(this.values[selectedValue] === undefined)
if(this._values[this._selectedValue] === undefined) // théoriquement impossible, mais cela vient du client...
throw new Error(errors.selectorSelectedIndexNotFound);
if(data[this.name] === undefined) // champ absent pour cet enregistrement, qui est donc refusé
// Si le champ est absent pour un enregistrement, il est refusé
if(data[this._name] === undefined)
return false;
const selectedValueTxt=this.values[selectedValue] ;
const selectedValueTxt=this._values[this._selectedValue];
if(this._separator === undefined)
{
if(data[this.name] !== selectedValueTxt)
if(data[this._name].trim() !== selectedValueTxt)
return false;
else
return true;
@ -139,10 +150,10 @@ export class Selector implements Selectors
else
{
let find=false;
let checkedValues=data[this.name].split(this._separator);
let checkedValues=data[this._name].split(this._separator);
for(let value of checkedValues)
{
if(value === selectedValueTxt)
if(value.trim() === selectedValueTxt)
{
find=true;
break;

View File

@ -4,6 +4,8 @@ module.exports =
converterFieldNotFound : "Le champ n'existe pas dans les données ou les données n'ont pas encore été chargées.",
converterNeedDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.",
converterRefreshFail: "Le nom des champs et l'élement du DOM receveur sont nécessaires à l'affichage des données.",
filterCheckIsOkFail: "Le test est lancé sur un filtre incorrectement initialisé ou sur un attribut absent de la donnée à tester.",
filterNeedDatas: "Le création d'un filtre nécessite la présence des données à filtrer.",
pagination2HTMLFail : "Toutes les donnée nécessaires à la création des sélecteurs de pagination n'ont pas été fournies.",
paginationNeedByfaultValueBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.",
paginationNeedDatas: "Il ne peut y avoir de pagination, si les données n'ont pas été récupérées.",
@ -28,11 +30,9 @@ module.exports =
remoteSourceUrlFail: "L'url fournie ne semble pas valide.",
renderNeedFields: "Les noms de champs doivent être fournis avant de demander l'affichage des 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.",
selectorCheckIsOkFail: "Le test est lancé sur un filtre incorrectement initialisé ou sur un attribut absent de la donnée à tester.",
selectorFieldIsEmpty: "Aucune donnée trouvée pour le champ du filtre",
selectorFieldNotFound: "Au moins un des champs devant servir à filtrer les données n'existe pas dans le fichier.",
selectorNeedDatas: "Le création d'un filtre nécessite la transmission des données à filtrer.",
selectorSelectedIndexNotFound: "La valeur sélectionnée n'a pas été trouvée dans la liste des champs.",
sortingField2HTMLFail: "Toutes les donnée nécessaires à la création du lien de classement n'ont pas été fournies.",
sortingFieldNeedDatas: "Le création d'un champ de classement nécessite la transmission de la liste des champs.",
sortingFieldNotFound: "Au moins un des champs devant permettre de classer les données n'existe pas dans le fichier.",
sortingFieldsNbFail: "Le nombre de champs trouvés dans le DOM ne correspond pas à celui des données à classer.",

View File

@ -31,7 +31,7 @@ export interface Filters
{
datasViewElt: DOMElement;
filter2HTML() : void;
dataIsOk(data: any) : boolean;
dataIsOk(data: {[index: string]:string}) : boolean;
}
export interface Paginations
{
@ -54,10 +54,6 @@ export interface PaginationsPages
values?: number[];
selectedValue?: number;
}
export interface Datas
{
[key: string]: string;
}
export interface ParseErrors
{
code?: string;
@ -93,9 +89,10 @@ export interface RemoteSources extends RemoteSourceSettings
export interface Selectors extends Filters
{
datasFieldNb: number;
separator?: string|undefined;
name?: string;
values?: string[];
name: string;
selectedValue: number|undefined;
separator: string|undefined;
values: string[];
}
export interface SortingFields
{

View File

@ -1,12 +1,15 @@
module.exports =
{
datasViewEltHTML: '<div id="fixture"><div id="selector1"></div><div id="selector2"></div><div id="paginationOptions"></div><div id="counter"></div><div id="datas"></div><div id="pages"></div></div>',
selector1HTML: '<label for="freeDatas2HTML_selector1">Famille : </label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option></select>',
selector2HTML: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) : </label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selector1HTMLWithSeparator: '<label for="freeDatas2HTML_selector1">Étiquettes : </label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Exemple0</option><option value="2">Exemple1</option><option value="3">Exemple2</option><option value="4">Exemple3</option><option value="5">Exemple4</option><option value="6">Exemple5</option><option value="7">Exemple6</option><option value="8">Exemple7</option><option value="9">Exemple8</option><option value="10">Exemple9</option><option value="11">Exemple10</option></select>',
selector1HTMLWithFunction: '<label for="freeDatas2HTML_selector1">Abondance des éléments dans la croûte terrestre (μg/k) : </label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Inexistant</option><option value="2">Traces</option><option value="3">≤ 1</option><option value="4">&gt; 1 et &lt; 100 000</option><option value="5">&gt; 100000</option></select>',
sortingColumn1HTML: '<a href="#freeDatas2HTMLSorting0" id="freeDatas2HTMLSorting0">Z (numéro atomique)</a>',
sortingColumn2HTML: '<a href="#freeDatas2HTMLSorting2" id="freeDatas2HTMLSorting2">Symbole</a>',
selector1HTML: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option></select>',
selector2HTML: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selector2HTMLWithLabel: '<label for="freeDatas2HTML_selector2">Abondance des éléments :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selector1HTMLWithSeparator: '<label for="freeDatas2HTML_selector1">Étiquettes :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Exemple0</option><option value="2">Exemple1</option><option value="3">Exemple2</option><option value="4">Exemple3</option><option value="5">Exemple4</option><option value="6">Exemple5</option><option value="7">Exemple6</option><option value="8">Exemple7</option><option value="9">Exemple8</option><option value="10">Exemple9</option><option value="11">Exemple10</option></select>',
selectorHTMLWithTrim: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Gaz noble</option><option value="2">Métal alcalin</option><option value="3">Métal alcalino-terreux</option><option value="4">Métalloïde</option><option value="5">Non-métal</option></select>',
selector1HTMLWithFunction: '<label for="freeDatas2HTML_selector1">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Inexistant</option><option value="2">Traces</option><option value="3">≤ 1</option><option value="4">&gt; 1 et &lt; 100 000</option><option value="5">&gt; 100000</option></select>',
selectorHTMLWithFakeItem: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option><option value="13">Je suis un gros fake !</option></select>',
sortingColumn1HTML: '<a href="#freeDatas2HTMLSorting0" id="freeDatas2HTMLSorting0">Z (numéro atomique)</a>',
sortingColumn2HTML: '<a href="#freeDatas2HTMLSorting2" id="freeDatas2HTMLSorting2">Symbole</a>',
selectorForPagination: '<label for="freeDatas2HTMLPaginationSelector">Choix de pagination : </label><select name="freeDatas2HTMLPaginationSelector" id="freeDatas2HTMLPaginationSelector"><option value="0">----</option><option value="1">10</option><option value="2">20</option><option value="3">50</option><option value="4">500</option></select>',
selectorFor2Pages: '<label for="freeDatas2HTMLPagesSelector">Page à afficher :</label><select name="freeDatas2HTMLPagesSelector" id="freeDatas2HTMLPagesSelector"><option value="1">1</option><option value="2">2</option></select>',
selectorForManyPages: '<label for="freeDatas2HTMLPagesSelector">Page à afficher :</label><select name="freeDatas2HTMLPagesSelector" id="freeDatas2HTMLPagesSelector"><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option></select>',

View File

@ -1,12 +1,12 @@
import { FreeDatas2HTML, Selector } from "../src/freeDatas2HTML";
const errors=require("../src/errors.js");
const fixtures=require("./fixtures.js");
describe("Test des filtres de données", () =>
describe("Test des sélecteurs de données", () =>
{
let converter: FreeDatas2HTML;
let selector: Selector;
let selectElement : HTMLInputElement;
beforeEach( async () =>
{
@ -22,13 +22,18 @@ describe("Test des filtres de données", () =>
document.body.removeChild(document.getElementById("fixture"));
});
describe("Test des données reçues pour configurer un filtre.", () =>
describe("Test des données de configuration.", () =>
{
it("Doit générer une erreur, si initialisé sans fournir la liste des champs servant à classer les données.", () =>
it("Doit générer une erreur, si initialisé sans avoir au préalable charger des données.", async () =>
{
// Convertisseur non lancé :
converter=new FreeDatas2HTML("CSV");
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1.csv" });
expect(() => { return new Selector(converter, 0, { id:"selector1" }); }).toThrowError(errors.selectorNeedDatas);
expect(() => { return new Selector(converter, 0, { id:"selector1" }); }).toThrowError(errors.filterNeedDatas);
// Note : les parseurs vont générer une erreur en amont s'ils ne trouvent pas de noms de champs.
// Par contre, ils acceptent de ne pas trouver de données :
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 Selector(converter, 0, { id:"selector1" }); }).toThrowError(errors.filterNeedDatas);
});
it("Doit générer une erreur, si le numéro de champ fourni n'existe pas dans les données.", () =>
@ -40,24 +45,20 @@ describe("Test des filtres de données", () =>
it("Si un séparateur vide est fourni pour un filtre, il doit être ignoré.", () =>
{
selector=new Selector(converter, 0, { id:"selector1" });
selector.separator="";
selector=new Selector(converter, 0, { id:"selector1" }, "");
expect(selector.separator).toBeUndefined();
});
it("Si toutes les paramètres sont correctes, ils doivent être acceptés.", () =>
{
const elt=document.getElementById("selector1");
const selector=new Selector(converter, 2, { id:"selector1" });
selector.separator=",";
expect(selector.datasFieldNb).toEqual(2);
expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:elt });
expect(selector.separator).toEqual(",");
expect(() => { selector=new Selector(converter, 2, { id:"selector1" }, ","); return true; }).not.toThrowError();
expect(selector.datasFieldNb).toEqual(2);
expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:document.getElementById("selector1") });
expect(selector.separator).toEqual(",");
});
});
describe("Création et action des sélecteurs permettant de filter les données affichées.", () =>
describe("Création des sélecteurs.", () =>
{
beforeEach( async () =>
{
@ -73,22 +74,34 @@ describe("Test des filtres de données", () =>
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTML);
});
it("Doit prendre en compte l'éventuel label fourni pour le SELECT.", () =>
{
selector=new Selector(converter, 4, { id:"selector2" });
selector.filter2HTML("Abondance des éléments");
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithLabel);
});
it("Si des valeurs vides sont présentes dans une champ utilisé pour un sélecteur, elles doivent être ignorées.", async () =>
{
converter=new FreeDatas2HTML("CSV");
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" });
await converter.run();
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" });
await selector.converter.run();
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML);
});
it("Si des espaces entourent certaines valeurs pour ce champ, ils doivent être supprimés.", async () =>
{
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasNeedTrim.csv" });
await selector.converter.run();
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selectorHTMLWithTrim);
});
it("Si un séparateur est fourni, les valeurs distinctes extraites de ce champ doivent le prendre en compte.", async () =>
{
converter=new FreeDatas2HTML("CSV");
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" });
selector.separator="|";
selector=new Selector(converter, 5, { id:"selector1" }, "|");
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithSeparator);
});
@ -109,93 +122,130 @@ describe("Test des filtres de données", () =>
};
converter.datasSortingFunctions=[{ datasFieldNb: 4, sort:mySort }];
selector=new Selector(converter, 4, { id:"selector1" });
selector.separator="|";
selector.filter2HTML();
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithFunction);
});
it("Doit générer une erreur si une donnée est testée pour un sélecteur non correctement initialisé.", () =>
it("Doit générer une erreur, si aucune donnée n'a été trouvée pour créer le <SELECT>.", async () =>
{
let data2Test= {
"Z (numéro atomique)" : "53",
"Élément": "Iode",
"Symbole": "I",
"Famille": "Halogène",
"Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000",
};
expect(() => { selector.dataIsOk(data2Test); }).toThrowError(errors.selectorCheckIsOkFail);
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasEmptyField.csv" });
await selector.converter.run();
expect(() => { return selector.filter2HTML(); }).toThrowError(errors.selectorFieldIsEmpty);
});
});
describe("Manipulation des sélecteurs et filtre des données.", () =>
{
beforeEach( async () =>
{
selector=new Selector(converter, 3, { id:"selector1" }); // filtre sur le champ "famille"
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
});
it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () =>
it("La manipulation d'un sélecteur doit enregistrer la valeur sélectionnée et appeler la fonction actualisant l'affichage.", () =>
{
selector.filter2HTML();
let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
spyOn(converter, "refreshView");
selectElement.value="4";
let data2Test= { // le champ à filtrer ("Famille") est manquant
"Z (numéro atomique)" : "53",
"Élément": "Iode",
"Symbole": "I",
"Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000",
};
expect(selector.dataIsOk(data2Test)).toBeFalse();
selectElement.dispatchEvent(new Event('change'));
expect(selector.selectedValue).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(1);
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie
selectElement.dispatchEvent(new Event('change'));
expect(selector.selectedValue).toBeUndefined();
expect(converter.refreshView).toHaveBeenCalledTimes(2);
});
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", () =>
it("Doit générer une erreur si une donnée est testée sur un sélecteur non affiché dans la page.", () =>
{
selector.filter2HTML();
let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
selectElement.value="4";
let data2Test= {
"Z (numéro atomique)" : "53",
"Élément": "Iode",
"Symbole": "I",
"Famille": "Halogene", // manque un accent :)
"Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000",
};
expect(selector.dataIsOk(data2Test)).toBeFalse();
selector=new Selector(converter, 3, { id:"selector1" });
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.filterCheckIsOkFail);
});
it("Doit toujours retourner true si aucune des valeurs du filtre n'est sélectionnée.", () =>
{
// Le filtre est sur 0 par défaut
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
// Même comportement après un retour :
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
});
it("Doit générer une erreur si la valeur sélectionnée n'est pas trouvé dans la liste des valeurs connues.", () =>
{
selectElement.innerHTML=fixtures.selectorHTMLWithFakeItem;
selectElement.value="13";
selectElement.dispatchEvent(new Event('change'));
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.selectorSelectedIndexNotFound);
});
it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () =>
{
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "nom" : "rémi sans famille" })).toBeFalse();
});
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", async () =>
{
selectElement.value="4"; // = Halogène
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).toBeFalse();
});
it("Doit retourner true, si une donnée testée correspond pas à la valeur sélectionnée pour ce filtre.", () =>
{
selector.filter2HTML();
let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
selectElement.value="4";
let data2Test= {
"Z (numéro atomique)" : "53",
"Élément": "Iode",
"Symbole": "I",
"Famille": "Halogène",
"Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000",
};
expect(selector.dataIsOk(data2Test)).toBeTrue();
});
it("Doit toujours retourner true, si aucune valeur sélectionnée dans la liste.", () =>
{
selector.filter2HTML();
let data2Test= {
"Z (numéro atomique)" : "530",
"Élément": "Yode",
"Symbole": "L",
"Famille": "Halogene",
"Abondance des éléments": "> 1 et < 100 000",
};
expect(selector.dataIsOk(data2Test)).toBeTrue();
});
it("La manipulation d'un sélecteur doit appeler la fonction actualisant l'affichage, y compris pour supprimer ce filtre (0).", () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
selector.filter2HTML();
converter.datasFilters=[selector];
spyOn(converter, "refreshView");
let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
expect(converter.refreshView).toHaveBeenCalledTimes(1);
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
expect(converter.refreshView).toHaveBeenCalledTimes(2);
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces :
expect(selector.dataIsOk({ "Famille": " Halogène " })).toBeTrue();
});
});
describe("Manipulation des sélecteurs avec séparateur.", () =>
{
beforeEach( async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|"); // filtre sur le champ "Étiquettes"
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
});
it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () =>
{
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "nom" : "oui" })).toBeFalse();
});
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", () =>
{
selectElement.value="4"; // = Exemple3
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "Étiquettes": "Mauvais exemple" })).toBeFalse();
});
it("Doit retourner true, si une donnée testée correspond pas à la valeur sélectionnée pour ce filtre.", () =>
{
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
expect(selector.dataIsOk({ "Étiquettes": "Exemple3" })).toBeTrue();
// Même pas seule :
expect(selector.dataIsOk({ "Étiquettes": "Exemple3|Exemple1|Exemple9" })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple3|Exemple2" })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple4|Exemple3" })).toBeTrue();
// Y compris si entourée d'espaces :
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple4| Exemple3 " })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": " Exemple3 " })).toBeTrue();
});
});
});