Browse Source

Merge pull request #1 from mholt/master

pull offical repo master
pull/832/head
Jacky Jiang 5 years ago committed by GitHub
parent
commit
5c2f56c6e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      docs/docs.html
  2. 25
      docs/resources/js/lovers.js
  3. 4
      package.json
  4. 95
      papaparse.js
  5. 4
      papaparse.min.js
  6. 199
      tests/test-cases.js
  7. 2
      tests/verylong-sample.csv

32
docs/docs.html

@ -259,7 +259,7 @@ @@ -259,7 +259,7 @@
delimiter: ",",
header: true,
newline: "\r\n",
skipEmptyLines: false, //or 'greedy',
skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
columns: null //or array of strings
}
</code></pre>
@ -318,7 +318,7 @@ @@ -318,7 +318,7 @@
<code>newline</code>
</td>
<td>
The newline sequence. Must be one of <code>"\r"</code>, <code>"\n"</code>, or <code>"\r\n"</code>.
The character used to determine newline sequence. It defaults to <code>"\r\n"</code>.
</td>
</tr>
<tr>
@ -337,6 +337,14 @@ @@ -337,6 +337,14 @@
If <code>data</code> is an array of objects this option can be used to manually specify the keys (columns) you expect in the objects. If not set the keys of the first objects are used as column.
</td>
</tr>
<tr>
<td>
<code>escapeFormulae</code>
</td>
<td>
If <code>true</code>, field values that begin with <code>=</code>, <code>+</code>, <code>-</code>, or <code>@</code>, will be prepended with a <code>'</code> to defend against <a href="https://www.contextis.com/en/blog/comma-separated-vulnerabilities" target="_blank" rel="noopener">injection attacks</a>, because Excel and LibreOffice will automatically parse such cells as formulae.
</td>
</tr>
</table>
</div>
<div class="clear"></div>
@ -433,8 +441,10 @@ var csv = Papa.unparse({ @@ -433,8 +441,10 @@ var csv = Papa.unparse({
error: undefined,
download: false,
downloadRequestHeaders: undefined,
downloadRequestBody: undefined,
skipEmptyLines: false,
chunk: undefined,
chunkSize: undefined,
fastMode: undefined,
beforeFirstChunk: undefined,
withCredentials: undefined,
@ -499,7 +509,7 @@ var csv = Papa.unparse({ @@ -499,7 +509,7 @@ var csv = Papa.unparse({
<code>transformHeader</code>
</td>
<td>
A function to apply on each header. Requires <code>header</code> to be <code>true</code>. The function receives the header as its first argument.<br>
A function to apply on each header. Requires <code>header</code> to be <code>true</code>. The function receives the header as its first argument and the index as second.<br>
Only available starting with version 5.0.
</td>
</tr>
@ -598,6 +608,14 @@ var csv = Papa.unparse({ @@ -598,6 +608,14 @@ var csv = Papa.unparse({
}</code>
</pre>
</tr>
<tr>
<td>
<code>downloadRequestBody</code>
</td>
<td>
Use POST request on the URL of the download option. The value passed will be set as the body of the request.
</td>
</tr>
<tr>
<td>
<code>skipEmptyLines</code>
@ -614,6 +632,14 @@ var csv = Papa.unparse({ @@ -614,6 +632,14 @@ var csv = Papa.unparse({
A callback function, identical to step, which activates streaming. However, this function is executed after every <i>chunk</i> of the file is loaded and parsed rather than every row. Works only with local and remote files. Do not use both chunk and step callbacks together. For the function signature, see the documentation for the step function.
</td>
</tr>
<tr>
<td>
<code>chunkSize</code>
</td>
<td>
Overrides <code>Papa.LocalChunkSize</code> and <code>Papa.RemoteChunkSize</code>. See <a href="#configurable">configurable</a> section to know the usage of both parameters.
</td>
</tr>
<tr>
<td>
<code>fastMode</code>

25
docs/resources/js/lovers.js

@ -102,6 +102,29 @@ var peopleLovePapa = [ @@ -102,6 +102,29 @@ var peopleLovePapa = [
name: "Hua Explore",
description: "The premier destination for information on Contemporary Chinese Art.",
quote: "Papa makes processing data that galleries send us totally seamless."
},
{
link: "https://monei.net",
name: "MONEI",
description: "Digital payments made easy.",
quote: "With Papa life became much easier for us to manage huge csv payments files of our merchants."
},
{
link: "https://moonmail.io",
name: "MoonMail",
description: "OmniChannel Communication Platform powered by AWS PinPoint",
quote: "Papa makes contact imports a plain sailing."
},
{
link: "https://apps.shopify.com/wholesaler",
name: "Wholesaler for Shopify",
description: "Shopify App to offer Wholesaling within one unique Shopify store",
quote: "Super fast bulk Wholesale product price uploads. Love Papa!."
},
{
link: "https://www.unnitmetaliya.com/sop-sample/",
name: "Visa SOP Sample",
description: "Providing free guide to international students.",
quote: "Use Papa Parse for many of side projects. Super fast and works all the time. Love it!"
}
];

4
package.json

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{
"name": "papaparse",
"version": "5.0.2",
"version": "5.3.0",
"description": "Fast and powerful CSV parser for the browser that supports web workers and streaming large files. Converts CSV to JSON and JSON to CSV.",
"keywords": [
"csv",
@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
"grunt-contrib-uglify": "^3.3.0",
"mocha": "^5.2.0",
"mocha-headless-chrome": "^2.0.1",
"open": "0.0.5",
"open": "7.0.0",
"serve-static": "^1.7.1"
},
"scripts": {

95
papaparse.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/* @license
Papa Parse
v5.0.2
v5.3.0
https://github.com/mholt/PapaParse
License: MIT
*/
@ -282,6 +282,9 @@ License: MIT @@ -282,6 +282,9 @@ License: MIT
/** the columns (keys) we expect when we unparse objects */
var _columns = null;
/** whether to prevent outputting cells that can be parsed as formulae by spreadsheet software (Excel and LibreOffice) */
var _escapeFormulae = false;
unpackConfig();
var quoteCharRegex = new RegExp(escapeRegExp(_quoteChar), 'g');
@ -361,6 +364,9 @@ License: MIT @@ -361,6 +364,9 @@ License: MIT
if (_config.escapeChar !== undefined) {
_escapedQuote = _config.escapeChar + _quoteChar;
}
if (typeof _config.escapeFormulae === 'boolean')
_escapeFormulae = _config.escapeFormulae;
}
@ -447,6 +453,10 @@ License: MIT @@ -447,6 +453,10 @@ License: MIT
if (str.constructor === Date)
return JSON.stringify(str).slice(1, 25);
if (_escapeFormulae === true && typeof str === "string" && (str.match(/^[=+\-@].*$/) !== null)) {
str = "'" + str;
}
var escapedQuoteStr = str.toString().replace(quoteCharRegex, _escapedQuote);
var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
@ -642,7 +652,7 @@ License: MIT @@ -642,7 +652,7 @@ License: MIT
xhr.onerror = bindFunction(this._chunkError, this);
}
xhr.open('GET', this._input, !IS_WORKER);
xhr.open(this._config.downloadRequestBody ? 'POST' : 'GET', this._input, !IS_WORKER);
// Headers can only be set when once the request state is OPENED
if (this._config.downloadRequestHeaders)
{
@ -661,7 +671,7 @@ License: MIT @@ -661,7 +671,7 @@ License: MIT
}
try {
xhr.send();
xhr.send(this._config.downloadRequestBody);
}
catch (err) {
this._chunkError(err.message);
@ -669,8 +679,6 @@ License: MIT @@ -669,8 +679,6 @@ License: MIT
if (IS_WORKER && xhr.status === 0)
this._chunkError();
else
this._start += this._config.chunkSize;
};
this._chunkLoaded = function()
@ -684,7 +692,9 @@ License: MIT @@ -684,7 +692,9 @@ License: MIT
return;
}
this._finished = !this._config.chunkSize || this._start > getFileSize(xhr);
// Use chunckSize as it may be a diference on reponse lentgh due to characters with more than 1 byte
this._start += this._config.chunkSize ? this._config.chunkSize : xhr.responseText.length;
this._finished = !this._config.chunkSize || this._start >= getFileSize(xhr);
this.parseChunk(xhr.responseText);
};
@ -700,7 +710,7 @@ License: MIT @@ -700,7 +710,7 @@ License: MIT
if (contentRange === null) { // no content range, then finish!
return -1;
}
return parseInt(contentRange.substr(contentRange.lastIndexOf('/') + 1));
return parseInt(contentRange.substring(contentRange.lastIndexOf('/') + 1));
}
}
NetworkStreamer.prototype = Object.create(ChunkStreamer.prototype);
@ -789,8 +799,14 @@ License: MIT @@ -789,8 +799,14 @@ License: MIT
{
if (this._finished) return;
var size = this._config.chunkSize;
var chunk = size ? remaining.substr(0, size) : remaining;
remaining = size ? remaining.substr(size) : '';
var chunk;
if(size) {
chunk = remaining.substring(0, size);
remaining = remaining.substring(size);
} else {
chunk = remaining;
remaining = '';
}
this._finished = !remaining;
return this.parseChunk(chunk);
};
@ -1000,7 +1016,7 @@ License: MIT @@ -1000,7 +1016,7 @@ License: MIT
// One goal is to minimize the use of regular expressions...
var MAX_FLOAT = Math.pow(2, 53);
var MIN_FLOAT = -MAX_FLOAT;
var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
var FLOAT = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)(e[-+]?\d+)?\s*$/;
var ISO_DATE = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/;
var self = this;
var _stepCounter = 0; // Number of times step was called (number of rows parsed)
@ -1037,8 +1053,10 @@ License: MIT @@ -1037,8 +1053,10 @@ License: MIT
_stepCounter += results.data.length;
if (_config.preview && _stepCounter > _config.preview)
_parser.abort();
else
else {
_results.data = _results.data[0];
userStep(_results, self);
}
}
};
}
@ -1093,7 +1111,10 @@ License: MIT @@ -1093,7 +1111,10 @@ License: MIT
{
_paused = true;
_parser.abort();
_input = _input.substr(_parser.getCharIndex());
// If it is streaming via "chunking", the reader will start appending correctly already so no need to substring,
// otherwise we can get duplicate content within a row
_input = isFunction(_config.chunk) ? "" : _input.substring(_parser.getCharIndex());
};
this.resume = function()
@ -1104,7 +1125,7 @@ License: MIT @@ -1104,7 +1125,7 @@ License: MIT
} else {
// Bugfix: #636 In case the processing hasn't halted yet
// wait for it to halt in order to resume
setTimeout(this.resume, 3);
setTimeout(self.resume, 3);
}
};
@ -1168,10 +1189,10 @@ License: MIT @@ -1168,10 +1189,10 @@ License: MIT
if (!_results)
return;
function addHeder(header)
function addHeader(header, i)
{
if (isFunction(_config.transformHeader))
header = _config.transformHeader(header);
header = _config.transformHeader(header, i);
_fields.push(header);
}
@ -1179,13 +1200,13 @@ License: MIT @@ -1179,13 +1200,13 @@ License: MIT
if (Array.isArray(_results.data[0]))
{
for (var i = 0; needsHeaderRow() && i < _results.data.length; i++)
_results.data[i].forEach(addHeder);
_results.data[i].forEach(addHeader);
_results.data.splice(0, 1);
}
// if _results.data[0] is not an array, we are in a step where _results.data is the row.
else
_results.data.forEach(addHeder);
_results.data.forEach(addHeader);
}
function shouldApplyDynamicTyping(field) {
@ -1331,7 +1352,7 @@ License: MIT @@ -1331,7 +1352,7 @@ License: MIT
function guessLineEndings(input, quoteChar)
{
input = input.substr(0, 1024 * 1024); // max length 1 MB
input = input.substring(0, 1024 * 1024); // max length 1 MB
// Replace all the text inside quotes
var re = new RegExp(escapeRegExp(quoteChar) + '([^]*?)' + escapeRegExp(quoteChar), 'gm');
input = input.replace(re, '');
@ -1357,12 +1378,15 @@ License: MIT @@ -1357,12 +1378,15 @@ License: MIT
function addError(type, code, msg, row)
{
_results.errors.push({
var error = {
type: type,
code: code,
message: msg,
row: row
});
message: msg
};
if(row !== undefined) {
error.row = row;
}
_results.errors.push(error);
}
}
@ -1449,7 +1473,7 @@ License: MIT @@ -1449,7 +1473,7 @@ License: MIT
cursor += newline.length;
else if (ignoreLastRow)
return returnable();
if (comments && row.substr(0, commentsLen) === comments)
if (comments && row.substring(0, commentsLen) === comments)
continue;
if (stepIsFunction)
{
@ -1529,6 +1553,12 @@ License: MIT @@ -1529,6 +1553,12 @@ License: MIT
continue;
}
if(nextDelim !== -1 && nextDelim < (quoteSearch + 1)) {
nextDelim = input.indexOf(delim, (quoteSearch + 1));
}
if(nextNewline !== -1 && nextNewline < (quoteSearch + 1)) {
nextNewline = input.indexOf(newline, (quoteSearch + 1));
}
// Check up to nextDelim or nextNewline, whichever is closest
var checkUpTo = nextNewline === -1 ? nextDelim : Math.min(nextDelim, nextNewline);
var spacesBetweenQuoteAndDelimiter = extraSpaces(checkUpTo);
@ -1552,7 +1582,7 @@ License: MIT @@ -1552,7 +1582,7 @@ License: MIT
var spacesBetweenQuoteAndNewLine = extraSpaces(nextNewline);
// Closing quote followed by newline or 'unnecessary spaces + newLine'
if (input.substr(quoteSearch + 1 + spacesBetweenQuoteAndNewLine, newlineLen) === newline)
if (input.substring(quoteSearch + 1 + spacesBetweenQuoteAndNewLine, quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen) === newline)
{
row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar));
saveRow(quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen);
@ -1591,7 +1621,7 @@ License: MIT @@ -1591,7 +1621,7 @@ License: MIT
}
// Comment found at start of new line
if (comments && row.length === 0 && input.substr(cursor, commentsLen) === comments)
if (comments && row.length === 0 && input.substring(cursor, cursor + commentsLen) === comments)
{
if (nextNewline === -1) // Comment ends at EOF
return returnable();
@ -1607,7 +1637,7 @@ License: MIT @@ -1607,7 +1637,7 @@ License: MIT
// we check, if we have quotes, because delimiter char may be part of field enclosed in quotes
if (quoteSearch > nextDelim) {
// we have quotes, so we try to find the next delimiter not enclosed in quotes and also next starting quote char
var nextDelimObj = getNextUnqotedDelimiter(nextDelim, quoteSearch, nextNewline);
var nextDelimObj = getNextUnquotedDelimiter(nextDelim, quoteSearch, nextNewline);
// if we have next delimiter char which is not enclosed in quotes
if (nextDelimObj && typeof nextDelimObj.nextDelim !== 'undefined') {
@ -1683,7 +1713,7 @@ License: MIT @@ -1683,7 +1713,7 @@ License: MIT
if (ignoreLastRow)
return returnable();
if (typeof value === 'undefined')
value = input.substr(cursor);
value = input.substring(cursor);
row.push(value);
cursor = inputLen; // important in case parsing is paused
pushRow(row);
@ -1707,11 +1737,10 @@ License: MIT @@ -1707,11 +1737,10 @@ License: MIT
}
/** Returns an object with the results, errors, and meta. */
function returnable(stopped, step)
function returnable(stopped)
{
var isStep = step || false;
return {
data: isStep ? data[0] : data,
data: data,
errors: errors,
meta: {
delimiter: delim,
@ -1726,13 +1755,13 @@ License: MIT @@ -1726,13 +1755,13 @@ License: MIT
/** Executes the user's step function and resets data & errors. */
function doStep()
{
step(returnable(undefined, true));
step(returnable());
data = [];
errors = [];
}
/** Gets the delimiter character, which is not inside the quoted field */
function getNextUnqotedDelimiter(nextDelim, quoteSearch, newLine) {
function getNextUnquotedDelimiter(nextDelim, quoteSearch, newLine) {
var result = {
nextDelim: undefined,
quoteSearch: undefined
@ -1754,7 +1783,7 @@ License: MIT @@ -1754,7 +1783,7 @@ License: MIT
nextQuoteSearch = input.indexOf(quoteChar, nextQuoteSearch + 1);
}
// try to get the next delimiter position
result = getNextUnqotedDelimiter(nextNextDelim, nextQuoteSearch, newLine);
result = getNextUnquotedDelimiter(nextNextDelim, nextQuoteSearch, newLine);
} else {
result = {
nextDelim: nextDelim,

4
papaparse.min.js vendored

File diff suppressed because one or more lines are too long

199
tests/test-cases.js

@ -592,7 +592,7 @@ describe('Core Parser Tests', function() { @@ -592,7 +592,7 @@ describe('Core Parser Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual = new Papa.Parser(test.config).parse(test.input);
assert.deepEqual(JSON.stringify(actual.errors), JSON.stringify(test.expected.errors));
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
});
}
@ -732,6 +732,23 @@ var PARSE_TESTS = [ @@ -732,6 +732,23 @@ var PARSE_TESTS = [
}]
}
},
{
description: "Row with enough fields but blank field in the begining",
input: 'A,B,C\r\n,b1,c1\r\na2,b2,c2',
expected: {
data: [["A", "B", "C"], ['', 'b1', 'c1'], ['a2', 'b2', 'c2']],
errors: []
}
},
{
description: "Row with enough fields but blank field in the begining using headers",
input: 'A,B,C\r\n,b1,c1\r\n,b2,c2',
config: { header: true },
expected: {
data: [{"A": "", "B": "b1", "C": "c1"}, {"A": "", "B": "b2", "C": "c2"}],
errors: []
}
},
{
description: "Row with enough fields but blank field at end",
input: 'A,B,C\r\na,b,',
@ -750,6 +767,15 @@ var PARSE_TESTS = [ @@ -750,6 +767,15 @@ var PARSE_TESTS = [
errors: []
}
},
{
description: "transformHeader accepts and optional index attribute",
input: 'A,B,C\r\na,b,c',
config: { header: true, transformHeader: function(header, i) { return i % 2 ? header.toLowerCase() : header; } },
expected: {
data: [{"A": "a", "b": "b", "C": "c"}],
errors: []
}
},
{
description: "Line ends with quoted field, first field of next line is empty using headers",
input: 'a,b,"c"\r\nd,e,"f"\r\n,"h","i"\r\n,"k","l"',
@ -1464,6 +1490,22 @@ var PARSE_TESTS = [ @@ -1464,6 +1490,22 @@ var PARSE_TESTS = [
data: [['a', 'b'], ['c', 'd'], [' , ', ','], ['" "', '""']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and next delimiter and contains delimiter",
input: 'A,",B" ,C,D\nE,F,G,H',
expected: {
data: [['A', ',B', 'C', 'D'],['E', 'F', 'G', 'H']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and newline and contains newline",
input: 'a,b,"c\n" \nd,e,f',
expected: {
data: [['a', 'b', 'c\n'], ['d', 'e', 'f']],
errors: []
}
}
];
@ -1475,7 +1517,7 @@ describe('Parse Tests', function() { @@ -1475,7 +1517,7 @@ describe('Parse Tests', function() {
if (test.expected.meta) {
assert.deepEqual(actual.meta, test.expected.meta);
}
assert.deepEqual(JSON.stringify(actual.errors), JSON.stringify(test.expected.errors));
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
});
}
@ -1556,7 +1598,7 @@ describe('Parse Async Tests', function() { @@ -1556,7 +1598,7 @@ describe('Parse Async Tests', function() {
var config = test.config;
config.complete = function(actual) {
assert.deepEqual(JSON.stringify(actual.errors), JSON.stringify(test.expected.errors));
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
done();
};
@ -1809,7 +1851,36 @@ var UNPARSE_TESTS = [ @@ -1809,7 +1851,36 @@ var UNPARSE_TESTS = [
input: [{a: 'foo', b: '"quoted"'}],
config: {header: false},
expected: 'foo,"""quoted"""'
}
},
{
description: "Escape formulae",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true },
expected: 'Col1,Col2,Col3\r\n\'=danger,\'@danger,safe\r\nsafe=safe,\'+danger,"\'-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
},
{
description: "Don't escape formulae by default",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
expected: 'Col1,Col2,Col3\r\n=danger,@danger,safe\r\nsafe=safe,+danger,"-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
},
{
description: "Escape formulae with forced quotes",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quotes: true },
expected: '"Col1","Col2","Col3"\r\n"\'=danger","\'@danger","safe"\r\n"safe=safe","\'+danger","\'-danger, danger"\r\n"\'+safe","\'@safe","safe, safe"'
},
{
description: "Escape formulae with single-quote quoteChar and escapeChar",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quoteChar: "'", escapeChar: "'" },
expected: 'Col1,Col2,Col3\r\n\'\'=danger,\'\'@danger,safe\r\nsafe=safe,\'\'+danger,\'\'\'-danger, danger\'\r\n\'\'+safe,\'\'@safe,\'safe, safe\''
},
{
description: "Escape formulae with single-quote quoteChar and escapeChar and forced quotes",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quotes: true, quoteChar: "'", escapeChar: "'" },
expected: '\'Col1\',\'Col2\',\'Col3\'\r\n\'\'\'=danger\',\'\'\'@danger\',\'safe\'\r\n\'safe=safe\',\'\'\'+danger\',\'\'\'-danger, danger\'\r\n\'\'\'+safe\',\'\'\'@safe\',\'safe, safe\''
},
];
describe('Unparse Tests', function() {
@ -1881,6 +1952,108 @@ var CUSTOM_TESTS = [ @@ -1881,6 +1952,108 @@ var CUSTOM_TESTS = [
});
}
},
{
description: "Pause and resume works for chunks with NetworkStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
Papa.parse(BASE_PATH + "verylong-sample.csv", {
download: true,
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
}
},
{
description: "Pause and resume works for chunks with FileStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
var xhr = new XMLHttpRequest();
xhr.onload = function() {
Papa.parse(new File([xhr.responseText], './verylong-sample.csv'), {
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
};
xhr.open("GET", BASE_PATH + "verylong-sample.csv");
try {
xhr.send();
} catch (err) {
callback(err);
return;
}
}
},
{
description: "Pause and resume works for chunks with StringStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
// Test also with string as byte size may be diferent
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
var xhr = new XMLHttpRequest();
xhr.onload = function() {
Papa.parse(xhr.responseText, {
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
};
xhr.open("GET", BASE_PATH + "verylong-sample.csv");
try {
xhr.send();
} catch (err) {
callback(err);
return;
}
}
},
{
description: "Complete is called with all results if neither step nor chunk is defined",
expected: [['A', 'b', 'c'], ['d', 'E', 'f'], ['G', 'h', 'i']],
@ -1973,6 +2146,22 @@ var CUSTOM_TESTS = [ @@ -1973,6 +2146,22 @@ var CUSTOM_TESTS = [
});
}
},
{
description: "Data is correctly parsed with steps when skipping empty lines",
expected: [['A', 'b', 'c'], ['d', 'E', 'f']],
run: function(callback) {
var data = [];
Papa.parse('A,b,c\n\nd,E,f', {
skipEmptyLines: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Step is called with the contents of the row",
expected: ['A', 'b', 'c'],
@ -2368,7 +2557,7 @@ describe('Custom Tests', function() { @@ -2368,7 +2557,7 @@ describe('Custom Tests', function() {
this.timeout(test.timeout);
}
test.run(function(actual) {
assert.deepEqual(JSON.stringify(actual), JSON.stringify(test.expected));
assert.deepEqual(actual, test.expected);
done();
});
});

2
tests/verylong-sample.csv

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
placeholder,meaning of life,TLD
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
"Lorem ipsum dolor sit",42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC

1 placeholder meaning of life TLD
2 Lorem ipsum dolor sit 42 ABC
3 Etiam a dolor vitae est vestibulum 84 DEF
4 Lorem ipsum dolor sit 42 ABC
5 Etiam a dolor vitae est vestibulum 84 DEF
6 Etiam a dolor vitae est vestibulum 84 DEF
7 Lorem ipsum dolor sit 42 ABC
Loading…
Cancel
Save