5 changed files with 1035 additions and 801 deletions
@ -0,0 +1,306 @@
@@ -0,0 +1,306 @@
|
||||
/** |
||||
Papa Parse 3.0 alpha - core parser function |
||||
(c) 2014 Matthew Holt. |
||||
Not for use in production or redistribution. |
||||
For development of Papa Parse only. |
||||
**/ |
||||
function Parser(config) |
||||
{ |
||||
var self = this; |
||||
var BYTE_ORDER_MARK = "\ufeff"; |
||||
var EMPTY = /^\s*$/; |
||||
|
||||
// Delimiters that are not allowed
|
||||
var _badDelimiters = ["\r", "\n", "\"", BYTE_ORDER_MARK]; |
||||
|
||||
var _input; // The input text being parsed
|
||||
var _delimiter; // The delimiting character
|
||||
var _comments; // Comment character (default '#') or boolean
|
||||
var _step; // The step (streaming) function
|
||||
var _callback; // The callback to invoke when finished
|
||||
var _preview; // Maximum number of lines (not rows) to parse
|
||||
var _ch; // Current character
|
||||
var _i; // Current character's positional index
|
||||
var _inQuotes; // Whether in quotes or not
|
||||
var _lineNum; // Current line number (1-based indexing)
|
||||
var _data; // Parsed data (results)
|
||||
var _errors; // Parse errors
|
||||
var _rowIdx; // Current row index within results (0-based)
|
||||
var _colIdx; // Current col index within result row (0-based)
|
||||
var _aborted; // Abort flag
|
||||
var _paused; // Pause flag
|
||||
|
||||
// Unpack the config object
|
||||
config = config || {}; |
||||
_delimiter = config.delimiter; |
||||
_comments = config.comments; |
||||
_step = config.step; |
||||
_callback = config.complete; |
||||
_preview = config.preview; |
||||
|
||||
// Delimiter integrity check
|
||||
if (typeof _delimiter !== 'string' |
||||
|| _delimiter.length != 1 |
||||
|| _badDelimiters.indexOf(_delimiter) > -1) |
||||
_delimiter = ","; |
||||
|
||||
// Comment character integrity check
|
||||
if (_comments === true) |
||||
_comments = "#"; |
||||
else if (typeof _comments !== 'string' |
||||
|| _comments.length != 1 |
||||
|| _badDelimiters.indexOf(_comments) > -1 |
||||
|| _comments == _delimiter) |
||||
_comments = false; |
||||
|
||||
// Parses delimited text input
|
||||
this.parse = function(input) |
||||
{ |
||||
if (typeof input !== 'string') |
||||
throw "Input must be a string"; |
||||
reset(input); |
||||
return parserLoop(); |
||||
}; |
||||
|
||||
this.pause = function() |
||||
{ |
||||
_paused = true; |
||||
}; |
||||
|
||||
this.resume = function() |
||||
{ |
||||
_paused = false; |
||||
if (_i < _input.length) |
||||
return parserLoop(); |
||||
}; |
||||
|
||||
this.abort = function() |
||||
{ |
||||
_aborted = true; |
||||
}; |
||||
|
||||
function parserLoop() |
||||
{ |
||||
while (_i < _input.length) |
||||
{ |
||||
if (_aborted) break; |
||||
if (_preview > 0 && _rowIdx >= _preview) break; |
||||
if (_paused) return; |
||||
|
||||
if (_ch == '"') |
||||
parseQuotes(); |
||||
else if (_inQuotes) |
||||
parseInQuotes(); |
||||
else |
||||
parseNotInQuotes(); |
||||
|
||||
nextChar(); |
||||
} |
||||
|
||||
return finishParsing(); |
||||
} |
||||
|
||||
function nextChar() |
||||
{ |
||||
_i++; |
||||
_ch = _input[_i]; |
||||
} |
||||
|
||||
function finishParsing() |
||||
{ |
||||
if (_inQuotes) |
||||
addError("Quotes", "MissingQuotes", "Unescaped or mismatched quotes"); |
||||
|
||||
endRow(); // End of input is also end of the last row
|
||||
|
||||
if (typeof _step !== 'function') |
||||
return returnable(); |
||||
else if (typeof _callback === 'function') |
||||
_callback(); |
||||
} |
||||
|
||||
function parseQuotes() |
||||
{ |
||||
if (quotesOnBoundary() && !quotesEscaped()) |
||||
_inQuotes = !_inQuotes; |
||||
else |
||||
{ |
||||
saveChar(); |
||||
if (_inQuotes && quotesEscaped()) |
||||
_i++ |
||||
else |
||||
addError("Quotes", "UnexpectedQuotes", "Unexpected quotes"); |
||||
} |
||||
} |
||||
|
||||
function parseInQuotes() |
||||
{ |
||||
saveChar(); |
||||
if (twoCharLineBreak()) |
||||
{ |
||||
nextChar(); |
||||
saveChar(); |
||||
_lineNum++; |
||||
} |
||||
else if (oneCharLineBreak()) |
||||
_lineNum++; |
||||
} |
||||
|
||||
function parseNotInQuotes() |
||||
{ |
||||
if (_ch == _delimiter) |
||||
newField(); |
||||
else if (twoCharLineBreak()) |
||||
{ |
||||
newRow(); |
||||
nextChar(); |
||||
} |
||||
else if (oneCharLineBreak()) |
||||
newRow(); |
||||
else if (isCommentStart()) |
||||
skipLine(); |
||||
else |
||||
saveChar(); |
||||
} |
||||
|
||||
function isCommentStart() |
||||
{ |
||||
var firstCharOfLine = _i == 0 |
||||
|| oneCharLineBreak(_i-1) |
||||
|| twoCharLineBreak(_i-2); |
||||
return firstCharOfLine && _input[_i] === _comments; |
||||
} |
||||
|
||||
function skipLine() |
||||
{ |
||||
while (!twoCharLineBreak() |
||||
&& !oneCharLineBreak() |
||||
&& _i < _input.length) |
||||
{ |
||||
nextChar(); |
||||
} |
||||
} |
||||
|
||||
function saveChar() |
||||
{ |
||||
_data[_rowIdx][_colIdx] += _ch; |
||||
} |
||||
|
||||
function newField() |
||||
{ |
||||
_data[_rowIdx].push(""); |
||||
_colIdx = _data[_rowIdx].length - 1; |
||||
} |
||||
|
||||
function newRow() |
||||
{ |
||||
endRow(); |
||||
|
||||
_lineNum++; |
||||
_data.push([]); |
||||
_rowIdx = _data.length - 1; |
||||
newField(); |
||||
} |
||||
|
||||
function endRow() |
||||
{ |
||||
trimEmptyLastRow(); |
||||
if (typeof _step === 'function') |
||||
{ |
||||
if (_data[_rowIdx]) |
||||
_step(returnable(), self); |
||||
clearErrorsAndData(); |
||||
} |
||||
} |
||||
|
||||
function trimEmptyLastRow() |
||||
{ |
||||
if (_data[_rowIdx].length == 1 && EMPTY.test(_data[_rowIdx][0])) |
||||
{ |
||||
_data.splice(_rowIdx, 1); |
||||
_rowIdx = _data.length - 1; |
||||
} |
||||
} |
||||
|
||||
function twoCharLineBreak(i) |
||||
{ |
||||
if (typeof i !== 'number') |
||||
i = _i; |
||||
return i < _input.length - 1 && |
||||
((_input[i] == "\r" && _input[i+1] == "\n") |
||||
|| (_input[i] == "\n" && _input[i+1] == "\r")) |
||||
} |
||||
|
||||
function oneCharLineBreak(i) |
||||
{ |
||||
if (typeof i !== 'number') |
||||
i = _i; |
||||
return _input[i] == "\r" || _input[i] == "\n"; |
||||
} |
||||
|
||||
function quotesEscaped() |
||||
{ |
||||
// Quotes as data cannot be on boundary, for example: ,"", are not escaped quotes
|
||||
return !quotesOnBoundary() && _i < _input.length - 1 && _input[_i+1] == '"'; |
||||
} |
||||
|
||||
function quotesOnBoundary() |
||||
{ |
||||
return isBoundary(_i-1) || isBoundary(_i+1); |
||||
} |
||||
|
||||
function isBoundary(i) |
||||
{ |
||||
if (typeof i != 'number') |
||||
i = _i; |
||||
|
||||
var ch = _input[i]; |
||||
|
||||
return (i == -1 || i == _input.length) |
||||
|| (i < _input.length |
||||
&& i > -1 |
||||
&& (ch == _delimiter |
||||
|| ch == "\r" |
||||
|| ch == "\n")); |
||||
} |
||||
|
||||
function addError(type, code, msg) |
||||
{ |
||||
_errors.push({ |
||||
type: type, |
||||
code: code, |
||||
message: msg, |
||||
line: _lineNum, |
||||
row: _rowIdx, |
||||
index: _i |
||||
}); |
||||
} |
||||
|
||||
function reset(input) |
||||
{ |
||||
_input = input; |
||||
_inQuotes = false; |
||||
_lineNum = 1; |
||||
_i = 0; |
||||
clearErrorsAndData(); |
||||
_data = [ [""] ]; // starting parsing requires an empty field
|
||||
_ch = _input[_i]; |
||||
} |
||||
|
||||
function clearErrorsAndData() |
||||
{ |
||||
_data = []; |
||||
_errors = []; |
||||
_rowIdx = 0; |
||||
_colIdx = 0; |
||||
} |
||||
|
||||
function returnable() |
||||
{ |
||||
return { |
||||
data: _data, |
||||
errors: _errors, |
||||
lines: _lineNum |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,172 @@
@@ -0,0 +1,172 @@
|
||||
var passCount = 0; |
||||
var failCount = 0; |
||||
|
||||
$(function() |
||||
{ |
||||
// First, wireup!
|
||||
$('#results').on('click', 'td.rvl', function() |
||||
{ |
||||
var tr = $(this).closest('tr'); |
||||
if (tr.hasClass('collapsed')) |
||||
{ |
||||
$('.revealer', tr).hide(); |
||||
$('.hidden', tr).show(); |
||||
$(this).html("-"); |
||||
} |
||||
else |
||||
{ |
||||
$('.revealer', tr).show(); |
||||
$('.hidden', tr).hide(); |
||||
$(this).html("+"); |
||||
} |
||||
tr.toggleClass('collapsed expanded'); |
||||
}); |
||||
|
||||
$('#expand-all').click(function() |
||||
{ |
||||
$('.collapsed .rvl').click(); |
||||
}); |
||||
|
||||
$('#collapse-all').click(function() |
||||
{ |
||||
$('.expanded .rvl').click(); |
||||
}); |
||||
|
||||
|
||||
|
||||
// Next, run tests and render results!
|
||||
for (var i = 0; i < TESTS.length; i++) |
||||
{ |
||||
var test = TESTS[i]; |
||||
var passed = runTest(test, i); |
||||
if (passed) |
||||
passCount++; |
||||
else |
||||
failCount++; |
||||
} |
||||
|
||||
|
||||
|
||||
// Finally, show the overall status.
|
||||
if (failCount == 0) |
||||
$('#status').addClass('status-pass').html("All <b>"+passCount+"</b> test"+(passCount == 1 ? "" : "s")+" passed"); |
||||
else |
||||
$('#status').addClass('status-fail').html("<b>"+failCount+"</b> test"+(failCount == 1 ? "" : "s")+" failed; <b>"+passCount+"</b> passed"); |
||||
}); |
||||
|
||||
function runTest(test, num) |
||||
{ |
||||
var parser = new Parser(test.config); |
||||
var actual = parser.parse(test.input); |
||||
|
||||
var results = compare(actual.data, actual.errors, test.expected); |
||||
|
||||
var testDescription = (test.description || ""); |
||||
if (testDescription.length > 0) |
||||
testDescription += '<br>'; |
||||
if (test.notes) |
||||
testDescription += '<span class="notes">' + test.notes + '</span>'; |
||||
|
||||
var tr = '<tr class="collapsed" id="test-'+num+'">' |
||||
+ '<td class="rvl">+</td>' |
||||
+ '<td>' + testDescription + '</td>' |
||||
+ passOrFailTd(results.data) |
||||
+ passOrFailTd(results.errors) |
||||
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">' + JSON.stringify(test.config, null, 2) + '</div></td>' |
||||
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">' + revealChars(test.input) + '</div></td>' |
||||
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">data: ' + JSON.stringify(test.expected.data, null, 4) + '\r\nerrors: ' + JSON.stringify(test.expected.errors, null, 4) + '</div></td>' |
||||
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">data: ' + JSON.stringify(actual.data, null, 4) + '\r\nerrors: ' + JSON.stringify(actual.errors, null, 4) + '</div></td>' |
||||
+ '</tr>'; |
||||
|
||||
$('#results').append(tr); |
||||
|
||||
if (!results.data.passed || !results.errors.passed) |
||||
$('#test-'+num+' td.rvl').click(); |
||||
|
||||
return results.data.passed && results.errors.passed |
||||
} |
||||
|
||||
function compare(actualData, actualErrors, expected) |
||||
{ |
||||
var data = compareData(actualData, expected.data); |
||||
var errors = compareErrors(actualErrors, expected.errors); |
||||
return { |
||||
data: data, |
||||
errors: errors |
||||
} |
||||
} |
||||
|
||||
function compareData(actual, expected) |
||||
{ |
||||
var passed = true; |
||||
|
||||
if (actual.length != expected.length) |
||||
passed = false; |
||||
|
||||
for (var row = 0; row < expected.length; row++) |
||||
{ |
||||
if (actual.length != expected.length) |
||||
{ |
||||
passed = false; |
||||
break; |
||||
} |
||||
|
||||
for (var col = 0; col < expected[row].length; col++) |
||||
{ |
||||
if (actual[row].length != expected[row].length) |
||||
{ |
||||
passed = false; |
||||
break; |
||||
} |
||||
|
||||
var expectedVal = expected[row][col]; |
||||
var actualVal = actual[row][col]; |
||||
|
||||
if (actualVal !== expectedVal) |
||||
{ |
||||
passed = false; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// We pass back an object right now, even though it only contains
|
||||
// one value, because we might add details to the test results later
|
||||
// (same with compareErrors below)
|
||||
return { |
||||
passed: passed |
||||
}; |
||||
} |
||||
|
||||
function compareErrors(actual, expected) |
||||
{ |
||||
var passed = JSON.stringify(actual) == JSON.stringify(expected); |
||||
|
||||
return { |
||||
passed: passed |
||||
}; |
||||
} |
||||
|
||||
function passOrFailTd(result) |
||||
{ |
||||
if (result.passed) |
||||
return '<td class="ok">OK</td>'; |
||||
else |
||||
return '<td class="fail">FAIL</td>'; |
||||
} |
||||
|
||||
function revealChars(txt) |
||||
{ |
||||
// Make spaces and tabs more obvious when glancing
|
||||
txt = txt.replace(/( |\t)/ig, '<span class="whitespace-char">$1</span>'); |
||||
|
||||
txt = txt.replace(/(\r\n|\n\r|\r|\n)/ig, '<span class="whitespace-char special-char">$1</span>$1'); |
||||
|
||||
// Now make the line breaks within the spans actually appear on the page
|
||||
txt = txt.replace(/">\r\n<\/span>/ig, '">\\r\\n</span>'); |
||||
txt = txt.replace(/">\n\r<\/span>/ig, '">\\n\\r</span>'); |
||||
txt = txt.replace(/">\r<\/span>/ig, '">\\r</span>'); |
||||
txt = txt.replace(/">\n<\/span>/ig, '">\\n</span>'); |
||||
|
||||
return txt; |
||||
} |
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/* Eric Meyer's Reset CSS v2.0 */ |
||||
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0} |
||||
|
||||
body { |
||||
background: #F0F0F0; |
||||
font: 14px 'Helvetica Neue', sans-serif; |
||||
color: #333; |
||||
padding: 30px 15px; |
||||
} |
||||
|
||||
a { |
||||
color: rgb(0, 142, 236); |
||||
} |
||||
|
||||
a:hover { |
||||
color: rgb(82, 186, 255); |
||||
} |
||||
|
||||
b { |
||||
font-weight: bold; |
||||
} |
||||
|
||||
i { |
||||
font-style: italic; |
||||
} |
||||
|
||||
h1 { |
||||
text-align: center; |
||||
font-weight: bold; |
||||
font-size: 62px; |
||||
margin-bottom: 30px; |
||||
} |
||||
|
||||
.status-pass, |
||||
.status-fail { |
||||
padding: 10px; |
||||
margin-bottom: 30px; |
||||
color: #FFF; |
||||
text-align: center; |
||||
text-transform: uppercase; |
||||
font-size: 18px; |
||||
letter-spacing: 1px; |
||||
font-weight: 100; |
||||
} |
||||
|
||||
.status-pass { |
||||
background: rgb(3, 168, 3); |
||||
} |
||||
|
||||
.status-fail { |
||||
background: #BB0000; |
||||
} |
||||
|
||||
table { |
||||
width: 100%; |
||||
border-collapse: collapse; |
||||
margin-top: 5px; |
||||
} |
||||
|
||||
table th, |
||||
table td { |
||||
padding: 5px; |
||||
border: 1px solid #BBB; |
||||
} |
||||
|
||||
table th { |
||||
color: #000; |
||||
background: #DDD; |
||||
font-weight: bold; |
||||
padding: 10px 5px; |
||||
text-transform: uppercase; |
||||
} |
||||
|
||||
table td { |
||||
background: #FFF; |
||||
color: #555; |
||||
font-size: 14px; |
||||
} |
||||
|
||||
td.ok, |
||||
td.fail { |
||||
text-transform: uppercase; |
||||
font-weight: 300; |
||||
vertical-align: middle; |
||||
text-align: center; |
||||
width: 80px; |
||||
} |
||||
|
||||
td.ok { |
||||
background: rgb(204, 250, 144); |
||||
} |
||||
|
||||
td.fail { |
||||
background: rgb(255, 192, 192); |
||||
} |
||||
|
||||
td.rvl { |
||||
background: #444; |
||||
color: #999; |
||||
vertical-align: middle; |
||||
text-align: center; |
||||
cursor: pointer; |
||||
width: 20px; |
||||
} |
||||
|
||||
td.rvl:hover { |
||||
color: #FFF; |
||||
} |
||||
|
||||
tr.collapsed td.revealable { |
||||
background: #ECECEC; |
||||
vertical-align: middle; |
||||
text-align: center; |
||||
font-family: 'Helvetica Neue', sans-serif; |
||||
text-transform: lowercase; |
||||
color: #AAA; |
||||
} |
||||
|
||||
tr.expanded .revealer { |
||||
font-family: 'Helvetica Neue', sans-serif; |
||||
text-transform: lowercase; |
||||
font-size: 10px; |
||||
background: #FFF; |
||||
position: absolute; |
||||
display: block; |
||||
padding: 3px; |
||||
top: -5px; |
||||
right: -5px; |
||||
} |
||||
|
||||
td .container { |
||||
position: relative; |
||||
} |
||||
|
||||
.notes { |
||||
color: #888; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.pre { |
||||
font-family: Menlo, Monaco, monospace; |
||||
white-space: pre-wrap; |
||||
} |
||||
|
||||
td.pre { |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.hidden { |
||||
display: none; |
||||
} |
||||
|
||||
.special-char { |
||||
color: #78B7E7; |
||||
} |
||||
|
||||
.whitespace-char { |
||||
background: #D5FCFA; |
||||
} |
@ -1,127 +1,33 @@
@@ -1,127 +1,33 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<title>Parser Tests</title> |
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> |
||||
<script src="jquery.parse.js"></script> |
||||
<title>Tests - Papa Parse</title> |
||||
<link rel="stylesheet" href="tests.css"> |
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script> |
||||
<script src="Parser.js"></script> |
||||
<script src="tests.js"></script> |
||||
<style> |
||||
body { |
||||
font-family: sans-serif; |
||||
} |
||||
|
||||
#tmp { |
||||
white-space: pre; |
||||
font-family: 'Menlo', 'Monaco', 'Courier New', monospace; |
||||
font-size: 10px; |
||||
} |
||||
|
||||
#results { |
||||
border-collapse: collapse; |
||||
width: 100%; |
||||
} |
||||
|
||||
#results td { |
||||
vertical-align: top; |
||||
padding: 10px; |
||||
border-bottom: 10px solid white; |
||||
} |
||||
|
||||
#results td div { |
||||
overflow-x: auto; |
||||
} |
||||
|
||||
.count { |
||||
background: #333; |
||||
color: #DDD; |
||||
width: 2em; |
||||
text-align: center; |
||||
} |
||||
|
||||
.input, |
||||
.output { |
||||
width: 25%; |
||||
} |
||||
|
||||
.input { |
||||
background: #DDD; |
||||
} |
||||
|
||||
.config { |
||||
background: #CCC; |
||||
} |
||||
|
||||
.output { |
||||
background: #EEE; |
||||
} |
||||
|
||||
.input code, |
||||
.config code, |
||||
.output code { |
||||
font: 12px/1.5em 'Menlo', 'Monaco', 'Courier New', monospace; |
||||
display: block; |
||||
white-space: pre; |
||||
} |
||||
|
||||
.clr-green, |
||||
.passing { |
||||
color: #475B15; |
||||
} |
||||
|
||||
.clr-red, |
||||
.failing { |
||||
color: #AA0000; |
||||
} |
||||
|
||||
.passing { |
||||
background: #ECF9CC; |
||||
color: #475B15; |
||||
} |
||||
|
||||
.failing { |
||||
background: #FFE8E8; |
||||
} |
||||
|
||||
.failing code { |
||||
font-weight: bold; |
||||
} |
||||
|
||||
hr { |
||||
border: 0; |
||||
background: 0; |
||||
clear: both; |
||||
} |
||||
<script src="test-runner.js"></script> |
||||
</head> |
||||
<body> |
||||
<h1>Papa Parse Tests</h1> |
||||
|
||||
.clr-green { |
||||
color: #79A01E; |
||||
} |
||||
<div id="status"></div> |
||||
|
||||
.clr-red { |
||||
color: #AA0000; |
||||
} |
||||
<a href="javascript:" id="expand-all">Expand all</a> |
||||
· |
||||
<a href="javascript:" id="collapse-all">Collapse all</a> |
||||
<br> |
||||
|
||||
#pass-count, |
||||
#fail-count { |
||||
font-weight: bold; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
SUMMARY |
||||
|
||||
<span class="clr-green"><span id="pass-count">-</span> PASS</span> |
||||
|
||||
<span class="clr-red"><span id="fail-count">-</span> FAIL</span> |
||||
<br><br> |
||||
<table id="results"> |
||||
<tr> |
||||
<th></th> |
||||
<th>Input</th> |
||||
<th colspan="2">Test Case</th> |
||||
<th>Data</th> |
||||
<th>Errors</th> |
||||
<th>Config</th> |
||||
<th>Input</th> |
||||
<th>Expected</th> |
||||
<th>Actual</th> |
||||
</tr> |
||||
</table> |
||||
<div id="output"></div> |
||||
</body> |
||||
</html> |
@ -1,693 +1,384 @@
@@ -1,693 +1,384 @@
|
||||
var passCount = 0, failCount = 0; |
||||
var passing = "passing"; |
||||
var failing = "failing"; |
||||
var RECORD_SEP = String.fromCharCode(30); |
||||
var UNIT_SEP = String.fromCharCode(31); |
||||
|
||||
var recordSep = String.fromCharCode(30); |
||||
var unitSep = String.fromCharCode(31); |
||||
|
||||
var resultSet1 = [ |
||||
var TESTS = [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: 'A,b,c', |
||||
description: "One row", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": 2, |
||||
"F3": "V3" |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['A', 'b', 'c']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: false, dynamicTyping: true }, |
||||
input: 'A,b,c\r\nd,E,f', |
||||
description: "Two rows", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
2, |
||||
"V3" |
||||
] |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['A', 'b', 'c'], ['d', 'E', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: false, dynamicTyping: false }, |
||||
input: 'A,b,c\rd,E,f', |
||||
description: "Two rows, just \\r", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
"2", |
||||
"V3" |
||||
] |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['A', 'b', 'c'], ['d', 'E', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: false }, |
||||
input: 'A,b,c\nd,E,f', |
||||
description: "Two rows, just \\n", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": "2", |
||||
"F3": "V3" |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['A', 'b', 'c'], ['d', 'E', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: "", header: true, dynamicTyping: true }, |
||||
input: 'a, b ,c', |
||||
description: "Whitespace at edges of unquoted field", |
||||
notes: "Extra whitespace should graciously be preserved", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": 2, |
||||
"F3": "V3" |
||||
data: [['a', ' b ', 'c']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
}, |
||||
]; |
||||
|
||||
var tests = [ |
||||
{ |
||||
input: "F1,F2,F3\nV1,2,V3", |
||||
cases: resultSet1 |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\r\nV1,2,V3", |
||||
cases: resultSet1 |
||||
}, |
||||
{ |
||||
input: "F1,\"F2\",F3\r\nV1,2,\"V3\"", |
||||
cases: resultSet1 |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\n\nV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A,"B",C', |
||||
description: "Quoted field", |
||||
expected: { |
||||
data: [['A', 'B', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\r\n\r\nV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A," B ",C', |
||||
description: "Quoted field with extra whitespace on edges", |
||||
expected: { |
||||
data: [['A', ' B ', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\n\rV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A,"B,B",C', |
||||
description: "Quoted field with delimiter", |
||||
expected: { |
||||
data: [['A', 'B,B', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\rV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A,"B\r\nB",C', |
||||
description: "Quoted field with \\r\\n", |
||||
expected: { |
||||
data: [['A', 'B\r\nB', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\r\n \r\nV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A,"B\rB",C', |
||||
description: "Quoted field with \\r", |
||||
expected: { |
||||
data: [['A', 'B\rB', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "\r\nF1,F2,F3\r\nV1,2,V3", |
||||
cases: resultSet1 |
||||
input: 'A,"B\nB",C', |
||||
description: "Quoted field with \\n", |
||||
expected: { |
||||
data: [['A', 'B\nB', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: 'F1,F2,"F3"\n"V1","2",V3', |
||||
cases: resultSet1 |
||||
input: 'A,"B""B""B",C', |
||||
description: "Quoted field with escaped quotes", |
||||
expected: { |
||||
data: [['A', 'B"B"B', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\nV1,2,V3\nV4,V5,V6", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: 'A,"""B""",C', |
||||
description: "Quoted field with escaped quotes at boundaries", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": 2, |
||||
"F3": "V3" |
||||
data: [['A', '"B"', 'C']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V4", |
||||
"F2": "V5", |
||||
"F3": "V6" |
||||
} |
||||
input: 'A, "B" ,C', |
||||
description: "Quoted field with whitespace around quotes", |
||||
notes: "This is malformed input, but it should be parsed gracefully (with errors)", |
||||
expected: { |
||||
data: [['A', ' "B" ', 'C']], |
||||
errors: [ |
||||
{"type": "Quotes", "code": "UnexpectedQuotes", "message": "Unexpected quotes", "line": 1, "row": 0, "index": 3}, |
||||
{"type": "Quotes", "code": "UnexpectedQuotes", "message": "Unexpected quotes", "line": 1, "row": 0, "index": 5} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: false, dynamicTyping: true }, |
||||
input: 'a\tb\tc\r\nd\te\tf', |
||||
config: { delimiter: "\t" }, |
||||
description: "Tab delimiter", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
2, |
||||
"V3" |
||||
], |
||||
[ |
||||
"V4", |
||||
"V5", |
||||
"V6" |
||||
] |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: false, dynamicTyping: false }, |
||||
input: 'a|b|c\r\nd|e|f', |
||||
config: { delimiter: "|" }, |
||||
description: "Pipe delimiter", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
"2", |
||||
"V3" |
||||
], |
||||
[ |
||||
"V4", |
||||
"V5", |
||||
"V6" |
||||
] |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: false }, |
||||
input: 'a'+RECORD_SEP+'b'+RECORD_SEP+'c\r\nd'+RECORD_SEP+'e'+RECORD_SEP+'f', |
||||
config: { delimiter: RECORD_SEP }, |
||||
description: "ASCII 30 delimiter", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": "2", |
||||
"F3": "V3" |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V4", |
||||
"F2": "V5", |
||||
"F3": "V6" |
||||
input: 'a'+UNIT_SEP+'b'+UNIT_SEP+'c\r\nd'+UNIT_SEP+'e'+UNIT_SEP+'f', |
||||
config: { delimiter: UNIT_SEP }, |
||||
description: "ASCII 31 delimiter", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: 'aDELIMbDELIMc', |
||||
config: { delimiter: "DELIM" }, |
||||
description: "Bad delimiter", |
||||
notes: "Should silently default to comma", |
||||
expected: { |
||||
data: [['aDELIMbDELIMc']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\n,2,V3\nV4,V5,V6", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: '# Comment!\r\na,b,c', |
||||
config: { comments: true }, |
||||
description: "Commented line at beginning (comments: true)", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "", |
||||
"F2": 2, |
||||
"F3": "V3" |
||||
data: [['a', 'b', 'c']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V4", |
||||
"F2": "V5", |
||||
"F3": "V6" |
||||
input: 'a,b,c\r\n# Comment\r\nd,e,f', |
||||
config: { comments: true }, |
||||
description: "Commented line in middle (comments: true)", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: 'a,b,c\r\n# Comment', |
||||
config: { comments: true }, |
||||
description: "Commented line at end (comments: true)", |
||||
expected: { |
||||
data: [['a', 'b', 'c']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\n,2,V3,V4\nV5,V6,V7", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: 'a,b,c\r\n!Comment goes here\r\nd,e,f', |
||||
config: { comments: '!' }, |
||||
description: "Comment with non-default character (comments: '!')", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "", |
||||
"F2": 2, |
||||
"F3": "V3", |
||||
"__parsed_extra": [ |
||||
"V4" |
||||
] |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V5", |
||||
"F2": "V6", |
||||
"F3": "V7" |
||||
input: 'a,b,c\r\n=N(Comment)\r\nd,e,f', |
||||
config: { comments: '=N(' }, |
||||
description: "Comment, but bad char specified (comments: \"=N(\")", |
||||
notes: "Should silently disable comment parsing", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['=N(Comment)'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"0": [ |
||||
{ |
||||
"type": "FieldMismatch", |
||||
"code": "TooManyFields", |
||||
"message": "Too many fields: expected 3 fields but parsed 4", |
||||
"line": 2, |
||||
"row": 0, |
||||
"index": 17 |
||||
input: '#commented line\r\n', |
||||
config: { comments: true }, |
||||
description: "Input with only a commented line (comments: true)", |
||||
expected: { |
||||
data: [], |
||||
errors: [] |
||||
} |
||||
], |
||||
"length": 1 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: '#commented line', |
||||
description: "Input with comment without comments enabled", |
||||
expected: { |
||||
data: [['#commented line']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\nV1,2.0,-3.01, V4\n\rV5,\"V\n6\",V7\r,,", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: 'a\r\n b\r\nc', |
||||
description: "Input without comments with line starting with whitespace", |
||||
notes: "\" \" == false, but \" \" !== false, so === comparison is required", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": 2, |
||||
"F3": -3.01, |
||||
"__parsed_extra": [ |
||||
" V4" |
||||
] |
||||
data: [['a'], [' b'], ['c']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V5", |
||||
"F2": "V\n6", |
||||
"F3": "V7" |
||||
input: 'a#b#c\r\n# Comment', |
||||
config: { delimiter: '#', comments: '#' }, |
||||
description: "Comment char same as delimiter", |
||||
notes: "Comment parsing should automatically be silently disabled in this case", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['', ' Comment']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "", |
||||
"F2": "", |
||||
"F3": "" |
||||
input: '\r\na,b,c\r\nd,e,f', |
||||
description: "Blank line at beginning", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"0": [ |
||||
{ |
||||
"type": "FieldMismatch", |
||||
"code": "TooManyFields", |
||||
"message": "Too many fields: expected 3 fields but parsed 4", |
||||
"line": 2, |
||||
"row": 0, |
||||
"index": 25 |
||||
input: 'a,b,c\r\n\r\nd,e,f', |
||||
description: "Blank line in middle", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
], |
||||
"length": 1 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: 'a,b,c\r\nd,e,f\r\n\r\n', |
||||
description: "Blank lines at end", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\nV1,V2,V3\nV5,\"V6,V7", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: 'a,b,c\r\n \r\nd,e,f', |
||||
description: "Blank line in middle with whitespace", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": "V1", |
||||
"F2": "V2", |
||||
"F3": "V3" |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": "V5", |
||||
"F2": "V6,V7" |
||||
input: 'a,b,c\r\n,e,f', |
||||
description: "First field of a line is empty", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"1": [ |
||||
{ |
||||
"type": "FieldMismatch", |
||||
"code": "TooFewFields", |
||||
"message": "Too few fields: expected 3 fields but parsed 2", |
||||
"line": 3, |
||||
"row": 1, |
||||
"index": 27 |
||||
input: 'a,b,c\r\n,e,f', |
||||
description: "First field of a line is empty", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"type": "Quotes", |
||||
"code": "MissingQuotes", |
||||
"message": "Unescaped or mismatched quotes", |
||||
"line": 3, |
||||
"row": 1, |
||||
"index": 27 |
||||
input: 'a,,c\r\n,,', |
||||
description: "Other fields are empty", |
||||
expected: { |
||||
data: [['a', '', 'c'], ['', '', '']], |
||||
errors: [] |
||||
} |
||||
], |
||||
"length": 2 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: '', |
||||
description: "Empty input string", |
||||
expected: { |
||||
data: [], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1,F2,F3\n2,-2, 2\n 2. ,.2, .2 \n-2., -2.0e-5, -.4 ", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: ",", header: true, dynamicTyping: true }, |
||||
input: ',', |
||||
description: "Input is just the delimiter (2 empty fields)", |
||||
expected: { |
||||
"results": { |
||||
"fields": [ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
"rows": [ |
||||
{ |
||||
"F1": 2, |
||||
"F2": -2, |
||||
"F3": 2 |
||||
data: [['', '']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": 2, |
||||
"F2": 0.2, |
||||
"F3": 0.2 |
||||
input: 'Abc def', |
||||
description: "Input is just a string (a single field)", |
||||
expected: { |
||||
data: [['Abc def']], |
||||
errors: [] |
||||
} |
||||
}, |
||||
{ |
||||
"F1": -2, |
||||
"F2": -0.00002, |
||||
"F3": -0.4 |
||||
input: 'a,b,c\r\nd,e,f\r\ng,h,i', |
||||
config: { preview: 0 }, |
||||
description: "Preview 0 rows should default to parsing all", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: 'a,b,c\r\nd,e,f\r\ng,h,i', |
||||
config: { preview: 1 }, |
||||
description: "Preview 1 row", |
||||
expected: { |
||||
data: [['a', 'b', 'c']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: "F1\nV1\nV2\nV3\nV4", |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: "", header: false, dynamicTyping: false }, |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1" |
||||
], |
||||
[ |
||||
"V1" |
||||
], |
||||
[ |
||||
"V2" |
||||
], |
||||
[ |
||||
"V3" |
||||
], |
||||
[ |
||||
"V4" |
||||
] |
||||
], |
||||
"errors": { |
||||
"length": 1, |
||||
"config": [ |
||||
{ |
||||
"type": "Delimiter", |
||||
"code": "UndetectableDelimiter", |
||||
"message": "Unable to auto-detect delimiting character; defaulted to comma", |
||||
"line": 1, |
||||
"row": 0, |
||||
"index": 0 |
||||
input: 'a,b,c\r\nd,e,f\r\ng,h,i', |
||||
config: { preview: 2 }, |
||||
description: "Preview 2 rows", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
"meta": { |
||||
"delimiter": "," |
||||
} |
||||
} |
||||
{ |
||||
input: 'a,b,c\r\nd,e,f\r\ng,h,i', |
||||
config: { preview: 3 }, |
||||
description: "Preview all (3) rows", |
||||
expected: { |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']], |
||||
errors: [] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: ["F1","F2","F3\r\nV1","V2","V3"].join(recordSep), |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: "", header: false, dynamicTyping: false }, |
||||
input: 'a,b,c\r\nd,e,f\r\ng,h,i', |
||||
config: { preview: 4 }, |
||||
description: "Preview more rows than input has", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
"V2", |
||||
"V3" |
||||
], |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "\u001e" |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']], |
||||
errors: [] |
||||
} |
||||
} |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
input: ["F1","F2","F3\r\nV1","V2","V3"].join(unitSep), |
||||
cases: [ |
||||
{ |
||||
config: { delimiter: "", header: false, dynamicTyping: false }, |
||||
input: 'a,b,c\r\nd,e,"f\r\nf",g,h,i', |
||||
config: { preview: 2 }, |
||||
description: "Preview should count rows, not lines", |
||||
expected: { |
||||
"results": [ |
||||
[ |
||||
"F1", |
||||
"F2", |
||||
"F3" |
||||
], |
||||
[ |
||||
"V1", |
||||
"V2", |
||||
"V3" |
||||
], |
||||
], |
||||
"errors": { |
||||
"length": 0 |
||||
}, |
||||
"meta": { |
||||
"delimiter": "\u001f" |
||||
data: [['a', 'b', 'c'], ['d', 'e', 'f\r\nf', 'g', 'h', 'i']], |
||||
errors: [] |
||||
} |
||||
} |
||||
} |
||||
] |
||||
} |
||||
]; |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$(function() |
||||
{ |
||||
var counter = 0; |
||||
for (var i = 0; i < tests.length; i++) |
||||
{ |
||||
var test = tests[i]; |
||||
var input = test.input; |
||||
for (var j = 0; j < test.cases.length; j++) |
||||
{ |
||||
counter++; |
||||
var testCase = test.cases[j]; |
||||
var actual = doTest(input, testCase.config); |
||||
var status = equal(actual, testCase.expected) ? passing : failing; |
||||
render(input, testCase.expected, actual, testCase.config, counter, status); |
||||
} |
||||
} |
||||
|
||||
$('#pass-count').text(passCount); |
||||
$('#fail-count').text(failCount); |
||||
}); |
||||
|
||||
function doTest(input, config) |
||||
{ |
||||
// try
|
||||
// {
|
||||
return $.parse(input, config); |
||||
// }
|
||||
// catch (e)
|
||||
// {
|
||||
// return {exception: e.message, error: e, note: "See console to inspect stack"};
|
||||
// }
|
||||
} |
||||
|
||||
function render(input, expected, actual, config, count, status) |
||||
{ |
||||
if (status == passing) |
||||
passCount++; |
||||
else |
||||
{ |
||||
console.log("TEST " + count +" FAILED."); |
||||
console.log(" Expected:", expected); |
||||
console.log(" Actual:", actual); |
||||
console.log(" Config:", config); |
||||
failCount++; |
||||
} |
||||
|
||||
var html = '<tr>' + |
||||
'<td class="count">'+count+'</td>' + |
||||
'<td class="input"><div><code>'+string(input)+'</code></div></td>' + |
||||
'<td class="config"><div><code>'+string(config)+'</code></div></td>' + |
||||
'<td class="output"><div><code>'+string(expected)+'</code></div></td>' + |
||||
'<td class="output '+status+'"><div><code>'+string(actual)+'</code></div></td>' + |
||||
'</tr>'; |
||||
$('#results').append(html); |
||||
} |
||||
|
||||
function string(obj) |
||||
{ |
||||
return typeof obj === "string" ? obj : JSON.stringify(obj, undefined, 2); |
||||
} |
||||
|
||||
function equal(actual, expected) |
||||
{ |
||||
return string(actual) === string(expected); |
||||
} |
Loading…
Reference in new issue