Fichier parseur CSV renommé. Relecture code + ajout tests.

This commit is contained in:
Fabrice PENHOËT 2021-10-13 18:20:53 +02:00
parent 75c9179902
commit c3c0206c64
3 changed files with 48 additions and 34 deletions

View File

@ -34,24 +34,23 @@ export class ParserForCSV implements Parsers
private _parseResults:ParseResults|undefined=undefined; private _parseResults:ParseResults|undefined=undefined;
public options: PublicPapaParseOptions = public options: PublicPapaParseOptions =
{ {
delimiter: "", delimiter:"",
newline: "", newline:"",
quoteChar: '"', quoteChar:'"',
escapeChar: '"', escapeChar:'"',
transformHeader: function(field: string, index: number): string { return field.trim() }, transformHeader:function(field: string, index: number): string { return field.trim() },
preview: 0, preview:0,
comments: "", comments:"",
fastMode: undefined, fastMode:undefined,
transform: undefined transform:undefined
} }
// Options de Papa Parse ne pouvant être modifées de l'extérieur
private _privateOptions: PrivatePapaParseOptions = private _privateOptions: PrivatePapaParseOptions =
{ {
header: true, // nécessaire pour obtenir le nom des champs header:true,
download: false, download:false,
downloadRequestHeaders: undefined, downloadRequestHeaders:undefined,
skipEmptyLines:"greedy", skipEmptyLines:"greedy",
withCredentials: undefined withCredentials:undefined
} }
// L'instance d'une autre classe que RemoteSource peut être passée au constructeur // L'instance d'une autre classe que RemoteSource peut être passée au constructeur
@ -96,8 +95,6 @@ export class ParserForCSV implements Parsers
return this._privateOptions; return this._privateOptions;
} }
// async dans le cas d'une source distante
// Et création d'une Promise car PapaParse utilise des fonctions callback.
public async parse(): Promise<any> public async parse(): Promise<any>
{ {
const parser=this, options=this.options; const parser=this, options=this.options;
@ -110,8 +107,8 @@ export class ParserForCSV implements Parsers
if(parser._datasRemoteSource.headers !== undefined) if(parser._datasRemoteSource.headers !== undefined)
{ {
this._privateOptions.downloadRequestHeaders={}; this._privateOptions.downloadRequestHeaders={};
for(let i=0; i< parser._datasRemoteSource.headers.length; i++) for(let header of parser._datasRemoteSource.headers)
this._privateOptions.downloadRequestHeaders[parser._datasRemoteSource.headers[i].key]=parser._datasRemoteSource.headers[i].value; this._privateOptions.downloadRequestHeaders[header.key]=header.value;
} }
} }
else if(parser._datas2Parse !== "") else if(parser._datas2Parse !== "")
@ -133,18 +130,22 @@ export class ParserForCSV implements Parsers
comments: options.comments, comments: options.comments,
complete: function(results :any) complete: function(results :any)
{ {
// Attention, Papa Parse peut accepter un nom de champ vide // Attention, Papa Parse peut accepter un nom de champ vide ou en doublon !
let realFields: string[]=[]; let realFields: string[]=[];
for(let field of results.meta.fields) for(let field of results.meta.fields)
{ {
if(field.trim() !== "") let checkField=field.trim();
realFields.push(field); if(checkField !== "" && realFields.indexOf(checkField) === -1)
realFields.push(checkField);
else
console.error(errors.parserFieldNameFail);
} }
if(realFields.length === 0) if(realFields.length === 0)
reject(new Error(errors.parserFail)); reject(new Error(errors.parserFieldsNotFound));
else else
{ {
parser._parseResults={ parser._parseResults=
{
datas: results.data, datas: results.data,
errors: results.errors, errors: results.errors,
fields: realFields, fields: realFields,
@ -157,9 +158,8 @@ export class ParserForCSV implements Parsers
skipEmptyLines:"greedy", skipEmptyLines:"greedy",
fastMode: options.fastMode, fastMode: options.fastMode,
withCredentials: this._privateOptions.withCredentials, withCredentials: this._privateOptions.withCredentials,
transform: options.transform transform: options.transform
}); });
}); });
} }
} }

View File

@ -3,7 +3,7 @@ const errors=require("./errors.js");
import { Counter, Datas, DatasRenders, DOMElement, Paginations, Parsers, ParseErrors, RemoteSources, Selectors, SortingFields, SortingFunctions } from "./freeDatas2HTMLInterfaces"; import { Counter, Datas, DatasRenders, DOMElement, Paginations, Parsers, ParseErrors, RemoteSources, Selectors, SortingFields, SortingFunctions } from "./freeDatas2HTMLInterfaces";
import { Pagination} from "./freeDatas2HTMLPagination"; import { Pagination} from "./freeDatas2HTMLPagination";
import { ParserForCSV} from "./freeDatas2HTMLParserForCSV"; import { ParserForCSV} from "./ParserForCSV";
import { ParserForHTML} from "./freeDatas2HTMLParserForHTML"; import { ParserForHTML} from "./freeDatas2HTMLParserForHTML";
import { ParserForJSON} from "./freeDatas2HTMLParserForJSON"; import { ParserForJSON} from "./freeDatas2HTMLParserForJSON";
import { Render} from "./freeDatas2HTMLRender"; import { Render} from "./freeDatas2HTMLRender";
@ -24,7 +24,7 @@ export class FreeDatas2HTML
public datasType: "CSV"|"HTML"|"JSON"|undefined; public datasType: "CSV"|"HTML"|"JSON"|undefined;
// Le nom des champs trouvés dans les données : // Le nom des champs trouvés dans les données :
public fields: string[]|undefined=undefined; public fields: string[]|undefined=undefined;
// Les données à proprement parler : // Les données à proprement parler :
public datas: {[index: string]:any}[]=[]; public datas: {[index: string]:any}[]=[];
// Les erreurs rencontrées durant le traitement des données reçues : // Les erreurs rencontrées durant le traitement des données reçues :

View File

@ -1,10 +1,8 @@
const Papa = require("papaparse"); const Papa=require("papaparse");
import { RemoteSourceSettings } from "../src/freeDatas2HTMLInterfaces"; import { RemoteSourceSettings } from "../src/freeDatas2HTMLInterfaces";
import { ParserForCSV as Parser } from "../src/freeDatas2HTMLParserForCSV"; import { ParserForCSV as Parser } from "../src/ParserForCSV";
const errors=require("../src/errors.js"); const errors=require("../src/errors.js");
// Pas de test de Papa Parse lui-même, car il s'agit d'un module externe.
// Mais tests de la classe l'utilisant.
describe("Tests du parseur de CSV", () => describe("Tests du parseur de CSV", () =>
{ {
let parser: Parser; let parser: Parser;
@ -14,9 +12,10 @@ describe("Tests du parseur de CSV", () =>
parser=new Parser(); parser=new Parser();
}); });
it("Doit avoir créé une instance du Parser", () => it("Doit avoir créé une instance du Parser et initialiser la ressource distante avec une url vide.", () =>
{ {
expect(parser).toBeInstanceOf(Parser); expect(parser).toBeInstanceOf(Parser);
expect(parser.datasRemoteSource.url).toEqual("");
}); });
it("Doit générer une erreur si la chaîne de données à parser est vide.", () => it("Doit générer une erreur si la chaîne de données à parser est vide.", () =>
@ -39,7 +38,7 @@ describe("Tests du parseur de CSV", () =>
it("Si les données sont directement fournies, cela doit être pris en compte dans les options passées à Papa Parse.", () => it("Si les données sont directement fournies, cela doit être pris en compte dans les options passées à Papa Parse.", () =>
{ {
// Idéalement il faudrait tester les paramètres passés à PapaParse // Idéalement il faudrait tester les paramètres passés à PapaParse
// Mais semble impossible à cause des fonctions callback ?! // Mais semble impossible à cause des fonctions callback ?
spyOn(Papa, "parse"); spyOn(Papa, "parse");
parser.datas2Parse="datas"; parser.datas2Parse="datas";
parser.parse(); parser.parse();
@ -51,6 +50,7 @@ describe("Tests du parseur de CSV", () =>
skipEmptyLines:"greedy", skipEmptyLines:"greedy",
withCredentials:undefined withCredentials:undefined
}); });
expect(Papa.parse).toHaveBeenCalledTimes(1);
}); });
it("Si une ressource distante est fournie, cela doit être pris en compte dans les options passées à Papa Parse.", () => it("Si une ressource distante est fournie, cela doit être pris en compte dans les options passées à Papa Parse.", () =>
@ -64,7 +64,7 @@ describe("Tests du parseur de CSV", () =>
{ {
header:true, header:true,
download:true, download:true,
downloadRequestHeaders:{},// on passe une liste de headers, même vide downloadRequestHeaders:{},// passe une liste de headers, même vide
skipEmptyLines:"greedy", skipEmptyLines:"greedy",
withCredentials:true withCredentials:true
}); });
@ -82,6 +82,20 @@ describe("Tests du parseur de CSV", () =>
expect(Papa.parse).toHaveBeenCalledTimes(2); expect(Papa.parse).toHaveBeenCalledTimes(2);
}); });
it("Si les données à parser contiennent des noms de champ vide ou en doublon, ils doivent être ignorés.", async () =>
{
parser.datas2Parse="field1;field2;field3;field3; ;";
await parser.parse();
expect(parser.parseResults.fields).toEqual(["field1","field2","field3"]);
});
it("Doit générer une erreur si aucun nom de champ trouvé.", async () =>
{
parser.setRemoteSource({ url:"http://localhost:9876/datas/nodatas.csv" });
await expectAsync(parser.parse()).toBeRejectedWith(new Error(errors.parserFieldsNotFound));
});
// Pas de test du fonctionnement de "Papa Parse" lui-même, car il s'agit d'un module externe.
it("Si le parseur a été appelé avec les données nécessaires, des résultats doivent être enregistrés.", async () => it("Si le parseur a été appelé avec les données nécessaires, des résultats doivent être enregistrés.", async () =>
{ {
parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1.csv" }); parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1.csv" });