Browse Source

Use "mocha" for browser tests

pull/233/head
Tobias Bieniek 10 years ago
parent
commit
efafabafdb
  1. 485
      tests/test-runner.js
  2. 173
      tests/tests.css
  3. 112
      tests/tests.html

485
tests/test-runner.js

@ -1,448 +1,101 @@ @@ -1,448 +1,101 @@
var passCount = 0;
var failCount = 0;
var testCount = 0;
var assert = chai.assert;
$(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()
{
var $testGroup = $(this).closest('.test-group');
$('.collapsed .rvl', $testGroup).click();
});
$('.collapse-all').click(function()
{
var $testGroup = $(this).closest('.test-group');
$('.expanded .rvl', $testGroup).click();
});
function asyncDone()
{
// 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");
// Generates all tests from CORE_PARSER_TESTS in test-cases.js
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.data, test.expected.data);
});
}
// Next, run tests and render results!
runCoreParserTests();
runParseTests(asyncDone);
runUnparseTests();
runCustomTests(asyncDone);
for (var i = 0; i < CORE_PARSER_TESTS.length; i++) {
generateTest(CORE_PARSER_TESTS[i]);
}
});
// Executes all tests in CORE_PARSER_TESTS from test-cases.js
// and renders results in the table.
function runCoreParserTests()
{
for (var i = 0; i < CORE_PARSER_TESTS.length; i++)
{
var test = CORE_PARSER_TESTS[i];
var passed = runTest(test);
if (passed)
passCount++;
else
failCount++;
}
function runTest(test)
{
var actual = new Papa.Parser(test.config).parse(test.input);
var results = compare(actual.data, actual.errors, test.expected);
displayResults('#tests-for-core-parser', test, actual, results);
return results.data.passed && results.errors.passed
// Generates all tests from PARSE_TESTS in test-cases.js
describe('Parse Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual = Papa.parse(test.input, test.config);
assert.deepEqual(JSON.stringify(actual.errors), JSON.stringify(test.expected.errors));
assert.deepEqual(actual.data, test.expected.data);
});
}
}
// Executes all tests in PARSE_TESTS from test-cases.js
// and renders results in the table.
function runParseTests(asyncDone)
{
for (var i = 0; i < PARSE_TESTS.length; i++)
{
var test = PARSE_TESTS[i];
var passed = runTest(test);
if (passed)
passCount++;
else
failCount++;
for (var i = 0; i < PARSE_TESTS.length; i++) {
generateTest(PARSE_TESTS[i]);
}
});
var asyncRemaining = 0;
PARSE_ASYNC_TESTS.forEach(function(test)
{
if (test.disabled)
return;
asyncRemaining++;
var config = test.config;
config.complete = function(actual)
{
var results = compare(actual.data, actual.errors, test.expected);
displayResults("#tests-for-parse", test, actual, results);
if (results.data.passed && results.errors.passed) {
passCount++;
} else {
failCount++;
}
if (--asyncRemaining === 0) {
asyncDone();
}
};
config.error = function(err)
{
failCount++;
displayResults("#tests-for-parse", test, {data:[],errors:err}, test.expected);
if (--asyncRemaining === 0) {
asyncDone();
}
};
Papa.parse(test.input, config);
});
function runTest(test)
{
var actual = Papa.parse(test.input, test.config);
var results = compare(actual.data, actual.errors, test.expected);
displayResults('#tests-for-parse', test, actual, results);
return results.data.passed && results.errors.passed
}
}
function displayResults(tableId, test, actual, results)
{
var testId = testCount++;
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-'+testId+'">'
+ '<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>';
$(tableId+' .results').append(tr);
if (!results.data.passed || !results.errors.passed)
$('#test-'+testId+' td.rvl').click();
}
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;
else
{
// The order is important, so we go through manually before using stringify to check everything else
for (var row = 0; row < expected.length; row++)
{
if (actual[row].length != expected[row].length)
{
passed = false;
break;
}
for (var col = 0; col < expected[row].length; col++)
{
var expectedVal = expected[row][col];
var actualVal = actual[row][col];
// Generates all tests from PARSE_ASYNC_TESTS in test-cases.js
describe('Parse Async Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function(done) {
var config = test.config;
if (actualVal !== expectedVal)
{
passed = false;
break;
}
}
}
}
config.complete = function(actual) {
assert.deepEqual(JSON.stringify(actual.errors), JSON.stringify(test.expected.errors));
assert.deepEqual(actual.data, test.expected.data);
done();
};
if (passed) // final check will catch any other differences
passed = JSON.stringify(actual) == JSON.stringify(expected);
config.error = function(err) {
throw err;
};
// 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
};
Papa.parse(test.input, config);
});
}
function compareErrors(actual, expected)
{
var passed = JSON.stringify(actual) == JSON.stringify(expected);
return {
passed: passed
};
for (var i = 0; i < PARSE_ASYNC_TESTS.length; i++) {
generateTest(PARSE_ASYNC_TESTS[i]);
}
}
});
// Executes all tests in UNPARSE_TESTS from test-cases.js
// and renders results in the table.
function runUnparseTests()
{
for (var i = 0; i < UNPARSE_TESTS.length; i++)
{
var test = UNPARSE_TESTS[i];
var passed = runTest(test);
if (passed)
passCount++;
else
failCount++;
}
function runTest(test)
{
var actual;
// Generates all tests from UNPARSE_TESTS in test-cases.js
describe('Unparse Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual;
try
{
actual = Papa.unparse(test.input, test.config);
}
catch (e)
{
if (e instanceof Error) {
throw e;
try {
actual = Papa.unparse(test.input, test.config);
} catch (e) {
if (e instanceof Error) {
throw e;
}
actual = e;
}
actual = e;
}
var testId = testCount++;
var results = compare(actual, 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-'+testId+'">'
+ '<td class="rvl">+</td>'
+ '<td>' + testDescription + '</td>'
+ passOrFailTd(results)
+ '<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">' + JSON.stringify(test.input, null, 4) + '</div></td>'
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">' + revealChars(test.expected) + '</div></td>'
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">' + revealChars(actual) + '</div></td>'
+ '</tr>';
$('#tests-for-unparse .results').append(tr);
if (!results.passed)
$('#test-' + testId + ' td.rvl').click();
return results.passed;
}
function compare(actual, expected)
{
return {
passed: actual === expected
};
}
}
// Executes all tests in CUSTOM_TESTS from test-cases.js
// and renders results in the table.
function runCustomTests(asyncDone)
{
var asyncRemaining = 0;
for (var i = 0; i < CUSTOM_TESTS.length; i++)
{
runTest(CUSTOM_TESTS[i]);
assert.strictEqual(actual, test.expected);
});
}
function runTest(test)
{
if (test.disabled)
return;
asyncRemaining++;
try
{
displayAsyncTest(test);
}
catch (e)
{
displayResults(test, e);
}
for (var i = 0; i < UNPARSE_TESTS.length; i++) {
generateTest(UNPARSE_TESTS[i]);
}
});
function displayAsyncTest(test)
{
var testId = testCount++;
test.testId = testId;
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-'+testId+'">'
+ '<td class="rvl">+</td>'
+ '<td>' + testDescription + '</td>'
+ '<td class="status pending">pending</td>'
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden">' + test.expected + '</div></td>'
+ '<td class="revealable pre"><div class="revealer">condensed</div><div class="hidden actual"></div></td>'
+ '</tr>';
$('#custom-tests .results').append(tr);
test.run(function(actual)
{
displayAsyncResults(test, actual);
// Generates all tests from CUSTOM_TESTS in test-cases.js
describe('Custom Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function(done) {
test.run(function (actual) {
assert.deepEqual(JSON.stringify(actual), JSON.stringify(test.expected));
done();
});
});
setTimeout(function()
{
if (test.complete) return;
displayAsyncResults(test, '(incomplete)');
}, 2000);
}
function displayAsyncResults(test, actual)
{
var testId = test.testId;
if (test.complete)
{
asyncRemaining++;
actual = '(multiple results from test)';
}
test.complete = true;
var results = compare(actual, test.expected);
var tr = $('#test-'+testId);
tr.find('.actual').text(actual);
var status = $(passOrFailTd(results));
var oldStatus = tr.find('.status');
oldStatus.attr('class', status.attr('class'));
oldStatus.text(status.text());
if (!results.passed)
$('#test-' + testId + ' td.rvl').click();
if (results.passed)
passCount++;
else
failCount++;
if (--asyncRemaining === 0)
asyncDone();
}
function compare(actual, expected)
{
return {
passed: JSON.stringify(actual) === JSON.stringify(expected)
};
for (var i = 0; i < CUSTOM_TESTS.length; i++) {
generateTest(CUSTOM_TESTS[i]);
}
}
// Makes a TD tag with OK or FAIL depending on test result
function passOrFailTd(result)
{
if (result.passed)
return '<td class="status ok">OK</td>';
else
return '<td class="status fail">FAIL</td>';
}
// Reveals some hidden, whitespace, or invisible characters
function revealChars(txt)
{
if (typeof txt != 'string')
return '(file)';
// 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');
// Make UNIT_SEP and RECORD_SEP characters visible
txt = txt.replace(/(\u001e|\u001f)/ig, '<span class="special-char">$1</span>$1');
// Now make the whitespace and invisible characters
// 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>');
txt = txt.replace(/">\u001e<\/span>/ig, '">\\u001e</span>');
txt = txt.replace(/">\u001f<\/span>/ig, '">\\u001f</span>');
return txt;
}
});

173
tests/tests.css

@ -1,173 +0,0 @@ @@ -1,173 +0,0 @@
/* 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;
}
h2 {
text-align: center;
font-weight: bold;
font-size: 26px;
margin-bottom: 20px;
}
.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;
}
.test-group {
margin-bottom: 50px;
}
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.status {
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.pending {
background: rgb(255, 255, 150);
}
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;
}

112
tests/tests.html

@ -1,113 +1,25 @@ @@ -1,113 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Papa Parse Tests</title>
<meta charset="utf-8">
<link rel="stylesheet" href="tests.css">
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="../papaparse.js"></script>
<script src="http://chaijs.com/chai.js"></script>
<script src="../node_modules/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="test-cases.js"></script>
<script src="test-runner.js"></script>
</head>
<body>
<h1>Papa Parse Tests</h1>
<div id="status"></div>
<div class="test-group" id="tests-for-core-parser">
<h2>Core Parser Tests</h2>
<a href="javascript:" class="expand-all">Expand all</a>
&middot;
<a href="javascript:" class="collapse-all">Collapse all</a>
<br>
<table class="results">
<tr>
<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>
<div class="test-group" id="tests-for-parse">
<h2>Papa.parse() Wrapper Tests</h2>
<a href="javascript:" class="expand-all">Expand all</a>
&middot;
<a href="javascript:" class="collapse-all">Collapse all</a>
<br>
<table class="results">
<tr>
<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>
<div class="test-group" id="tests-for-unparse">
<h2>Papa.unparse() Tests</h2>
<a href="javascript:" class="expand-all">Expand all</a>
&middot;
<a href="javascript:" class="collapse-all">Collapse all</a>
<br>
<table class="results">
<tr>
<th colspan="2">Test Case</th>
<th>Data</th>
<th>Config</th>
<th>Input</th>
<th>Expected</th>
<th>Actual</th>
</tr>
</table>
</div>
<div class="test-group" id="custom-tests">
<h2>Miscellaneous Tests</h2>
<a href="javascript:" class="expand-all">Expand all</a>
&middot;
<a href="javascript:" class="collapse-all">Collapse all</a>
<br>
<table class="results">
<tr>
<th colspan="2">Test Case</th>
<th>Data</th>
<th>Expected</th>
<th>Actual</th>
</tr>
</table>
</div>
<div id="mocha"></div>
<script>
mocha.checkLeaks();
mocha.globals(['jQuery']);
mocha.run();
</script>
</body>
</html>

Loading…
Cancel
Save