Nouvelle version avec possibilité de liste de filtre permettant de sélectionner plusieurs valeurs.

This commit is contained in:
Fabrice PENHOËT 2022-01-13 17:25:06 +01:00
parent 6532c04ca1
commit 7a4992a243
9 changed files with 321 additions and 201 deletions

View File

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

@ -8,10 +8,11 @@ export class Selector implements Selectors
private _converter: FreeDatas2HTML;
private _datasFieldNb: number;
private _datasViewElt: DOMElement={ id: "", eltDOM: undefined };
private _selectedValue: number|undefined=undefined;
private _selectedValues: number[]=[];
private _separator: string|undefined;
private _values: string[]=[];
private _name: string="";
public isMultiple: boolean=false;// permet à l'utilisateur de sélectionner plusieurs valeurs dans la liste.
// 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.
@ -29,6 +30,8 @@ export class Selector implements Selectors
// Pas de trim(), car l'espace peut être le séparateur :
if(separator !== undefined && separator !== "")
this._separator=separator;
this._name=this._converter.fields[this._datasFieldNb];
this.setValues();
}
}
@ -52,9 +55,9 @@ export class Selector implements Selectors
return this._name;
}
get selectedValue() : number|undefined
get selectedValues() : number[]
{
return this._selectedValue;
return this._selectedValues;
}
get separator() : string|undefined
@ -67,11 +70,10 @@ export class Selector implements Selectors
return this._values;
}
// Création du <select> dans le DOM correspondant au filtre
public filter2HTML(label:string="") : void
// Dédoublonnage et classement des valeurs disponibles pour le champ du sélecteur
private setValues() :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)
for (const row of this._converter.datas)
{
let checkedValue;
if(this._separator === undefined)
@ -83,7 +85,7 @@ export class Selector implements Selectors
else
{
let checkedValues=row[this._name].split(this._separator);
for(let value of checkedValues)
for(const value of checkedValues)
{
checkedValue=value.trim();
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
@ -101,66 +103,96 @@ export class Selector implements Selectors
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.
// Actualisation de l'affichage lorsqu'une valeur est sélectionnée :
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();
});
}
}
// Création du <select> dans le DOM correspondant au filtre
public filter2HTML(label:string="") : void
{
label=(label === "") ? this._name : label;
const multipleAttr=(this.isMultiple)? " multiple" :"";
let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+label+" :</label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'"+multipleAttr+"><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.
// Actualisation de l'affichage lorsqu'une valeur est sélectionnée :
const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLSelectElement, mySelector=this;
selectElement.addEventListener("change", function(e)
{
mySelector._selectedValues=[];
if(mySelector.isMultiple)
{
for(let i=0; i < selectElement.selectedOptions.length; i++)
{
const selectedValue=parseInt(selectElement.selectedOptions[i].value,10);
if(selectedValue === 0) // = annulation de ce filtre
{
mySelector._selectedValues=[];
break;
}
else
mySelector._selectedValues.push(selectedValue-1);
}
}
else
{
let selectedValue=parseInt(selectElement.value,10);
if(selectedValue === 0) // = annulation de ce filtre
mySelector._selectedValues=[];
else
mySelector._selectedValues[0]=selectedValue-1;
}
mySelector._converter.refreshView();
});
}
public dataIsOk(data: {[index: string]:string}) : boolean
{
// Permet de vérifier que filter2HTML() a été préalablement appelée :
if(this._name === "")
throw new Error(errors.filterCheckIsOkFail);
const checkIsValid=(selector : Selector, data: {[index: string]:string}, checkedValue:string) : boolean =>
{
if(selector._separator === undefined)
{
if(data[selector._name].trim() !== checkedValue)
return false;
else
return true;
}
else
{
let find=false;
let checkedValues=data[selector._name].split(selector._separator);
for(let value of checkedValues)
{
if(value.trim() === checkedValue)
{
find=true;
break;
}
}
return find;
}
};
// Pas de valeur sélectionnée = pas de filtre sur ce champ
if(this._selectedValue === undefined)
// Pas de valeur sélectionnée = pas de filtre sur ce champ, donc tout passe :
if(this._selectedValues.length === 0)
return true;
if(this._values[this._selectedValue] === undefined) // théoriquement impossible, mais cela vient du client...
throw new Error(errors.selectorSelectedIndexNotFound);
// Si le champ est absent pour un enregistrement, il est refusé
// Un enregistrement n'ayant pas le champ du filtre sera refusé :
if(data[this._name] === undefined)
return false;
const selectedValueTxt=this._values[this._selectedValue];
if(this._separator === undefined)
// Si plusieurs options sont sélectionnées dans une liste multiple,
// il suffit qu'une soit trouvée pour que l'enregistrement soit valide.
let find=false;
for(const value of this._selectedValues)
{
if(data[this._name].trim() !== selectedValueTxt)
return false;
else
return true;
}
else
{
let find=false;
let checkedValues=data[this._name].split(this._separator);
for(let value of checkedValues)
{
if(value.trim() === selectedValueTxt)
{
find=true;
break;
}
}
return find;
if(this._values[value] === undefined) // théoriquement impossible, mais cela vient du client...
throw new Error(errors.selectorSelectedIndexNotFound);
find=checkIsValid(this, data, this._values[value]);
if(find)
break;
}
return find;
}
}

View File

@ -4,9 +4,10 @@ import { FreeDatas2HTML } from "./FreeDatas2HTML";
var Selector = (function () {
function Selector(converter, datasFieldNb, elt, separator) {
this._datasViewElt = { id: "", eltDOM: undefined };
this._selectedValue = undefined;
this._selectedValues = [];
this._values = [];
this._name = "";
this.isMultiple = false;
if (converter.fields.length === 0 || converter.datas.length === 0)
throw new Error(errors.filterNeedDatas);
else if (!converter.checkFieldExist(Number(datasFieldNb)))
@ -17,6 +18,8 @@ var Selector = (function () {
this._datasFieldNb = datasFieldNb;
if (separator !== undefined && separator !== "")
this._separator = separator;
this._name = this._converter.fields[this._datasFieldNb];
this.setValues();
}
}
Object.defineProperty(Selector.prototype, "converter", {
@ -47,9 +50,9 @@ var Selector = (function () {
enumerable: true,
configurable: true
});
Object.defineProperty(Selector.prototype, "selectedValue", {
Object.defineProperty(Selector.prototype, "selectedValues", {
get: function () {
return this._selectedValue;
return this._selectedValues;
},
enumerable: true,
configurable: true
@ -68,9 +71,7 @@ var Selector = (function () {
enumerable: true,
configurable: true
});
Selector.prototype.filter2HTML = function (label) {
if (label === void 0) { label = ""; }
this._name = this._converter.fields[this._datasFieldNb];
Selector.prototype.setValues = function () {
for (var _i = 0, _a = this._converter.datas; _i < _a.length; _i++) {
var row = _a[_i];
var checkedValue = void 0;
@ -96,50 +97,76 @@ var Selector = (function () {
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb).sort);
else
this._values.sort(compare());
label = (label === "") ? this._name : label;
var selectorsHTML = "<label for='freeDatas2HTML_" + this._datasViewElt.id + "'>" + label + " :</label><select name='freeDatas2HTML_" + this._datasViewElt.id + "' id='freeDatas2HTML_" + this._datasViewElt.id + "'><option value='0'>----</option>";
for (var i = 0; i < this._values.length; i++)
selectorsHTML += "<option value='" + (i + 1) + "'>" + this._values[i] + "</option>";
selectorsHTML += "</select>";
this._datasViewElt.eltDOM.innerHTML = selectorsHTML;
var selectElement_1 = document.getElementById("freeDatas2HTML_" + this._datasViewElt.id), mySelector_1 = this;
selectElement_1.addEventListener("change", function (e) {
if (selectElement_1.value === "0")
mySelector_1._selectedValue = undefined;
else
mySelector_1._selectedValue = parseInt(selectElement_1.value, 10) - 1;
mySelector_1._converter.refreshView();
});
}
};
Selector.prototype.dataIsOk = function (data) {
if (this._name === "")
throw new Error(errors.filterCheckIsOkFail);
if (this._selectedValue === undefined)
return true;
if (this._values[this._selectedValue] === undefined)
throw new Error(errors.selectorSelectedIndexNotFound);
if (data[this._name] === undefined)
return false;
var selectedValueTxt = this._values[this._selectedValue];
if (this._separator === undefined) {
if (data[this._name].trim() !== selectedValueTxt)
return false;
else
return true;
}
else {
var find = false;
var checkedValues = data[this._name].split(this._separator);
for (var _i = 0, checkedValues_2 = checkedValues; _i < checkedValues_2.length; _i++) {
var value = checkedValues_2[_i];
if (value.trim() === selectedValueTxt) {
find = true;
break;
Selector.prototype.filter2HTML = function (label) {
if (label === void 0) { label = ""; }
label = (label === "") ? this._name : label;
var multipleAttr = (this.isMultiple) ? " multiple" : "";
var selectorsHTML = "<label for='freeDatas2HTML_" + this._datasViewElt.id + "'>" + label + " :</label><select name='freeDatas2HTML_" + this._datasViewElt.id + "' id='freeDatas2HTML_" + this._datasViewElt.id + "'" + multipleAttr + "><option value='0'>----</option>";
for (var i = 0; i < this._values.length; i++)
selectorsHTML += "<option value='" + (i + 1) + "'>" + this._values[i] + "</option>";
selectorsHTML += "</select>";
this._datasViewElt.eltDOM.innerHTML = selectorsHTML;
var selectElement = document.getElementById("freeDatas2HTML_" + this._datasViewElt.id), mySelector = this;
selectElement.addEventListener("change", function (e) {
mySelector._selectedValues = [];
if (mySelector.isMultiple) {
for (var i = 0; i < selectElement.selectedOptions.length; i++) {
var selectedValue = parseInt(selectElement.selectedOptions[i].value, 10);
if (selectedValue === 0) {
mySelector._selectedValues = [];
break;
}
else
mySelector._selectedValues.push(selectedValue - 1);
}
}
return find;
else {
var selectedValue = parseInt(selectElement.value, 10);
if (selectedValue === 0)
mySelector._selectedValues = [];
else
mySelector._selectedValues[0] = selectedValue - 1;
}
mySelector._converter.refreshView();
});
};
Selector.prototype.dataIsOk = function (data) {
var checkIsValid = function (selector, data, checkedValue) {
if (selector._separator === undefined) {
if (data[selector._name].trim() !== checkedValue)
return false;
else
return true;
}
else {
var find_1 = false;
var checkedValues = data[selector._name].split(selector._separator);
for (var _i = 0, checkedValues_2 = checkedValues; _i < checkedValues_2.length; _i++) {
var value = checkedValues_2[_i];
if (value.trim() === checkedValue) {
find_1 = true;
break;
}
}
return find_1;
}
};
if (this._selectedValues.length === 0)
return true;
if (data[this._name] === undefined)
return false;
var find = false;
for (var _i = 0, _a = this._selectedValues; _i < _a.length; _i++) {
var value = _a[_i];
if (this._values[value] === undefined)
throw new Error(errors.selectorSelectedIndexNotFound);
find = checkIsValid(this, data, this._values[value]);
if (find)
break;
}
return find;
};
return Selector;
}());

View File

@ -5,7 +5,6 @@ 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.",

View File

@ -4,7 +4,6 @@ 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.",

View File

@ -85,7 +85,7 @@ export interface Selectors extends Filters
{
datasFieldNb: number;
name: string;
selectedValue: number|undefined;
selectedValues: number[];
separator: string|undefined;
values: string[];
}

View File

@ -4,9 +4,7 @@ module.exports =
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>',
selector2HTMLWithMultiple: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2" multiple=""><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>',
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>',

View File

@ -6,7 +6,7 @@ describe("Test des sélecteurs de données", () =>
{
let converter: FreeDatas2HTML;
let selector: Selector;
let selectElement : HTMLInputElement;
let selectElement : HTMLSelectElement;
beforeEach( async () =>
{
@ -22,9 +22,9 @@ describe("Test des sélecteurs de données", () =>
document.body.removeChild(document.getElementById("fixture"));
});
describe("Test des données de configuration.", () =>
describe("Test des données d'initialisation.", () =>
{
it("Doit générer une erreur, si initialisé sans avoir au préalable charger des données.", async () =>
it("Doit générer une erreur, si initialisé sans avoir au préalable chargé des données.", async () =>
{
// Convertisseur non lancé :
converter=new FreeDatas2HTML("CSV");
@ -43,18 +43,71 @@ describe("Test des sélecteurs de données", () =>
expect(() => { return new Selector(converter, 1.1, { id:"selector1" }); }).toThrowError(errors.selectorFieldNotFound);
});
it("Doit générer une erreur, si aucune donnée n'a été trouvée dans le champ du filtre.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasEmptyField.csv" });
await converter.run();
expect(() => { return new Selector(converter, 3, { id:"selector1" });}).toThrowError(errors.selectorFieldIsEmpty);
});
it("Si un séparateur vide est fourni pour un filtre, il doit être ignoré.", () =>
{
selector=new Selector(converter, 0, { id:"selector1" }, "");
expect(selector.separator).toBeUndefined();
});
it("Si toutes les paramètres sont valides, ils doivent être acceptés.", () =>
it("Si toutes les paramètres sont valides, ils doivent être acceptés et les informations correctement récupérées.", () =>
{
expect(() => { selector=new Selector(converter, 2, { id:"selector1" }, ","); return true; }).not.toThrowError();
expect(selector.datasFieldNb).toEqual(2);
expect(() => { selector=new Selector(converter, 3, { id:"selector1" }, ","); return true; }).not.toThrowError();
expect(selector.datasFieldNb).toEqual(3);
expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:document.getElementById("selector1") });
expect(selector.separator).toEqual(",");
expect(selector.name).toEqual("Famille");
expect(selector.values).toEqual(["Actinide","Gaz noble","gaz rare","Halogène","Indéfinie","Lanthanide","Métal alcalin","Métal alcalino-terreux","Métal de transition","Métal pauvre","Métalloïde","Non-métal"]);
});
it("Si des valeurs vides sont présentes dans un champ utilisé pour un filtre, elles doivent être ignorées.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" });
await converter.run();
selector=new Selector(converter, 3, { id:"selector1" }, "");
expect(selector.values).toEqual(["Actinide","Gaz noble","gaz rare","Halogène","Indéfinie","Lanthanide","Métal alcalin","Métal alcalino-terreux","Métal de transition","Métal pauvre","Métalloïde","Non-métal"]);
});
it("Si des espaces entourent certaines valeurs pour ce champ, ils doivent être supprimés.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasNeedTrim.csv" });
await converter.run();
selector=new Selector(converter, 3, { id:"selector1" }, "");
expect(selector.values).toEqual(["Gaz noble","Métal alcalin","Métal alcalino-terreux","Métalloïde","Non-métal"]);
});
it("Si un séparateur est fourni, les valeurs distinctes extraites de ce champ doivent le prendre en compte.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|");
selector.filter2HTML();
expect(selector.values).toEqual(["Exemple0","Exemple1","Exemple2","Exemple3","Exemple4","Exemple5","Exemple6","Exemple7","Exemple8","Exemple9","Exemple10"]);
});
it("Si une fonction spécifique est fournie pour le champ utilisé pour ce filtre, elle doit être prise en compte.", () =>
{
const mySort=(a: any, b: any, order: "asc"|"desc"="asc") =>
{
const values=[ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"];
if(order === "desc")
values.reverse();
if(values.indexOf(a) > values.indexOf(b))
return -1;
else if(values.indexOf(a) < values.indexOf(b))
return 1;
else
return 0;
};
converter.datasSortingFunctions=[{ datasFieldNb: 4, sort:mySort }];
selector=new Selector(converter, 4, { id:"selector1" });
expect(selector.values).toEqual(["Inexistant","Traces","≤ 1","> 1 et < 100 000","> 100000"]);
});
});
@ -81,56 +134,13 @@ describe("Test des sélecteurs de données", () =>
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 () =>
it("Doit prendre en compte la possibilité de sélectionner plusieurs valeurs.", () =>
{
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" });
await selector.converter.run();
selector=new Selector(converter, 4, { id:"selector2" });
selector.isMultiple=true;
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.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|");
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithSeparator);
});
it("Si une fonction spécifique est fournie pour le champ utilisé pour ce filtre, elle doit être prise en compte.", () =>
{
const mySort=(a: any, b: any, order: "asc"|"desc"="asc") =>
{
const values=[ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"];
if(order === "desc")
values.reverse();
if(values.indexOf(a) > values.indexOf(b))
return -1;
else if(values.indexOf(a) < values.indexOf(b))
return 1;
else
return 0;
};
converter.datasSortingFunctions=[{ datasFieldNb: 4, sort:mySort }];
selector=new Selector(converter, 4, { id:"selector1" });
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithFunction);
});
it("Doit générer une erreur, si aucune donnée n'a été trouvée pour créer le <SELECT>.", async () =>
{
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasEmptyField.csv" });
await selector.converter.run();
expect(() => { return selector.filter2HTML(); }).toThrowError(errors.selectorFieldIsEmpty);
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithMultiple);
// le 'multiple' ajouté est transformé en 'multiple=""' par le navigateur.
});
});
@ -140,28 +150,57 @@ describe("Test des sélecteurs de données", () =>
{
selector=new Selector(converter, 3, { id:"selector1" }); // filtre sur le champ "famille"
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLSelectElement;
});
it("La manipulation d'un sélecteur doit enregistrer la valeur sélectionnée et appeler la fonction actualisant l'affichage.", () =>
{
spyOn(converter, "refreshView");
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
expect(selector.selectedValue).toEqual(3);
selectElement.value="4";
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).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();
// Pas de choix multiples, donc seul le dernier choix est gardé :
selectElement.value="2";
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(1);
expect(converter.refreshView).toHaveBeenCalledTimes(2);
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues.length).toEqual(0);
expect(converter.refreshView).toHaveBeenCalledTimes(3);
});
it("Doit générer une erreur si une donnée est testée sur un sélecteur non affiché dans la page.", () =>
it("Si plusieurs choix sont autorisés, toutes les valeurs doivent être enregistrées, l'affichage s'actualisant à chaque fois.", () =>
{
spyOn(converter, "refreshView");
selector=new Selector(converter, 3, { id:"selector1" });
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.filterCheckIsOkFail);
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[4].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(1);
// Le choix d'une nouvelle option ne supprime pas les précédents choix
// Mais les valeurs sélectionnées sont toujours parcourues dans l'ordre du SELECT
selectElement.options[3].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(2);
expect(selector.selectedValues[1]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(2);
// Le choix 0 annule les précédents choix
selectElement.options[0].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues.length).toEqual(0);
expect(converter.refreshView).toHaveBeenCalledTimes(3);
});
it("Doit toujours retourner true si aucune des valeurs du filtre n'est sélectionnée.", () =>
@ -170,42 +209,68 @@ describe("Test des sélecteurs de données", () =>
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
// Même comportement après un retour :
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
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'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "nom" : "rémi sans famille" })).toBeFalse();
});
it("Doit générer une erreur si la valeur sélectionnée n'est pas trouvée dans la liste des valeurs connues.", () =>
{
selectElement.innerHTML=fixtures.selectorHTMLWithFakeItem;
selectElement.value="13";
selectElement.dispatchEvent(new Event("change"));
expect(() => { selector.dataIsOk({ "Famille" : "nombreuse" }); }).toThrowError(errors.selectorSelectedIndexNotFound);
});
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'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).toBeFalse();
});
it("Doit retourner true, si une donnée testée correspond à la valeur sélectionnée pour ce filtre.", () =>
{
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces :
expect(selector.dataIsOk({ "Famille": " Halogène " })).toBeTrue();
});
it("Doit retourner false, si une donnée testée ne correspond à aucune des valeurs sélectionnées dans un filtre multichoix.", async () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[3].selected=true;// sélection = gaz rare
selectElement.options[4].selected=true; // sélection = 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 à au moins une des valeurs sélectionnées dans un filtre multichoix.", () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[3].selected=true;// sélection = gaz rare
selectElement.options[4].selected=true; // sélection = Halogène
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces :
expect(selector.dataIsOk({ "Famille": " gaz rare " })).toBeTrue();
});
});
describe("Manipulation des sélecteurs avec séparateur.", () =>
@ -216,27 +281,27 @@ describe("Test des sélecteurs de données", () =>
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|"); // filtre sur le champ "Étiquettes"
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement;
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLSelectElement;;
});
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'));
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'));
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'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Étiquettes": "Exemple3" })).toBeTrue();
// Même quand elle n'est pas seule :
expect(selector.dataIsOk({ "Étiquettes": "Exemple3|Exemple1|Exemple9" })).toBeTrue();