Commit 8eb88937 authored by Solly Ross's avatar Solly Ross

Support Skipped Tests and Fix JSHint Issues

The test runner now will not break when Mocha skips tests,
and will properly report them.  Additionally, several JSHint
warnings were fixed, and a `--debug` option was added to see
output from the provider.
parent 466a09f0
...@@ -3,7 +3,7 @@ var path = require('path'); ...@@ -3,7 +3,7 @@ var path = require('path');
var phantom_path = require('phantomjs').path; var phantom_path = require('phantomjs').path;
var casper_path = path.resolve(__dirname, 'node_modules/casperjs/bin/casperjs'); var casper_path = path.resolve(__dirname, 'node_modules/casperjs/bin/casperjs');
process.env['PHANTOMJS_EXECUTABLE'] = phantom_path; process.env.PHANTOMJS_EXECUTABLE = phantom_path;
var casper_opts = { var casper_opts = {
child: { child: {
transport: 'http', transport: 'http',
...@@ -13,7 +13,7 @@ var casper_opts = { ...@@ -13,7 +13,7 @@ var casper_opts = {
logLevel: 'debug', logLevel: 'debug',
verbose: true verbose: true
} }
} };
var provide_emitter = function(file_paths) { var provide_emitter = function(file_paths) {
var spooky = new Spooky(casper_opts, function(err) { var spooky = new Spooky(casper_opts, function(err) {
...@@ -29,22 +29,24 @@ var provide_emitter = function(file_paths) { ...@@ -29,22 +29,24 @@ var provide_emitter = function(file_paths) {
spooky.then([{ path_ind: path_ind }, function() { spooky.then([{ path_ind: path_ind }, function() {
var res_json = { var res_json = {
file_ind: path_ind file_ind: path_ind
} };
res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length }); res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length; });
res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length }); res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length; });
res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length }); res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length; });
res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast)').length }); res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast):not(.pending)').length; });
res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent }); res_json.num_skipped = this.evaluate(function () { return document.querySelectorAll('li.test.pending').length; });
res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent; });
res_json.suites = this.evaluate(function() { res_json.suites = this.evaluate(function() {
var traverse_node = function(elem) { var traverse_node = function(elem) {
var res;
if (elem.classList.contains('suite')) { if (elem.classList.contains('suite')) {
var res = { res = {
type: 'suite', type: 'suite',
name: elem.querySelector('h1').textContent, name: elem.querySelector('h1').textContent,
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0, has_subfailures: elem.querySelectorAll('li.test.fail').length > 0,
} };
var child_elems = elem.querySelector('ul').children; var child_elems = elem.querySelector('ul').children;
res.children = Array.prototype.map.call(child_elems, traverse_node); res.children = Array.prototype.map.call(child_elems, traverse_node);
...@@ -52,15 +54,22 @@ var provide_emitter = function(file_paths) { ...@@ -52,15 +54,22 @@ var provide_emitter = function(file_paths) {
} }
else { else {
var h2_content = elem.querySelector('h2').childNodes; var h2_content = elem.querySelector('h2').childNodes;
var res = { res = {
type: 'test', type: 'test',
text: h2_content[0].textContent, text: h2_content[0].textContent,
} };
if (elem.classList.contains('pass')) { if (elem.classList.contains('pass')) {
res.pass = true; res.pass = true;
res.slow = !elem.classList.contains('fast'); if (elem.classList.contains('pending')) {
res.duration = h2_content[1].textContent; res.slow = false;
res.skipped = true;
}
else {
res.slow = !elem.classList.contains('fast');
res.skipped = false;
res.duration = h2_content[1].textContent;
}
} }
else { else {
res.error = elem.querySelector('pre.error').textContent; res.error = elem.querySelector('pre.error').textContent;
...@@ -68,23 +77,23 @@ var provide_emitter = function(file_paths) { ...@@ -68,23 +77,23 @@ var provide_emitter = function(file_paths) {
return res; return res;
} }
} };
var top_suites = document.querySelectorAll('#mocha-report > li.suite'); var top_suites = document.querySelectorAll('#mocha-report > li.suite');
return Array.prototype.map.call(top_suites, traverse_node); return Array.prototype.map.call(top_suites, traverse_node);
}); });
res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent }); res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent; });
this.emit('test_ready', res_json); this.emit('test_ready', res_json);
}]); }]);
}); });
spooky.run(); spooky.run();
}); });
return spooky; return spooky;
} };
module.exports = { module.exports = {
provide_emitter: provide_emitter, provide_emitter: provide_emitter,
name: 'SpookyJS (CapserJS on PhantomJS)' name: 'SpookyJS (CapserJS on PhantomJS)'
} };
...@@ -5,7 +5,7 @@ var path = require('path'); ...@@ -5,7 +5,7 @@ var path = require('path');
var make_list = function(val) { var make_list = function(val) {
return val.split(','); return val.split(',');
} };
program program
.option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, []) .option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, [])
...@@ -16,6 +16,7 @@ program ...@@ -16,6 +16,7 @@ program
.option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper') .option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper')
.option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with -i)') .option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with -i)')
.option('-o, --output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with -i)') .option('-o, --output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with -i)')
.option('-d, --debug', 'Show debug output (the "console" event) from the provider')
.parse(process.argv); .parse(process.argv);
var file_paths = []; var file_paths = [];
...@@ -26,13 +27,15 @@ if (program.autoInject) { ...@@ -26,13 +27,15 @@ if (program.autoInject) {
temp.track(); temp.track();
var template = { var template = {
header: "<html>\n<head>\n<meta charset='utf-8' />\<link rel='stylesheet' href='node_modules/mocha/mocha.css'/>\n</head>\n<body><div id='mocha'></div>", header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + path.resolve(__dirname, 'node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
script_tag: function(p) { return "<script src='" + p + "'></script>" }, script_tag: function(p) { return "<script src='" + p + "'></script>"; },
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run();\n</script>\n</body>\n</html>" footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run();\n</script>\n</body>\n</html>"
}; };
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/chai/chai.js')); template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/chai/chai.js'));
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/mocha/mocha.js')); template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/mocha/mocha.js'));
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/sinon/pkg/sinon.js'));
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/sinon-chai/lib/sinon-chai.js'));
template.header += "\n<script>mocha.setup('bdd');</script>"; template.header += "\n<script>mocha.setup('bdd');</script>";
...@@ -74,7 +77,7 @@ if (program.outputHtml) { ...@@ -74,7 +77,7 @@ if (program.outputHtml) {
console.warn(error.stack); console.warn(error.stack);
return; return;
} }
cursor cursor
.bold() .bold()
.write(program.tests[path_ind]) .write(program.tests[path_ind])
...@@ -137,8 +140,8 @@ if (!program.outputHtml && !program.generateHtml) { ...@@ -137,8 +140,8 @@ if (!program.outputHtml && !program.generateHtml) {
console.log(''); console.log('');
cursor.write(''+test_json.num_tests+' tests run, ')
cursor cursor
.write(''+test_json.num_tests+' tests run, ')
.green() .green()
.write(''+test_json.num_passes+' passed'); .write(''+test_json.num_passes+' passed');
if (test_json.num_slow > 0) { if (test_json.num_slow > 0) {
...@@ -157,9 +160,16 @@ if (!program.outputHtml && !program.generateHtml) { ...@@ -157,9 +160,16 @@ if (!program.outputHtml && !program.generateHtml) {
cursor cursor
.red() .red()
.write(''+test_json.num_fails+' failed'); .write(''+test_json.num_fails+' failed');
if (test_json.num_skipped > 0) {
cursor
.reset()
.write(', ')
.grey()
.write(''+test_json.num_skipped+' skipped');
}
cursor cursor
.reset() .reset()
.write(' -- duration: '+test_json.duration+"\n"); .write(' -- duration: '+test_json.duration+"s\n");
console.log(''); console.log('');
...@@ -168,7 +178,7 @@ if (!program.outputHtml && !program.generateHtml) { ...@@ -168,7 +178,7 @@ if (!program.outputHtml && !program.generateHtml) {
if (node.type == 'suite') { if (node.type == 'suite') {
if (!node.has_subfailures && !program.printAll) return; if (!node.has_subfailures && !program.printAll) return;
if (indentation == 0) { if (indentation === 0) {
cursor.bold(); cursor.bold();
console.log(node.name); console.log(node.name);
console.log(Array(node.name.length+1).join('-')); console.log(Array(node.name.length+1).join('-'));
...@@ -195,17 +205,25 @@ if (!program.outputHtml && !program.generateHtml) { ...@@ -195,17 +205,25 @@ if (!program.outputHtml && !program.generateHtml) {
cursor.magenta(); cursor.magenta();
console.log('- failed: '+node.text+test_json.replay); console.log('- failed: '+node.text+test_json.replay);
cursor.red(); cursor.red();
console.log(' '+node.error.split("\n")[0]); // the split is to avoid a weird thing where in PhantomJS, we get a stack trace too console.log(' '+node.error.split("\n")[0]); // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
cursor.reset(); cursor.reset();
console.log(''); console.log('');
} }
else if (program.printAll) { else if (program.printAll) {
if (node.slow) cursor.yellow(); if (node.skipped) {
else cursor.green(); cursor
cursor .grey()
.write('- pass: '+node.text) .write('- skipped: '+node.text);
.grey() }
.write(' ('+node.duration+') '); else {
if (node.slow) cursor.yellow();
else cursor.green();
cursor
.write('- pass: '+node.text)
.grey()
.write(' ('+node.duration+') ');
}
/*if (node.slow) cursor.yellow(); /*if (node.slow) cursor.yellow();
else cursor.green();*/ else cursor.green();*/
cursor cursor
...@@ -215,23 +233,25 @@ if (!program.outputHtml && !program.generateHtml) { ...@@ -215,23 +233,25 @@ if (!program.outputHtml && !program.generateHtml) {
console.log(''); console.log('');
} }
} }
} };
for (var i = 0; i < test_json.suites.length; i++) { for (var i = 0; i < test_json.suites.length; i++) {
traverse_tree(0, test_json.suites[i]); traverse_tree(0, test_json.suites[i]);
} }
} }
if (test_json.num_fails == 0) { if (test_json.num_fails === 0) {
cursor.fg.green(); cursor.fg.green();
console.log('all tests passed :-)'); console.log('all tests passed :-)');
cursor.reset(); cursor.reset();
} }
}); });
/*provider.on('console', function(line) { if (program.debug) {
//console.log(line); provider.on('console', function(line) {
});*/ console.log(line);
});
}
/*gprom.finally(function(ph) { /*gprom.finally(function(ph) {
ph.exit(); ph.exit();
......
...@@ -18,16 +18,18 @@ var provide_emitter = function(file_paths) { ...@@ -18,16 +18,18 @@ var provide_emitter = function(file_paths) {
res_json.num_fails = browser.querySelectorAll('li.test.fail').length; res_json.num_fails = browser.querySelectorAll('li.test.fail').length;
res_json.num_passes = browser.querySelectorAll('li.test.pass').length; res_json.num_passes = browser.querySelectorAll('li.test.pass').length;
res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length; res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length;
res_json.num_skipped = browser.querySelectorAll('li.test.pending').length;
res_json.duration = browser.text('li.duration em'); res_json.duration = browser.text('li.duration em');
var traverse_node = function(elem) { var traverse_node = function(elem) {
var classList = elem.className.split(' '); var classList = elem.className.split(' ');
var res;
if (classList.indexOf('suite') > -1) { if (classList.indexOf('suite') > -1) {
var res = { res = {
type: 'suite', type: 'suite',
name: elem.querySelector('h1').textContent, name: elem.querySelector('h1').textContent,
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0 has_subfailures: elem.querySelectorAll('li.test.fail').length > 0
} };
var child_elems = elem.querySelector('ul').children; var child_elems = elem.querySelector('ul').children;
res.children = Array.prototype.map.call(child_elems, traverse_node); res.children = Array.prototype.map.call(child_elems, traverse_node);
...@@ -35,15 +37,22 @@ var provide_emitter = function(file_paths) { ...@@ -35,15 +37,22 @@ var provide_emitter = function(file_paths) {
} }
else { else {
var h2_content = elem.querySelector('h2').childNodes; var h2_content = elem.querySelector('h2').childNodes;
var res = { res = {
type: 'test', type: 'test',
text: h2_content[0].textContent text: h2_content[0].textContent
} };
if (classList.indexOf('pass') > -1) { if (classList.indexOf('pass') > -1) {
res.pass = true; res.pass = true;
res.slow = classList.indexOf('fast') < 0; if (classList.indexOf('pending') > -1) {
res.duration = h2_content[1].textContent; res.slow = false;
res.skipped = true;
}
else {
res.slow = classList.indexOf('fast') < 0;
res.skipped = false;
res.duration = h2_content[1].textContent;
}
} }
else { else {
res.error = elem.querySelector('pre.error').textContent; res.error = elem.querySelector('pre.error').textContent;
...@@ -51,7 +60,7 @@ var provide_emitter = function(file_paths) { ...@@ -51,7 +60,7 @@ var provide_emitter = function(file_paths) {
return res; return res;
} }
} };
var top_suites = browser.querySelectorAll('#mocha-report > li.suite'); var top_suites = browser.querySelectorAll('#mocha-report > li.suite');
res_json.suites = Array.prototype.map.call(top_suites, traverse_node); res_json.suites = Array.prototype.map.call(top_suites, traverse_node);
...@@ -65,9 +74,9 @@ var provide_emitter = function(file_paths) { ...@@ -65,9 +74,9 @@ var provide_emitter = function(file_paths) {
}, Q(new Browser())); }, Q(new Browser()));
return emitter; return emitter;
} };
module.exports = { module.exports = {
provide_emitter: provide_emitter, provide_emitter: provide_emitter,
name: 'ZombieJS' name: 'ZombieJS'
} };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment