You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					451 lines
				
				14 KiB
			
		
		
			
		
	
	
					451 lines
				
				14 KiB
			| 
											3 years ago
										 | import {Converter} from "../src/Converter"; | ||
|  | import csv from "../src"; | ||
|  | var assert = require("assert"); | ||
|  | var fs = require("fs"); | ||
|  | var sandbox = require("sinon").sandbox.create(); | ||
|  | var file = __dirname + "/data/testData"; | ||
|  | var trailCommaData = __dirname + "/data/trailingComma"; | ||
|  | describe("CSV Converter", function () { | ||
|  |   afterEach(function () { | ||
|  |     sandbox.restore(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should create new instance of csv", function () { | ||
|  |     var obj = new Converter(); | ||
|  |     assert(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should read from a stream", function (done) { | ||
|  |     var obj = new Converter(); | ||
|  |     var stream = fs.createReadStream(file); | ||
|  |     obj.then(function (obj) { | ||
|  |       assert.equal(obj.length, 2); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     stream.pipe(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should call onNext once a row is parsed.", function (done) { | ||
|  |     var obj = new Converter(); | ||
|  |     var stream = fs.createReadStream(file); | ||
|  |     var called = false; | ||
|  |     obj.subscribe(function (resultRow) { | ||
|  |       assert(resultRow); | ||
|  |       called = true; | ||
|  |     }); | ||
|  |     obj.on("done", function () { | ||
|  |       assert(called); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     stream.pipe(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should emit end_parsed message once it is finished.", function (done) { | ||
|  |     var obj = new Converter(); | ||
|  |     obj.then(function (result) { | ||
|  |       assert(result); | ||
|  |       assert(result.length === 2); | ||
|  |       assert(result[0].date); | ||
|  |       assert(result[0].employee); | ||
|  |       assert(result[0].employee.name); | ||
|  |       assert(result[0].employee.age); | ||
|  |       assert(result[0].employee.number); | ||
|  |       assert(result[0].employee.key.length === 2); | ||
|  |       assert(result[0].address.length === 2); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     fs.createReadStream(file).pipe(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should handle traling comma gracefully", function (done) { | ||
|  |     var stream = fs.createReadStream(trailCommaData); | ||
|  |     var obj = new Converter(); | ||
|  |     obj.then(function (result) { | ||
|  |       assert(result); | ||
|  |       assert(result.length > 0); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     stream.pipe(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should handle comma in column which is surrounded by qoutes", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithComma"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var obj = new Converter({ | ||
|  |       "quote": "#" | ||
|  |     }); | ||
|  |     obj.then(function (result) { | ||
|  |       assert(result[0].col1 === "\"Mini. Sectt"); | ||
|  |       assert.equal(result[3].col2, "125001,fenvkdsf"); | ||
|  |       // console.log(result);
 | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(obj); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to convert a csv to column array data", function (done) { | ||
|  |     var columArrData = __dirname + "/data/columnArray"; | ||
|  |     var rs = fs.createReadStream(columArrData); | ||
|  |     var result:any = {}; | ||
|  |     var csvConverter = new Converter(); | ||
|  |     //end_parsed will be emitted once parsing finished
 | ||
|  |     csvConverter.then(function () { | ||
|  |       assert(result.TIMESTAMP.length === 5); | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     //record_parsed will be emitted each time a row has been parsed.
 | ||
|  |     csvConverter.subscribe(function (resultRow, rowIndex) { | ||
|  |       for (var key in resultRow) { | ||
|  |         if (resultRow.hasOwnProperty(key)) { | ||
|  |           if (!result[key] || !(result[key] instanceof Array)) { | ||
|  |             result[key] = []; | ||
|  |           } | ||
|  |           result[key][rowIndex] = resultRow[key]; | ||
|  |         } | ||
|  |       } | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to convert csv string directly", function (done) { | ||
|  |     var testData = __dirname + "/data/testData"; | ||
|  |     var data = fs.readFileSync(testData).toString(); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     //end_parsed will be emitted once parsing finished
 | ||
|  |     csvConverter.then(function (jsonObj) { | ||
|  |       assert.equal(jsonObj.length, 2); | ||
|  |     }); | ||
|  |     csvConverter.fromString(data).then(function (jsonObj) { | ||
|  |       assert(jsonObj.length === 2); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to convert csv string with error", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithUnclosedQuotes"; | ||
|  |     var data = fs.readFileSync(testData).toString(); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     csvConverter.fromString(data).then(undefined, function (err) { | ||
|  |       assert(err); | ||
|  |       assert.equal(err.err, "unclosed_quote"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to convert csv string without callback provided", function (done) { | ||
|  |     var testData = __dirname + "/data/testData"; | ||
|  |     var data = fs.readFileSync(testData).toString(); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     //end_parsed will be emitted once parsing finished
 | ||
|  |     csvConverter.then(function (jsonObj) { | ||
|  |       assert(jsonObj.length === 2); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     csvConverter.fromString(data); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to handle columns with double quotes", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithQoutes"; | ||
|  |     var data = fs.readFileSync(testData).toString(); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     csvConverter.fromString(data).then(function (jsonObj) { | ||
|  |       assert(jsonObj[0].TIMESTAMP === '13954264"22', JSON.stringify(jsonObj[0].TIMESTAMP)); | ||
|  | 
 | ||
|  |       assert(jsonObj[1].TIMESTAMP === 'abc, def, ccc', JSON.stringify(jsonObj[1].TIMESTAMP)); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should be able to handle columns with two double quotes", function (done) { | ||
|  |     var testData = __dirname + "/data/twodoublequotes"; | ||
|  |     var data = fs.readFileSync(testData).toString(); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     csvConverter.fromString(data).then(function (jsonObj) { | ||
|  |       assert.equal(jsonObj[0].title, "\""); | ||
|  |       assert.equal(jsonObj[0].data, "xyabcde"); | ||
|  |       assert.equal(jsonObj[0].uuid, "fejal\"eifa"); | ||
|  |       assert.equal(jsonObj[0].fieldA, "bnej\"\"falkfe"); | ||
|  |       assert.equal(jsonObj[0].fieldB, "\"eisjfes\""); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should handle empty csv file", function (done) { | ||
|  |     var testData = __dirname + "/data/emptyFile"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     csvConverter.then(function (jsonObj) { | ||
|  |       assert(jsonObj.length === 0); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should parse large csv file", function (done) { | ||
|  |     var testData = __dirname + "/data/large-csv-sample.csv"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var csvConverter = new Converter(); | ||
|  |     var count = 0; | ||
|  |     csvConverter.subscribe(function () { | ||
|  |       //console.log(arguments);
 | ||
|  |       count++; | ||
|  |     }); | ||
|  |     csvConverter.then(function () { | ||
|  |       assert(count === 5290); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should parse data and covert to specific types", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithType"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var csvConverter = new Converter({ | ||
|  |       checkType: true, | ||
|  |       colParser: { | ||
|  |         "column6": "string", | ||
|  |         "column7": "string" | ||
|  |       } | ||
|  |     }); | ||
|  |     csvConverter.subscribe(function (d) { | ||
|  |       assert(typeof d.column1 === "number"); | ||
|  |       assert(typeof d.column2 === "string"); | ||
|  |       assert.equal(d["colume4"], "someinvaliddate"); | ||
|  |       assert(d.column5.hello === "world"); | ||
|  |       assert(d.column6 === '{"hello":"world"}'); | ||
|  |       assert(d.column7 === "1234"); | ||
|  |       assert(d.column8 === "abcd"); | ||
|  |       assert(d.column9 === true); | ||
|  |       assert(d.column10[0] === 23); | ||
|  |       assert(d.column10[1] === 31); | ||
|  |       assert(d.column11[0].hello === "world"); | ||
|  |       assert(d["name#!"] === false); | ||
|  |     }); | ||
|  |     csvConverter.on("done", function () { | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should turn off field type check", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithType"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var csvConverter = new Converter({ | ||
|  |       checkType: false | ||
|  |     }); | ||
|  |     csvConverter.subscribe(function (d) { | ||
|  |       assert(typeof d.column1 === "string"); | ||
|  |       assert(typeof d.column2 === "string"); | ||
|  |       assert(d["column3"] === "2012-01-01"); | ||
|  |       assert(d["colume4"] === "someinvaliddate"); | ||
|  |       assert(d.column5 === '{"hello":"world"}'); | ||
|  |       assert.equal(d["column6"], '{"hello":"world"}'); | ||
|  |       assert(d["column7"] === "1234"); | ||
|  |       assert(d["column8"] === "abcd"); | ||
|  |       assert(d.column9 === "true"); | ||
|  |       assert(d.column10[0] === "23"); | ||
|  |       assert(d.column10[1] === "31"); | ||
|  |       assert(d["name#!"] === 'false'); | ||
|  |     }); | ||
|  |     csvConverter.then(function () { | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should emit data event correctly", function (done) { | ||
|  |     var testData = __dirname + "/data/large-csv-sample.csv"; | ||
|  | 
 | ||
|  |     var csvConverter = new Converter({ | ||
|  |     }); | ||
|  |     var count = 0; | ||
|  |     csvConverter.on("data", function (d) { | ||
|  |       count++; | ||
|  |     }); | ||
|  |     csvConverter.on("end", function () { | ||
|  |       assert.equal(count, 5290); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should process column with linebreaks", function (done) { | ||
|  |     var testData = __dirname + "/data/lineBreak"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var csvConverter = new Converter({ | ||
|  |       checkType: true | ||
|  |     }); | ||
|  |     csvConverter.subscribe(function (d) { | ||
|  |       assert(d.Period === 13); | ||
|  |       assert(d["Apparent age"] === "Unknown"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |     rs.pipe(csvConverter); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("be able to ignore empty columns", function (done) { | ||
|  |     var testData = __dirname + "/data/dataIgnoreEmpty"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(csv({ ignoreEmpty: true })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 3); | ||
|  |       assert(j.col2.length === 2); | ||
|  |       assert(j.col2[1] === "d3"); | ||
|  |       assert(j.col4.col3 === undefined); | ||
|  |       assert(j.col4.col5 === "world"); | ||
|  |       assert(res[1].col1 === "d2"); | ||
|  |       assert(res[2].col1 === "d4"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should allow no header", function (done) { | ||
|  |     var testData = __dirname + "/data/noheadercsv"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(new Converter({ noheader: true })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 5); | ||
|  |       assert(j.field1 === "CC102-PDMI-001"); | ||
|  |       assert(j.field2 === "eClass_5.1.3"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should allow customised header", function (done) { | ||
|  |     var testData = __dirname + "/data/noheadercsv"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       noheader: true, | ||
|  |       headers: ["a", "b"] | ||
|  |     })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 5); | ||
|  |       assert(j.a === "CC102-PDMI-001"); | ||
|  |       assert(j.b === "eClass_5.1.3"); | ||
|  |       assert(j.field3 === "10/3/2014"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should allow customised header to override existing header", function (done) { | ||
|  |     var testData = __dirname + "/data/complexJSONCSV"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       headers: [] | ||
|  |     })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 2); | ||
|  |       assert(j.field1 === "Food Factory"); | ||
|  |       assert(j.field2 === "Oscar"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should handle when there is an empty string", function (done) { | ||
|  |     var testData = __dirname + "/data/dataWithEmptyString"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       noheader: true, | ||
|  |       headers: ["a", "b", "c"], | ||
|  |       checkType: true | ||
|  |     })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  | 
 | ||
|  |       // assert(res.length===2);
 | ||
|  |       assert(j.a === "green"); | ||
|  |       assert(j.b === 40); | ||
|  |       assert.equal(j.c, ""); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should detect eol correctly when first chunk is smaller than header row length", function (done) { | ||
|  |     var testData = __dirname + "/data/dataNoTrimCRLF"; | ||
|  |     var rs = fs.createReadStream(testData, { highWaterMark: 3 }); | ||
|  | 
 | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       trim: false | ||
|  |     })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 2); | ||
|  |       assert(j.name === "joe"); | ||
|  |       assert(j.age === "20"); | ||
|  |       assert.equal(res[1].name, "sam"); | ||
|  |       assert.equal(res[1].age, "30"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should detect eol correctly when first chunk ends in middle of CRLF line break", function (done) { | ||
|  |     var testData = __dirname + "/data/dataNoTrimCRLF"; | ||
|  |     var rs = fs.createReadStream(testData, { highWaterMark: 9 }); | ||
|  | 
 | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       trim: false | ||
|  |     })); | ||
|  |     st.then(function (res) { | ||
|  |       var j = res[0]; | ||
|  |       assert(res.length === 2); | ||
|  |       assert(j.name === "joe"); | ||
|  |       assert(j.age === "20"); | ||
|  |       assert.equal(res[1].name, "sam"); | ||
|  |       assert.equal(res[1].age, "30"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should emit eol event when line ending is detected as CRLF", function (done) { | ||
|  |     var testData = __dirname + "/data/dataNoTrimCRLF"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  | 
 | ||
|  |     var st = rs.pipe(new Converter()); | ||
|  |     var eolCallback = sandbox.spy(function (eol) { | ||
|  |       assert.equal(eol, "\r\n"); | ||
|  |     }); | ||
|  |     st.on("eol", eolCallback); | ||
|  |     st.then(function () { | ||
|  |       assert.equal(eolCallback.callCount, 1, 'should emit eol event once'); | ||
|  |       done(); | ||
|  |     }) | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should emit eol event when line ending is detected as LF", function (done) { | ||
|  |     var testData = __dirname + "/data/columnArray"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  | 
 | ||
|  |     var st = rs.pipe(new Converter()); | ||
|  |     var eolCallback = sandbox.spy(function (eol) { | ||
|  |       assert.equal(eol, "\n"); | ||
|  |     }); | ||
|  |     st.on("eol", eolCallback); | ||
|  |     st.then(function () { | ||
|  |       assert.equal(eolCallback.callCount, 1, 'should emit eol event once'); | ||
|  |       done(); | ||
|  |     }) | ||
|  |   }); | ||
|  | 
 | ||
|  |   it("should remove the Byte Order Mark (BOM) from input", function (done) { | ||
|  |     var testData = __dirname + "/data/dataNoTrimBOM"; | ||
|  |     var rs = fs.createReadStream(testData); | ||
|  |     var st = rs.pipe(new Converter({ | ||
|  |       trim: false | ||
|  |     })); | ||
|  |     st.then( function (res) { | ||
|  |       var j = res[0]; | ||
|  | 
 | ||
|  |       assert(res.length===2); | ||
|  |       assert(j.name === "joe"); | ||
|  |       assert(j.age === "20"); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | }); |