Commit 2e6988ae 2e6988ae821b3251e063d11ba28af59b0683852a by Nicolas Perriault

added a new `error` event, better error reporting

The new `error` event is basically a hook into PhantomJS' `onError` native
handler.

Tester has been refactored to take part of this new evented strategy to
display better informations in test results.
1 parent a565f7d6
......@@ -233,32 +233,6 @@ phantom.loadCasper = function loadCasper() {
};
/**
* Custom global error handler.
*/
phantom.onError = function phantom_onError(msg, backtrace) {
var c = require('colorizer').create();
var match = /^(.*): __mod_error(.*):: (.*)/.exec(msg);
var notices = [];
if (match && match.length === 4) {
notices.push(' in module ' + match[2]);
notices.push(' NOTICE: errors within modules cannot be backtraced yet.');
msg = match[3];
}
console.error(c.colorize(msg, 'RED_BAR', 80));
notices.forEach(function(notice) {
console.error(c.colorize(notice, 'COMMENT'));
});
backtrace.forEach(function(item) {
var message = require('fs').absolute(item.file) + ":" + c.colorize(item.line, "COMMENT");
if (item['function']) {
message += " in " + c.colorize(item['function'], "PARAMETER");
}
console.error(" " + message);
});
phantom.exit(1);
};
/**
* Initializes the CasperJS Command Line Interface.
*/
phantom.initCasperCli = function initCasperCli() {
......
......@@ -66,6 +66,7 @@ var Casper = function Casper(options) {
// default options
this.defaults = {
clientScripts: [],
exitOnError: true,
logLevel: "error",
httpStatusHandlers: {},
onAlert: null,
......@@ -120,7 +121,32 @@ var Casper = function Casper(options) {
this.steps = [];
this.test = tester.create(this);
// basic event handlers
// init phantomjs error handler
this.initErrorHandler();
this.on('error', function(msg, backtrace) {
var c = colorizer.create();
var match = /^(.*): __mod_error(.*):: (.*)/.exec(msg);
var notices = [];
if (match && match.length === 4) {
notices.push(' in module ' + match[2]);
notices.push(' NOTICE: errors within modules cannot be backtraced yet.');
msg = match[3];
}
console.error(c.colorize(msg, 'RED_BAR', 80));
notices.forEach(function(notice) {
console.error(c.colorize(notice, 'COMMENT'));
});
backtrace.forEach(function(item) {
var message = fs.absolute(item.file) + ":" + c.colorize(item.line, "COMMENT");
if (item['function']) {
message += " in " + c.colorize(item['function'], "PARAMETER");
}
console.error(" " + message);
});
});
// deprecated feature event handler
this.on('deprecated', function onDeprecated(message) {
this.echo('[deprecated] ' + message, 'COMMENT');
});
......@@ -637,6 +663,20 @@ Casper.prototype.getTitle = function getTitle() {
};
/**
* Initializes PhantomJS error handler.
*
*/
Casper.prototype.initErrorHandler = function initErrorHandler() {
var casper = this;
phantom.onError = function phantom_onError(msg, backtrace) {
casper.emit('error', msg, backtrace);
if (casper.options.exitOnError === true) {
casper.exit(1);
}
};
};
/**
* Injects Client-side utilities in current page context.
*
*/
......
......@@ -66,6 +66,15 @@ var Tester = function Tester(casper, options) {
};
// events
casper.on('error', function(msg, backtrace) {
var line = 0;
try {
line = backtrace[0].line;
} catch (e) {}
this.test.uncaughtError(msg, this.test.currentTestFile, line);
this.test.done();
});
casper.on('step.error', function onStepError(e) {
this.test.uncaughtError(e, this.test.currentTestFile);
this.test.done();
......@@ -80,8 +89,11 @@ var Tester = function Tester(casper, options) {
this.exporter.addFailure(fs.absolute(failure.file), failure.message, failure.details || "test failed", failure.type || "unknown");
this.testResults.failures.push(failure);
// special printing
if (failure.type) {
this.comment(' type: ' + failure.type);
}
if (failure.details) {
this.comment(' details: ' + failure.details);
this.comment(' details: ' + failure.details);
}
if (failure.values && Object.keys(failure.values).length > 0) {
for (var name in failure.values) {
......@@ -469,14 +481,7 @@ var Tester = function Tester(casper, options) {
throw e;
}
this.currentTestFile = file;
try {
new Function('casper', phantom.getScriptCode(file))(casper);
} catch (e) {
// do not abort the whole suite, just fail fast displaying the
// caught error and process next suite
this.uncaughtError(e, file);
this.done();
}
phantom.injectJs(file);
};
/**
......@@ -591,7 +596,7 @@ var Tester = function Tester(casper, options) {
type = failure.type || "unknown";
line = ~~failure.line;
message = failure.message;
casper.echo(f('In %s:%d', failure.file, line));
casper.echo(f('In %s:%s', failure.file, line));
casper.echo(f(' %s: %s', type, message || "(no message was entered)"), "COMMENT");
});
};
......@@ -682,12 +687,7 @@ var Tester = function Tester(casper, options) {
this.runTest = function runTest(testFile) {
this.bar(f('Test file: %s', testFile), 'INFO_BAR');
this.running = true; // this.running is set back to false with done()
try {
this.exec(testFile);
} catch (e) {
this.uncaughtError(e, testFile);
this.done();
}
this.exec(testFile);
};
/**
......@@ -722,14 +722,16 @@ var Tester = function Tester(casper, options) {
* Processes an error caught while running tests contained in a given test
* file.
*
* @param Error|String error The error
* @param String file Test file where the error occured
* @param Error|String error The error
* @param String file Test file where the error occured
* @param Number line Line number (optional)
*/
this.uncaughtError = function uncaughtError(error, file) {
this.uncaughtError = function uncaughtError(error, file, line) {
return this.processAssertionResult({
success: false,
type: "uncaughtError",
file: this.currentTestFile,
file: file,
line: ~~line || "unknown",
message: utils.isObject(error) ? error.message : error,
values: {
error: error
......
......@@ -6,7 +6,9 @@ if (!phantom.casperLoaded) {
var fs = require('fs');
var utils = require('utils');
var f = utils.format;
var casper = require('casper').create();
var casper = require('casper').create({
exitOnError: false
});
// Options from cli
casper.options.verbose = casper.cli.get('direct') || false;
......