diff --git a/package.json b/package.json index 0e53ebd..8dbbc96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "freedatas2html", - "version": "0.6.0", + "version": "0.6.1", "description": "Visualization of data from various sources (CSV, API, HTML...) with filters, classification, pagination, etc.", "main": "index.js", "scripts": { diff --git a/src/freeDatas2HTMLInterfaces.ts b/src/freeDatas2HTMLInterfaces.ts index 1915d4d..f43619b 100644 --- a/src/freeDatas2HTMLInterfaces.ts +++ b/src/freeDatas2HTMLInterfaces.ts @@ -65,8 +65,8 @@ export interface ParseResults } export interface Parsers { - datasRemoteSource: RemoteSource|undefined; - datas2Parse:string|undefined; + datasRemoteSource: RemoteSource; + datas2Parse:string; parseResults:ParseResults|undefined; parse(): Promise; } diff --git a/src/freeDatas2HTMLParserForCSV.ts b/src/freeDatas2HTMLParserForCSV.ts index 2924f6e..712210b 100644 --- a/src/freeDatas2HTMLParserForCSV.ts +++ b/src/freeDatas2HTMLParserForCSV.ts @@ -2,7 +2,7 @@ const Papa = require("papaparse"); const errors = require("./errors.js"); import { ParseResults, Parsers, RemoteSource } from "./freeDatas2HTMLInterfaces"; -interface papaParseOptions +interface PapaParseOptions { delimiter: string; newline: string; @@ -14,16 +14,24 @@ interface papaParseOptions fastMode: boolean|undefined; transform?(value: string): string; } +interface PrivatePapaParseOptions +{ + header: boolean; // nécessaire pour obtenir le nom des champs + download: boolean; + downloadRequestHeaders: undefined| { [index: string]:string|boolean|number } ; + skipEmptyLines: string; + withCredentials: boolean|undefined; +} export class ParserForCSV implements Parsers { - private _datasRemoteSource: RemoteSource|undefined=undefined; - private _datas2Parse:string|undefined=undefined; + private _datasRemoteSource: RemoteSource={ url:"" }; + private _datas2Parse:string=""; private _parseResults:ParseResults|undefined=undefined; // Ouverture de certaines options de Papa Parse : // cf. https://www.papaparse.com/docs#config - public options: papaParseOptions = + public options: PapaParseOptions = { delimiter: "", newline: "", @@ -35,7 +43,17 @@ export class ParserForCSV implements Parsers fastMode: undefined, transform: undefined } - + // Options de Papa Parse ne pouvant être modifées de l'extérieur + private _privateOptions: PrivatePapaParseOptions = + { + header: true, // nécessaire pour obtenir le nom des champs + download: false, + downloadRequestHeaders: undefined, + skipEmptyLines:"greedy", + withCredentials: undefined + } + + // Revoir tous les protocoles possibles pour une source distante (http(s)://.. , ftp..) set datasRemoteSource(source: RemoteSource) { if(source.url.trim().length === 0) @@ -47,6 +65,11 @@ export class ParserForCSV implements Parsers } } + get datasRemoteSource() : RemoteSource + { + return this._datasRemoteSource; + } + set datas2Parse(datas: string) { if(datas.trim().length === 0) @@ -55,11 +78,21 @@ export class ParserForCSV implements Parsers this._datas2Parse=datas.trim(); } + get datas2Parse() : string + { + return this._datas2Parse; + } + get parseResults() : ParseResults|undefined { return this._parseResults; } + get privateOptions() : any + { + return this._privateOptions; + } + // async dans le cas d'une source distante // Et création d'une Promise car PapaParse utilise une fonction callback. public async parse(): Promise @@ -67,20 +100,20 @@ export class ParserForCSV implements Parsers const parser=this, options=this.options; return new Promise((resolve,reject) => { - let parseContent="", parseDownload=false, parseDownloadRequestHeaders: any=undefined, parseWithCredentials: any=undefined; - if(parser._datasRemoteSource !== undefined) + let parseContent=""; + if(parser._datasRemoteSource.url !== "") { parseContent=parser._datasRemoteSource.url; - parseDownload=true; - parseWithCredentials=parser._datasRemoteSource.withCredentials; // undefined ok pour PapaParse + this._privateOptions.download=true; + this._privateOptions.withCredentials=parser._datasRemoteSource.withCredentials; if(parser._datasRemoteSource.headers !== undefined) { - parseDownloadRequestHeaders={}; + this._privateOptions.downloadRequestHeaders={}; for (let i in parser._datasRemoteSource.headers) - parseDownloadRequestHeaders[parser._datasRemoteSource.headers[Number(i)].key]=parser._datasRemoteSource.headers[Number(i)].value; + this._privateOptions.downloadRequestHeaders[""+parser._datasRemoteSource.headers[Number(i)].key+""]=parser._datasRemoteSource.headers[Number(i)].value; } } - else if(parser._datas2Parse !== undefined) + else if(parser._datas2Parse !== "") parseContent=parser._datas2Parse; else reject(new Error(errors.parserNeedSource)); @@ -91,7 +124,7 @@ export class ParserForCSV implements Parsers newline: options.newline, quoteChar: options.quoteChar, escapeChar: options.escapeChar, - header: true, // nécessaire pour obtenir le nom des champs + header: true, transformHeader: options.transformHeader, preview: options.preview, comments: options.comments, @@ -116,12 +149,12 @@ export class ParserForCSV implements Parsers resolve(true); } }, - download: parseDownload, - downloadRequestHeaders: parseDownloadRequestHeaders, + download: this._privateOptions.download, + downloadRequestHeaders: this._privateOptions.downloadRequestHeaders, skipEmptyLines:"greedy", fastMode: options.fastMode, - withCredentials: parseWithCredentials, - transform: options.transform, + withCredentials: this._privateOptions.withCredentials, + transform: options.transform }); }); } diff --git a/tests/freeDatas2HTMLSpec.ts b/tests/freeDatas2HTMLSpec.ts index 243ff68..6aa1312 100644 --- a/tests/freeDatas2HTMLSpec.ts +++ b/tests/freeDatas2HTMLSpec.ts @@ -2,7 +2,7 @@ import { FreeDatas2HTML, Render} from "../src/freeDatas2HTML"; const errors=require("../src/errors.js"); const fixtures=require("./fixtures.js"); -/// Tests à revoir pour ne pas dépendre du bon fonctionnement de Parser et Render (mock...) +/// Tests à revoir pour ne pas dépendre du bon fonctionnement de Parser et Render. describe("Test du script central de FreeDatas2HTML", () => { diff --git a/tests/parserForCSVSpec.ts b/tests/parserForCSVSpec.ts new file mode 100644 index 0000000..fd8214c --- /dev/null +++ b/tests/parserForCSVSpec.ts @@ -0,0 +1,134 @@ +const Papa = require("papaparse"); +import { RemoteSource } from "../src/freeDatas2HTMLInterfaces"; +import { ParserForCSV as Parser } from "../src/freeDatas2HTMLParserForCSV"; +const errors=require("../src/errors.js"); + +// Pas de test de Papa Parse, car module externe +// Mais tests d'instanciation et configuration de la classe l'utilisant +describe("Tests du parseur de CSV", () => +{ + let parser: Parser; + let papaParseOptions: {}; + + beforeEach( () => + { + parser=new Parser(); + papaParseOptions = + { + delimiter: "", + newline: "", + quoteChar: '"', + escapeChar: '"', + header: true, + transformHeader: function(field: string, index: number): string { return field.trim() }, + preview: 0, + comments: "", + complete: function(results :any) + { + console.log(results);// peut importe cette partie pour les tests + }, + download: undefined, + downloadRequestHeaders: undefined, + skipEmptyLines:"greedy", + fastMode: undefined, + withCredentials: undefined, + transform: undefined, + } + }); + + it("Doit avoir créé une instance du Parser", () => + { + expect(parser).toBeInstanceOf(Parser); + }); + + it("Doit générer une erreur si l'url fournie pour importer les données est une chaîne vide.", () => + { + expect(() => { return parser.datasRemoteSource= { url:"" } }).toThrowError(errors.parserNeedUrl); + expect(() => { return parser.datasRemoteSource= { url:" " } }).toThrowError(errors.parserNeedUrl); + }); + + it("Doit accepter des paramètres valides pour la source de données distante.", () => + { + let myRemoteSource: RemoteSource={ url:"zz" }; + parser.datasRemoteSource=myRemoteSource; + expect(parser.datasRemoteSource).toEqual(myRemoteSource); + myRemoteSource={ url:"zz", headers: [ { key:"test", value: "coucou"}, { key:"test2", value:"coucou2"}], withCredentials:true }; + parser.datasRemoteSource=myRemoteSource; + expect(parser.datasRemoteSource).toEqual(myRemoteSource); + }); + + it("Doit générer une erreur si la chaîne de données à parser est vide.", () => + { + expect(() => { return parser. datas2Parse= "" }).toThrowError(errors.parserNeedDatas); + expect(() => { return parser. datas2Parse= " " }).toThrowError(errors.parserNeedDatas); + }); + + it("Doit accepter toute chaîne de caractères non vide pour les données à parser.", () => + { + parser. datas2Parse="datas"; + expect(parser.datas2Parse).toEqual("datas"); + }); + + it("Doit générer une erreur si le parseur est lancé sans source de données fournie.", async () => + { + await expectAsync(parser.parse()).toBeRejectedWith(new Error(errors.parserNeedSource)); + }); + + 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 + // Mais semble impossible à cause des fonctions callback ? + spyOn(Papa, "parse"); + parser.datas2Parse="datas"; + parser.parse(); + expect(parser.privateOptions).toEqual( + { + header: true, + download: false, + downloadRequestHeaders: undefined, + skipEmptyLines:"greedy", + withCredentials: undefined + }); + }); + + it("Si une ressource distante est fournie, cela doit être pris en compte dans les options passées à Papa Parse.", () => + { + // Même remarque, test précédent + spyOn(Papa, "parse"); + let myRemoteSource: RemoteSource={ url:"http://localhost:9876/datas/datas1.csv", withCredentials:true }; + parser.datasRemoteSource=myRemoteSource; + parser.parse(); + expect(parser.privateOptions).toEqual( + { + header: true, + download: true, + downloadRequestHeaders: undefined, + skipEmptyLines: "greedy", + withCredentials: true + }); + myRemoteSource={ url:"http://localhost:9876/datas/datas1.csv", headers: [ { key:"test", value: "coucou"}, { key:"test2", value:"coucou2"}] , withCredentials:false }; + parser.datasRemoteSource=myRemoteSource; + parser.parse(); + expect(parser.privateOptions).toEqual( + { + header: true, + download: true, + downloadRequestHeaders: { test:"coucou", test2:"coucou2"}, + skipEmptyLines: "greedy", + withCredentials: false + }); + expect(Papa.parse).toHaveBeenCalledTimes(2); + }); + + it("Si le parseur a été appelé avec les données nécessaires, des résultats doivent être enregistrés.", async () => + { + parser.datasRemoteSource={ url:"http://localhost:9876/datas/datas1.csv" }; + await parser.parse(); + expect(parser.parseResults).not.toBeUndefined(); + // Y compris si données fournies bidon : + parser=new Parser(); + parser.datas2Parse="datas"; + await parser.parse(); + expect(parser.parseResults).not.toBeUndefined(); + }); +}); \ No newline at end of file