Переглянути джерело

Clean up tests and add a bunch of quoted field tests.

Jason Grout 7 роки тому
батько
коміт
061075f7f7

+ 6 - 0
packages/csvviewer/src/parse.ts

@@ -10,6 +10,7 @@ Possible options to add to the parser:
 - Skip rows with empty columns
 - Logging an error for too many or too few fields on a line
 - Ignore whitespace around delimiters
+- Add an exported function in this file for getting a field from the returned offsets array (including stripping field or row delimiters and parsing quoted data). Right now this logic is in the DSVModel. Likely we want to keep the logic there for speed, but having it here as well will make the parser more self-contained and usable by others.
 - Sanity check on field size, with an error if the field exceeds the size
 - Tests against https://github.com/maxogden/csv-spectrum
 - Benchmark against https://www.npmjs.com/package/csv-parser and https://www.npmjs.com/package/csv-string and fast-csv.
@@ -64,6 +65,11 @@ namespace IParser {
 
     /**
      * The quote character for quoting fields. Defaults to the double quote (").
+     *
+     * #### Notes
+     * As specified in [RFC 4180](https://tools.ietf.org/html/rfc4180), quotes
+     * are escaped in a quoted field by doubling them (for example, "a""b" is the field
+     * a"b).
      */
     quote?: string;
 

+ 55 - 64
tests/test-csvviewer/src/parse-noquotes.spec.ts

@@ -4,10 +4,9 @@
 import expect = require('expect.js');
 
 import {
-  parseDSVNoQuotes
+  parseDSVNoQuotes as parser
 } from '@jupyterlab/csvviewer';
 
-let parseDSV = parseDSVNoQuotes;
 
 describe('csvviewer/parsenoquotes', () => {
 
@@ -15,20 +14,18 @@ describe('csvviewer/parsenoquotes', () => {
 
     it('does basic parsing of csv files', () => {
       let data = `a,b,c,d\r\n0,1,2,3\r\n4,5,6,7`;
-      let options = {data, columnOffsets: false};
-      let offsets = parseDSV(options);
-      expect(offsets.nrows).to.eql(3);
-      expect(offsets.ncols).to.eql(0);
-      expect(offsets.offsets).to.eql([0, 9, 18]);
-    });
+      let options = {data};
+      let results;
 
-    it('does basic parsing of csv files with column offsets', () => {
-      let data = `a,b,c,d\r\n0,1,2,3\r\n4,5,6,7`;
-      let options = {data, columnOffsets: true};
-      let offsets = parseDSV(options);
-      expect(offsets.nrows).to.eql(3);
-      expect(offsets.ncols).to.eql(4);
-      expect(offsets.offsets).to.eql([0, 2, 4, 6, 9, 11, 13, 15, 18, 20, 22, 24]);
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.ncols).to.eql(0);
+      expect(results.offsets).to.eql([0, 9, 18]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(3);
+      expect(results.ncols).to.eql(4);
+      expect(results.offsets).to.eql([0, 2, 4, 6, 9, 11, 13, 15, 18, 20, 22, 24]);
     });
 
     // For simplicity, we'll use \n as a row delimiter below.
@@ -38,11 +35,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(3);
       expect(results.offsets).to.eql([0, 8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]);
@@ -53,11 +50,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, delimiter: '\t', rowDelimiter: '\n'};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(3);
       expect(results.offsets).to.eql([0, 8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]);
@@ -68,11 +65,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', startIndex: 8};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(2);
       expect(results.offsets).to.eql([8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(2);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([8, 10, 12, 14, 16, 18, 20, 22]);
@@ -83,11 +80,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', maxRows: 2};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(2);
       expect(results.offsets).to.eql([0, 8]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(2);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14]);
@@ -98,11 +95,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', startIndex: 8, maxRows: 1};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(1);
       expect(results.offsets).to.eql([8]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([8, 10, 12, 14]);
@@ -113,12 +110,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 8, 11]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 10, 10, 11, 13, 15, 17]);
@@ -129,12 +125,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\r\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 9, 13]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([ 0, 2, 4, 6, 9, 11, 11, 11, 13, 15, 17, 19 ]);
@@ -145,12 +140,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', ncols: 5};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 8, 11]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(5);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 8, 10, 10, 10, 10, 11, 13, 15, 17, 19]);
@@ -161,12 +155,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\r\n', ncols: 5};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 9, 13]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(5);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 9, 11, 11, 11, 11, 13, 15, 17, 19, 21]);
@@ -177,12 +170,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', ncols: 7};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(7);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 7, 7]);
@@ -193,11 +185,11 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n', ncols: 7};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(7);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 7, 7]);
@@ -208,27 +200,26 @@ describe('csvviewer/parsenoquotes', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(1);
       expect(results.offsets).to.eql([0]);
     });
 
-    it('handles adding or deleting columns as necessary', () => {
+    it('handles adding columns or merging columns as necessary', () => {
       let data = `a,b,c\n,c,d,e,f\ng,h`;
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSVNoQuotes({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 6, 15]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(3);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 9, 15, 17, 18]);

+ 151 - 65
tests/test-csvviewer/src/parse.spec.ts

@@ -4,7 +4,7 @@
 import expect = require('expect.js');
 
 import {
-  parseDSV
+  parseDSV as parser
 } from '@jupyterlab/csvviewer';
 
 describe('csvviewer/parse', () => {
@@ -13,20 +13,18 @@ describe('csvviewer/parse', () => {
 
     it('does basic parsing of csv files', () => {
       let data = `a,b,c,d\r\n0,1,2,3\r\n4,5,6,7`;
-      let options = {data, columnOffsets: false};
-      let offsets = parseDSV(options);
-      expect(offsets.nrows).to.eql(3);
-      expect(offsets.ncols).to.eql(0);
-      expect(offsets.offsets).to.eql([0, 9, 18]);
-    });
+      let options = {data};
+      let results;
 
-    it('does basic parsing of csv files with column offsets', () => {
-      let data = `a,b,c,d\r\n0,1,2,3\r\n4,5,6,7`;
-      let options = {data, columnOffsets: true};
-      let offsets = parseDSV(options);
-      expect(offsets.nrows).to.eql(3);
-      expect(offsets.ncols).to.eql(4);
-      expect(offsets.offsets).to.eql([0, 2, 4, 6, 9, 11, 13, 15, 18, 20, 22, 24]);
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.ncols).to.eql(0);
+      expect(results.offsets).to.eql([0, 9, 18]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(3);
+      expect(results.ncols).to.eql(4);
+      expect(results.offsets).to.eql([0, 2, 4, 6, 9, 11, 13, 15, 18, 20, 22, 24]);
     });
 
     // For simplicity, we'll use \n as a row delimiter below.
@@ -36,11 +34,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(3);
       expect(results.offsets).to.eql([0, 8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]);
@@ -51,11 +49,11 @@ describe('csvviewer/parse', () => {
       let options = {data, delimiter: '\t', rowDelimiter: '\n'};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(3);
       expect(results.offsets).to.eql([0, 8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]);
@@ -66,11 +64,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', startIndex: 8};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(2);
       expect(results.offsets).to.eql([8, 16]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(2);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([8, 10, 12, 14, 16, 18, 20, 22]);
@@ -81,11 +79,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', maxRows: 2};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(2);
       expect(results.offsets).to.eql([0, 8]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(2);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 12, 14]);
@@ -96,11 +94,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', startIndex: 8, maxRows: 1};
       let results;
 
-      results = parseDSV({...options, columnOffsets: false});
+      results = parser({...options, columnOffsets: false});
       expect(results.nrows).to.eql(1);
       expect(results.offsets).to.eql([8]);
 
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([8, 10, 12, 14]);
@@ -111,12 +109,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 8, 11]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([0, 2, 4, 6, 8, 10, 10, 10, 11, 13, 15, 17]);
@@ -127,12 +124,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\r\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 9, 13]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(4);
       expect(results.offsets).to.eql([ 0, 2, 4, 6, 9, 11, 11, 11, 13, 15, 17, 19 ]);
@@ -143,12 +139,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', ncols: 5};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 8, 11]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(5);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 8, 10, 10, 10, 10, 11, 13, 15, 17, 19]);
@@ -159,12 +154,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\r\n', ncols: 5};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 9, 13]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(5);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 9, 11, 11, 11, 11, 13, 15, 17, 19, 21]);
@@ -175,12 +169,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', ncols: 7};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(7);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 7, 7]);
@@ -191,11 +184,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n', ncols: 7};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(7);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 7, 7]);
@@ -206,12 +199,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(1);
       expect(results.offsets).to.eql([0]);
@@ -222,12 +214,11 @@ describe('csvviewer/parse', () => {
       let options = {data, rowDelimiter: '\n'};
       let results;
 
-      // results = parseDSV({...options, columnOffsets: false});
-      // expect(results.nrows).to.eql(3);
-      // expect(results.offsets).to.eql([0, 8, 12]);
-
-      results = parseDSV({...options, columnOffsets: true});
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(3);
+      expect(results.offsets).to.eql([0, 6, 15]);
 
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(3);
       expect(results.ncols).to.eql(3);
       expect(results.offsets).to.eql([0, 2, 4, 6, 7, 9, 15, 17, 18]);
@@ -236,20 +227,115 @@ describe('csvviewer/parse', () => {
   });
 
   describe('parseDSV quotes', () => {
+
     it('does basic parsing of quoted csv files', () => {
       let data = `first,"last",address,city,zip`;
       let options = {data, rowDelimiter: '\n'};
       let results;
-      results = parseDSV({...options, columnOffsets: true});
 
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
+
+      results = parser({...options, columnOffsets: true});
       expect(results.nrows).to.eql(1);
       expect(results.ncols).to.eql(5);
       expect(results.offsets).to.eql([0, 6, 13, 21, 26]);
     });
 
+    it('handles quotes with field delimiters', () => {
+      let data = `a,"b,c",d\n"e","f"`;
+      let options = {data, rowDelimiter: '\n'};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(2);
+      expect(results.offsets).to.eql([0, 10]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(2);
+      expect(results.ncols).to.eql(3);
+      expect(results.offsets).to.eql([0, 2, 8, 10, 14, 17]);
+    });
+
+    it('handles quotes with row delimiters', () => {
+      let data = `a,"b\nc",d\ne,f`;
+      let options = {data, rowDelimiter: '\n'};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(2);
+      expect(results.offsets).to.eql([0, 10]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(2);
+      expect(results.ncols).to.eql(3);
+      expect(results.offsets).to.eql([0, 2, 8, 10, 12, 13]);
+    });
+
+    it('handles quotes with escaped quotes', () => {
+      let data = `a,"b""c",d\ne,f`;
+      let options = {data, rowDelimiter: '\n'};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(2);
+      expect(results.offsets).to.eql([0, 11]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(2);
+      expect(results.ncols).to.eql(3);
+      expect(results.offsets).to.eql([0, 2, 9, 11, 13, 14]);
+    });
+
+    it('handles setting the quote character', () => {
+      let data = `a,'b'',\nc',d\ne,f`;
+      let options = {data, rowDelimiter: '\n', quote: `'`};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(2);
+      expect(results.offsets).to.eql([0, 13]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(2);
+      expect(results.ncols).to.eql(3);
+      expect(results.offsets).to.eql([0, 2, 11, 13, 15, 16]);
+    });
+
+    it('handles single quoted field', () => {
+      let data = `"a"`;
+      let options = {data, rowDelimiter: '\n'};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(1);
+      expect(results.ncols).to.eql(1);
+      expect(results.offsets).to.eql([0]);
+    });
+
+    it('handles empty quoted field', () => {
+      let data = `a,"",b`;
+      let options = {data, rowDelimiter: '\n'};
+      let results;
+
+      results = parser({...options, columnOffsets: false});
+      expect(results.nrows).to.eql(1);
+      expect(results.offsets).to.eql([0]);
+
+      results = parser({...options, columnOffsets: true});
+      expect(results.nrows).to.eql(1);
+      expect(results.ncols).to.eql(3);
+      expect(results.offsets).to.eql([0, 2, 5]);
+    });
+
   });
 });
 
+// Helpful debugging logging
 // console.log(Array.from(results.offsets));
-// console.log(Array.from(results.offsets).map(i => data[i]));
 // console.log(Array.from(results.offsets).map((i, ind, arr) => data.slice(i, arr[ind + 1])));