diff --git a/History.md b/History.md index 6b91eebf..b3c68ddf 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +# v0.4.3 + +* Added ability to include a `rowDelimiter` at the end of a csv with the `includeEndRowDelimiter` optioin [#54](https://github.com/C2FO/fast-csv/issues/54) +* Added escaping for values that include a row delimiter +* Added more tests for new feature and escaping row delimiter values. + # v0.4.2 * Added ability to specify a rowDelimiter when creating a csv. diff --git a/README.md b/README.md index a2279712..19e52b47 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ Formatting accepts the same options as parsing with an additional `transform` op * `transform(row)`: A function that accepts a row and returns a transformed one to be written. * `rowDelimiter='\n'`: Specify an alternate row delimiter (i.e `\r\n`) +* `includeEndRowDelimiter=false`: Set to `true` to include a row delimiter at the end of the csv. **`createWriteStream(options)`** diff --git a/docs/History.html b/docs/History.html index de4caf67..229e0017 100644 --- a/docs/History.html +++ b/docs/History.html @@ -176,6 +176,12 @@ +
rowDelimiter
at the end of a csv with the includeEndRowDelimiter
optioin #54transform(row)
: A function that accepts a row and returns a transformed one to be written.rowDelimiter='\n'
: Specify an alternate row delimiter (i.e \r\n
)includeEndRowDelimiter=false
: Set to true
to include a row delimiter at the end of the csv.createWriteStream(options)
This is the lowest level of the write methods, it creates a stream that can be used to create a csv of unknown size and pipe to an output csv.
diff --git a/lib/formatter.js b/lib/formatter.js index d4e15f4b..c1dd70c1 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -2,7 +2,9 @@ var fs = require("fs"), util = require("util"), extended = require("./extended"), isUndefinedOrNull = extended.isUndefinedOrNull, + escape = extended.escape, hash = extended.hash, + has = extended.has, stream = require("stream"), Transform = stream.Transform, LINE_BREAK = extended.LINE_BREAK; @@ -10,7 +12,7 @@ var fs = require("fs"), function createFormatter(options) { options = options || {}; var delimiter = options.delimiter || ",", - ESCAPE_REGEXP = new RegExp("[" + delimiter + "\\r\\n']"), + ESCAPE_REGEXP = new RegExp("[" + delimiter + escape(options.rowDelimiter || LINE_BREAK) + "']"), QUOTE = options.quote || '"', ESCAPE = options.escape || '"', REPLACE_REGEXP = new RegExp(QUOTE, "g"); @@ -50,9 +52,10 @@ function defaultTransform(row) { function __write(writer, arr, options) { options = options || {}; var formatter = createFormatter(options), - transformer = extended.has(options, "transform") ? options.transform : defaultTransform, - hasHeaders = extended.has(options, "headers") ? options.headers : true, + transformer = has(options, "transform") ? options.transform : defaultTransform, + hasHeaders = has(options, "headers") ? options.headers : true, rowDelimiter = options.rowDelimiter || LINE_BREAK, + includeEndRowDelimiter = !!options.includeEndRowDelimiter, headersLength = 0, i = -1, j = -1, @@ -89,6 +92,9 @@ function __write(writer, arr, options) { ret.push(formatter(vals)); } writer.push(ret.join(rowDelimiter)); + if (includeEndRowDelimiter) { + writer.push(rowDelimiter); + } } } @@ -99,11 +105,12 @@ function CsvTransformStream(options) { Transform.call(this, options); this.formatter = createFormatter(options); this.rowDelimiter = options.rowDelimiter || "\n"; - var hasHeaders = this.hasHeaders = extended.has(options, "headers") ? options.headers : true; + var hasHeaders = this.hasHeaders = has(options, "headers") ? options.headers : true; this.parsedHeaders = hasHeaders ? false : true; this.buffer = []; this.maxBufferSize = options.maxBuffer || 100000; - extended.has(options, "transform") && this.transform(options.transform); + this.includeEndRowDelimiter = !!options.includeEndRowDelimiter, + has(options, "transform") && this.transform(options.transform); } util.inherits(CsvTransformStream, Transform); @@ -154,6 +161,9 @@ extended(CsvTransformStream).extend({ } } else { if (buffer.length) { + if (this.includeEndRowDelimiter) { + buffer.push(this.rowDelimiter); + } transformStreamWrite.call(this, new Buffer(buffer.join("")).toString("utf8")); buffer.length = 0; } diff --git a/package.json b/package.json index ae2ba6cf..eff26c28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fast-csv", - "version": "0.4.2", + "version": "0.4.3", "description": "CSV parser and writer", "main": "index.js", "scripts": { diff --git a/test/fast-csv.test.js b/test/fast-csv.test.js index b9784b93..8436e6f4 100644 --- a/test/fast-csv.test.js +++ b/test/fast-csv.test.js @@ -12,39 +12,129 @@ function camelize(str) { } var expected1 = [ - {"first_name": "First1", "last_name": "Last1", "email_address": "email1@email.com", address: "1 Street St, State ST, 88888"}, - {"first_name": "First2", "last_name": "Last2", "email_address": "email2@email.com", address: "2 Street St, State ST, 88888"}, - {"first_name": "First3", "last_name": "Last3", "email_address": "email3@email.com", address: "3 Street St, State ST, 88888"}, - {"first_name": "First4", "last_name": "Last4", "email_address": "email4@email.com", address: "4 Street St, State ST, 88888"}, - {"first_name": "First5", "last_name": "Last5", "email_address": "email5@email.com", address: "5 Street St, State ST, 88888"}, - {"first_name": "First6", "last_name": "Last6", "email_address": "email6@email.com", address: "6 Street St, State ST, 88888"}, - {"first_name": "First7", "last_name": "Last7", "email_address": "email7@email.com", address: "7 Street St, State ST, 88888"}, - {"first_name": "First8", "last_name": "Last8", "email_address": "email8@email.com", address: "8 Street St, State ST, 88888"}, - {"first_name": "First9", "last_name": "Last9", "email_address": "email9@email.com", address: "9 Street St, State ST, 88888"} + { + "first_name": "First1", + "last_name": "Last1", + "email_address": "email1@email.com", + address: "1 Street St, State ST, 88888" + }, + { + "first_name": "First2", + "last_name": "Last2", + "email_address": "email2@email.com", + address: "2 Street St, State ST, 88888" + }, + { + "first_name": "First3", + "last_name": "Last3", + "email_address": "email3@email.com", + address: "3 Street St, State ST, 88888" + }, + { + "first_name": "First4", + "last_name": "Last4", + "email_address": "email4@email.com", + address: "4 Street St, State ST, 88888" + }, + { + "first_name": "First5", + "last_name": "Last5", + "email_address": "email5@email.com", + address: "5 Street St, State ST, 88888" + }, + { + "first_name": "First6", + "last_name": "Last6", + "email_address": "email6@email.com", + address: "6 Street St, State ST, 88888" + }, + { + "first_name": "First7", + "last_name": "Last7", + "email_address": "email7@email.com", + address: "7 Street St, State ST, 88888" + }, + { + "first_name": "First8", + "last_name": "Last8", + "email_address": "email8@email.com", + address: "8 Street St, State ST, 88888" + }, + { + "first_name": "First9", + "last_name": "Last9", + "email_address": "email9@email.com", + address: "9 Street St, State ST, 88888" + } ]; var expected2 = [ - [ 'First1', 'Last1', 'email1@email.com', '1 Street St, State ST, 88888' ], - [ 'First2', 'Last2', 'email2@email.com', '2 Street St, State ST, 88888' ], - [ 'First3', 'Last3', 'email3@email.com', '3 Street St, State ST, 88888' ], - [ 'First4', 'Last4', 'email4@email.com', '4 Street St, State ST, 88888' ], - [ 'First5', 'Last5', 'email5@email.com', '5 Street St, State ST, 88888' ], - [ 'First6', 'Last6', 'email6@email.com', '6 Street St, State ST, 88888' ], - [ 'First7', 'Last7', 'email7@email.com', '7 Street St, State ST, 88888' ], - [ 'First8', 'Last8', 'email8@email.com', '8 Street St, State ST, 88888' ], - [ 'First9', 'Last9', 'email9@email.com', '9 Street St, State ST, 88888' ] + ['First1', 'Last1', 'email1@email.com', '1 Street St, State ST, 88888'], + ['First2', 'Last2', 'email2@email.com', '2 Street St, State ST, 88888'], + ['First3', 'Last3', 'email3@email.com', '3 Street St, State ST, 88888'], + ['First4', 'Last4', 'email4@email.com', '4 Street St, State ST, 88888'], + ['First5', 'Last5', 'email5@email.com', '5 Street St, State ST, 88888'], + ['First6', 'Last6', 'email6@email.com', '6 Street St, State ST, 88888'], + ['First7', 'Last7', 'email7@email.com', '7 Street St, State ST, 88888'], + ['First8', 'Last8', 'email8@email.com', '8 Street St, State ST, 88888'], + ['First9', 'Last9', 'email9@email.com', '9 Street St, State ST, 88888'] ]; var expected3 = [ - {"first_name": "First1", "last_name": "Last1", "email_address": "email1@email.com", address: '1 "Street" St, State ST, 88888'}, - {"first_name": "First2", "last_name": "Last2", "email_address": "email2@email.com", address: '2 "Street" St, State ST, 88888'}, - {"first_name": "First3", "last_name": "Last3", "email_address": "email3@email.com", address: '3 "Street" St, State ST, 88888'}, - {"first_name": "First4", "last_name": "Last4", "email_address": "email4@email.com", address: '4 "Street" St, State ST, 88888'}, - {"first_name": "First5", "last_name": "Last5", "email_address": "email5@email.com", address: '5 "Street" St, State ST, 88888'}, - {"first_name": "First6", "last_name": "Last6", "email_address": "email6@email.com", address: '6 "Street" St, State ST, 88888'}, - {"first_name": "First7", "last_name": "Last7", "email_address": "email7@email.com", address: '7 "Street" St, State ST, 88888'}, - {"first_name": "First8", "last_name": "Last8", "email_address": "email8@email.com", address: '8 "Street" St, State ST, 88888'}, - {"first_name": "First9", "last_name": "Last9", "email_address": "email9@email.com", address: '9 "Street" St, State ST, 88888'} + { + "first_name": "First1", + "last_name": "Last1", + "email_address": "email1@email.com", + address: '1 "Street" St, State ST, 88888' + }, + { + "first_name": "First2", + "last_name": "Last2", + "email_address": "email2@email.com", + address: '2 "Street" St, State ST, 88888' + }, + { + "first_name": "First3", + "last_name": "Last3", + "email_address": "email3@email.com", + address: '3 "Street" St, State ST, 88888' + }, + { + "first_name": "First4", + "last_name": "Last4", + "email_address": "email4@email.com", + address: '4 "Street" St, State ST, 88888' + }, + { + "first_name": "First5", + "last_name": "Last5", + "email_address": "email5@email.com", + address: '5 "Street" St, State ST, 88888' + }, + { + "first_name": "First6", + "last_name": "Last6", + "email_address": "email6@email.com", + address: '6 "Street" St, State ST, 88888' + }, + { + "first_name": "First7", + "last_name": "Last7", + "email_address": "email7@email.com", + address: '7 "Street" St, State ST, 88888' + }, + { + "first_name": "First8", + "last_name": "Last8", + "email_address": "email8@email.com", + address: '8 "Street" St, State ST, 88888' + }, + { + "first_name": "First9", + "last_name": "Last9", + "email_address": "email9@email.com", + address: '9 "Street" St, State ST, 88888' + } ]; var expected4 = [ @@ -106,42 +196,177 @@ var expected8 = [ ]; var expected9 = [ - {"first_name": "First'1", "last_name": "Last1", "email_address": "email1@email.com", address: "1 Street St, State ST, 88888"}, - {"first_name": "First'2", "last_name": "Last2", "email_address": "email2@email.com", address: "2 Street St, State ST, 88888"}, - {"first_name": "First'3", "last_name": "Last3", "email_address": "email3@email.com", address: "3 Street St, State ST, 88888"}, - {"first_name": "First'4", "last_name": "Last4", "email_address": "email4@email.com", address: "4 Street St, State ST, 88888"}, - {"first_name": "First'5", "last_name": "Last5", "email_address": "email5@email.com", address: "5 Street St, State ST, 88888"}, - {"first_name": "First'6", "last_name": "Last6", "email_address": "email6@email.com", address: "6 Street St, State ST, 88888"}, - {"first_name": "First'7", "last_name": "Last7", "email_address": "email7@email.com", address: "7 Street St, State ST, 88888"}, - {"first_name": "First'8", "last_name": "Last8", "email_address": "email8@email.com", address: "8 Street St, State ST, 88888"}, - {"first_name": "First'9", "last_name": "Last9", "email_address": "email9@email.com", address: "9 Street St, State ST, 88888"} + { + "first_name": "First'1", + "last_name": "Last1", + "email_address": "email1@email.com", + address: "1 Street St, State ST, 88888" + }, + { + "first_name": "First'2", + "last_name": "Last2", + "email_address": "email2@email.com", + address: "2 Street St, State ST, 88888" + }, + { + "first_name": "First'3", + "last_name": "Last3", + "email_address": "email3@email.com", + address: "3 Street St, State ST, 88888" + }, + { + "first_name": "First'4", + "last_name": "Last4", + "email_address": "email4@email.com", + address: "4 Street St, State ST, 88888" + }, + { + "first_name": "First'5", + "last_name": "Last5", + "email_address": "email5@email.com", + address: "5 Street St, State ST, 88888" + }, + { + "first_name": "First'6", + "last_name": "Last6", + "email_address": "email6@email.com", + address: "6 Street St, State ST, 88888" + }, + { + "first_name": "First'7", + "last_name": "Last7", + "email_address": "email7@email.com", + address: "7 Street St, State ST, 88888" + }, + { + "first_name": "First'8", + "last_name": "Last8", + "email_address": "email8@email.com", + address: "8 Street St, State ST, 88888" + }, + { + "first_name": "First'9", + "last_name": "Last9", + "email_address": "email9@email.com", + address: "9 Street St, State ST, 88888" + } ]; var expected10 = [ - {"first_name": "First\"1", "last_name": "Last1", "email_address": "email1@email.com", address: "1 Street St, State ST, 88888"}, - {"first_name": "First\"2", "last_name": "Last2", "email_address": "email2@email.com", address: "2 Street St, State ST, 88888"}, - {"first_name": "First\"3", "last_name": "Last3", "email_address": "email3@email.com", address: "3 Street St, State ST, 88888"}, - {"first_name": "First\"4", "last_name": "Last4", "email_address": "email4@email.com", address: "4 Street St, State ST, 88888"}, - {"first_name": "First\"5", "last_name": "Last5", "email_address": "email5@email.com", address: "5 Street St, State ST, 88888"}, - {"first_name": "First\"6", "last_name": "Last6", "email_address": "email6@email.com", address: "6 Street St, State ST, 88888"}, - {"first_name": "First\"7", "last_name": "Last7", "email_address": "email7@email.com", address: "7 Street St, State ST, 88888"}, - {"first_name": "First\"8", "last_name": "Last8", "email_address": "email8@email.com", address: "8 Street St, State ST, 88888"}, - {"first_name": "First\"9", "last_name": "Last9", "email_address": "email9@email.com", address: "9 Street St, State ST, 88888"} + { + "first_name": "First\"1", + "last_name": "Last1", + "email_address": "email1@email.com", + address: "1 Street St, State ST, 88888" + }, + { + "first_name": "First\"2", + "last_name": "Last2", + "email_address": "email2@email.com", + address: "2 Street St, State ST, 88888" + }, + { + "first_name": "First\"3", + "last_name": "Last3", + "email_address": "email3@email.com", + address: "3 Street St, State ST, 88888" + }, + { + "first_name": "First\"4", + "last_name": "Last4", + "email_address": "email4@email.com", + address: "4 Street St, State ST, 88888" + }, + { + "first_name": "First\"5", + "last_name": "Last5", + "email_address": "email5@email.com", + address: "5 Street St, State ST, 88888" + }, + { + "first_name": "First\"6", + "last_name": "Last6", + "email_address": "email6@email.com", + address: "6 Street St, State ST, 88888" + }, + { + "first_name": "First\"7", + "last_name": "Last7", + "email_address": "email7@email.com", + address: "7 Street St, State ST, 88888" + }, + { + "first_name": "First\"8", + "last_name": "Last8", + "email_address": "email8@email.com", + address: "8 Street St, State ST, 88888" + }, + { + "first_name": "First\"9", + "last_name": "Last9", + "email_address": "email9@email.com", + address: "9 Street St, State ST, 88888" + } ]; var expected14 = [ - {"first_name": "First1", "last_name": "Last1", "email_address": "email1@email.com", address: "1 Street St, State ST, 88888"}, - {"first_name": "First2", "last_name": "Last2", "email_address": "email2@email.com", address: "2 Street St, State ST, 88888"}, - {"first_name": "First\"3", "last_name": "Last3", "email_address": "email3@email.com", address: "3 Street St, State ST, 88888"}, - {"first_name": "First\"4", "last_name": "Last4", "email_address": "email4@email.com", address: "4 Street St, State ST, 88888"}, - {"first_name": "First'5", "last_name": "Last5", "email_address": "email5@email.com", address: "5 Street St, State ST, 88888"}, - {"first_name": "First'6", "last_name": "Last6", "email_address": "email6@email.com", address: "6 Street St, State ST, 88888"}, - {"first_name": "First'7", "last_name": "Last7", "email_address": "email7@email.com", address: "7 Street St, State ST, 88888"} + { + "first_name": "First1", + "last_name": "Last1", + "email_address": "email1@email.com", + address: "1 Street St, State ST, 88888" + }, + { + "first_name": "First2", + "last_name": "Last2", + "email_address": "email2@email.com", + address: "2 Street St, State ST, 88888" + }, + { + "first_name": "First\"3", + "last_name": "Last3", + "email_address": "email3@email.com", + address: "3 Street St, State ST, 88888" + }, + { + "first_name": "First\"4", + "last_name": "Last4", + "email_address": "email4@email.com", + address: "4 Street St, State ST, 88888" + }, + { + "first_name": "First'5", + "last_name": "Last5", + "email_address": "email5@email.com", + address: "5 Street St, State ST, 88888" + }, + { + "first_name": "First'6", + "last_name": "Last6", + "email_address": "email6@email.com", + address: "6 Street St, State ST, 88888" + }, + { + "first_name": "First'7", + "last_name": "Last7", + "email_address": "email7@email.com", + address: "7 Street St, State ST, 88888" + } ]; var expected21 = [ - {"first_name": "First\n1", "last_name": "Last\n1", "email_address": "email1@email.com", address: "1 Street St,\nState ST, 88888"}, - {"first_name": "First\n2", "last_name": "Last\n2", "email_address": "email2@email.com", address: "2 Street St,\nState ST, 88888"} + { + "first_name": "First\n1", + "last_name": "Last\n1", + "email_address": "email1@email.com", + address: "1 Street St,\nState ST, 88888" + }, + { + "first_name": "First\n2", + "last_name": "Last\n2", + "email_address": "email2@email.com", + address: "2 Street St,\nState ST, 88888" + } ]; var expected23 = [ @@ -713,6 +938,44 @@ it.describe("fast-csv", function (it) { } }).on("error", next); }); + + it.describe("rowDelimiter option", function (it) { + it.should("support specifying an alternate row delimiter", function (next) { + var ws = new stream.Writable(); + ws._write = function (data) { + assert.deepEqual(data.toString(), "a,b\r\na1,b1\r\na2,b2"); + next(); + }; + csv.writeToStream(ws, [ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ], {headers: true, rowDelimiter: "\r\n"}).on("error", next); + }); + + it.should("escape values that contain the alternate row delimiter", function (next) { + var ws = new stream.Writable(); + ws._write = function (data) { + assert.deepEqual(data.toString(), "a,b\t\"a\t1\",b1\t\"a\t2\",b2"); + next(); + }; + csv.writeToStream(ws, [ + {a: "a\t1", b: "b1"}, + {a: "a\t2", b: "b2"} + ], {headers: true, rowDelimiter: "\t"}).on("error", next); + }); + }); + + it.should("add a final rowDelimiter if includeEndRowDelimiter is true", function (next) { + var ws = new stream.Writable(); + ws._write = function (data) { + assert.deepEqual(data.toString(), "a,b\na1,b1\na2,b2\n"); + next(); + }; + csv.writeToStream(ws, [ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ], {headers: true, includeEndRowDelimiter: true}).on("error", next); + }); }); it.describe(".writeToString", function (it) { @@ -755,14 +1018,35 @@ it.describe("fast-csv", function (it) { }), "A,B\na1,b1\na2,b2"); }); - it.should("support specifying an alternate row delimiter", function () { + it.describe("rowDelimiter option", function (it) { + it.should("support specifying an alternate row delimiter", function () { + assert.equal(csv.writeToString([ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ], { + headers: true, + rowDelimiter: '\r\n' + }), "a,b\r\na1,b1\r\na2,b2"); + }); + it.should("escape values that contain the alternate row delimiter", function () { + assert.equal(csv.writeToString([ + {a: "a\t1", b: "b1"}, + {a: "a\t2", b: "b2"} + ], { + headers: true, + rowDelimiter: '\t' + }), "a,b\t\"a\t1\",b1\t\"a\t2\",b2"); + }); + }); + + it.should("add a final rowDelimiter if includeEndRowDelimiter is true", function () { assert.equal(csv.writeToString([ {a: "a1", b: "b1"}, {a: "a2", b: "b2"} ], { headers: true, - rowDelimiter: '\r\n' - }), "a,b\r\na1,b1\r\na2,b2"); + includeEndRowDelimiter: true + }), "a,b\na1,b1\na2,b2\n"); }); }); @@ -835,16 +1119,43 @@ it.describe("fast-csv", function (it) { }).on("error", next).pipe(ws); }); - it.should("support specifying an alternate row delimiter", function (next) { + it.describe("rowDelimiter option", function (it) { + + it.should("support specifying an alternate row delimiter", function (next) { + var ws = new stream.Writable(); + ws._write = function (data) { + assert.deepEqual(data.toString(), "a,b\r\na1,b1\r\na2,b2"); + next(); + }; + csv.write([ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ], {headers: true, rowDelimiter: '\r\n'}).on("error", next).pipe(ws); + }); + + it.should("escape values that contain the alternate row delimiter", function (next) { + var ws = new stream.Writable(); + ws._write = function (data) { + assert.deepEqual(data.toString(), "a,b\t\"a\t1\",b1\t\"a\t2\",b2"); + next(); + }; + csv.write([ + {a: "a\t1", b: "b1"}, + {a: "a\t2", b: "b2"} + ], {headers: true, rowDelimiter: '\t'}).on("error", next).pipe(ws); + }); + }); + + it.should("add a final rowDelimiter if includeEndRowDelimiter is true", function (next) { var ws = new stream.Writable(); ws._write = function (data) { - assert.deepEqual(data.toString(), "a,b\r\na1,b1\r\na2,b2"); + assert.deepEqual(data.toString(), "a,b\na1,b1\na2,b2\n"); next(); }; csv.write([ {a: "a1", b: "b1"}, {a: "a2", b: "b2"} - ], {headers: true, rowDelimiter: '\r\n'}).on("error", next).pipe(ws); + ], {headers: true, includeEndRowDelimiter: true}).on("error", next).pipe(ws); }); }); @@ -925,15 +1236,46 @@ it.describe("fast-csv", function (it) { }); }); - it.should("support specifying an alternate row delimiter", function (next) { + it.describe("rowDelimiter option", function (it) { + + it.should("support specifying an alternate row delimiter", function (next) { + csv + .writeToPath(path.resolve(__dirname, "assets/test.csv"), [ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ], {headers: true, rowDelimiter: '\r\n'}) + .on("error", next) + .on("finish", function () { + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\r\na1,b1\r\na2,b2"); + fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); + next(); + }); + }); + + it.should("escape values that contain the alternate row delimiter", function (next) { + csv + .writeToPath(path.resolve(__dirname, "assets/test.csv"), [ + {a: "a\t1", b: "b1"}, + {a: "a\t2", b: "b2"} + ], {headers: true, rowDelimiter: '\t'}) + .on("error", next) + .on("finish", function () { + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\t\"a\t1\",b1\t\"a\t2\",b2"); + fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); + next(); + }); + }); + }); + + it.should("add a final rowDelimiter if includeEndRowDelimiter is true", function (next) { csv .writeToPath(path.resolve(__dirname, "assets/test.csv"), [ {a: "a1", b: "b1"}, {a: "a2", b: "b2"} - ], {headers: true, rowDelimiter: '\r\n'}) + ], {headers: true, includeEndRowDelimiter: true}) .on("error", next) .on("finish", function () { - assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\r\na1,b1\r\na2,b2"); + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\na1,b1\na2,b2\n"); fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); next(); }); @@ -1013,14 +1355,62 @@ it.describe("fast-csv", function (it) { stream.write(null); }); - it.should("support specifying an alternate row delimiter", function (next) { + it.describe("rowDelimiter option", function (it) { + + it.should("support specifying an alternate row delimiter", function (next) { + var writable = fs.createWriteStream(path.resolve(__dirname, "assets/test.csv"), {encoding: "utf8"}); + var stream = csv + .createWriteStream({headers: true, rowDelimiter: '\r\n'}) + .on("error", next); + writable + .on("finish", function () { + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\r\na1,b1\r\na2,b2"); + fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); + next(); + }); + stream.pipe(writable); + var vals = [ + {a: "a1", b: "b1"}, + {a: "a2", b: "b2"} + ]; + vals.forEach(function (item) { + stream.write(item); + }); + stream.write(null); + }); + + it.should("escape values that contain the alternate row delimiter", function (next) { + var writable = fs.createWriteStream(path.resolve(__dirname, "assets/test.csv"), {encoding: "utf8"}); + var stream = csv + .createWriteStream({headers: true, rowDelimiter: '\t'}) + .on("error", next); + writable + .on("finish", function () { + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\t\"a\t1\",b1\t\"a\t2\",b2"); + fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); + next(); + }); + stream.pipe(writable); + var vals = [ + {a: "a\t1", b: "b1"}, + {a: "a\t2", b: "b2"} + ]; + vals.forEach(function (item) { + stream.write(item); + }); + stream.write(null); + }); + + }); + + it.should("add a final rowDelimiter if includeEndRowDelimiter is true", function (next) { var writable = fs.createWriteStream(path.resolve(__dirname, "assets/test.csv"), {encoding: "utf8"}); var stream = csv - .createWriteStream({headers: true, rowDelimiter: '\r\n'}) + .createWriteStream({headers: true, includeEndRowDelimiter: true}) .on("error", next); writable .on("finish", function () { - assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\r\na1,b1\r\na2,b2"); + assert.equal(fs.readFileSync(path.resolve(__dirname, "assets/test.csv")).toString(), "a,b\na1,b1\na2,b2\n"); fs.unlinkSync(path.resolve(__dirname, "assets/test.csv")); next(); });