Commit a1c41aef a1c41aef5ffae009722c66e48b28d66dcfef61ab by Nicolas Perriault

error handling now mainly relies on phantom.onError()

1 parent aaf83a53
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
28 * 28 *
29 */ 29 */
30 30
31 /**
32 * Loads and initialize the CasperJS environment.
33 */
31 phantom.loadCasper = function loadCasper() { 34 phantom.loadCasper = function loadCasper() {
32 // Patching fs 35 // Patching fs
33 // TODO: watch for these methods being implemented in official fs module 36 // TODO: watch for these methods being implemented in official fs module
...@@ -78,9 +81,6 @@ phantom.loadCasper = function loadCasper() { ...@@ -78,9 +81,6 @@ phantom.loadCasper = function loadCasper() {
78 // Embedded, up-to-date, validatable & controlable CoffeeScript 81 // Embedded, up-to-date, validatable & controlable CoffeeScript
79 phantom.injectJs(fs.pathJoin(phantom.casperPath, 'modules', 'vendors', 'coffee-script.js')); 82 phantom.injectJs(fs.pathJoin(phantom.casperPath, 'modules', 'vendors', 'coffee-script.js'));
80 83
81 // Index of file sources, for error localization
82 phantom.sourceIds = {};
83
84 // custom global CasperError 84 // custom global CasperError
85 window.CasperError = function CasperError(msg) { 85 window.CasperError = function CasperError(msg) {
86 Error.call(this); 86 Error.call(this);
...@@ -97,34 +97,6 @@ phantom.loadCasper = function loadCasper() { ...@@ -97,34 +97,6 @@ phantom.loadCasper = function loadCasper() {
97 // standard Error prototype inheritance 97 // standard Error prototype inheritance
98 window.CasperError.prototype = Object.getPrototypeOf(new Error()); 98 window.CasperError.prototype = Object.getPrototypeOf(new Error());
99 99
100 // Stack formatting
101 window.CasperError.prototype.formatStack = function formatStack() {
102 var location = this.fileName || phantom.sourceIds[this.sourceId] || "unknown";
103 location += ':' + (this.line ? this.line : 0);
104 return this.toString() + '\n ' + (this._from || "anonymous") + '() at ' + location;
105 };
106
107 /**
108 * Adding pseudo stack traces to CasperError
109 * Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/
110 * TODO: remove when phantomjs has js engine upgrade
111 */
112 if (!new CasperError().hasOwnProperty('stack')) {
113 Object.defineProperty(CasperError.prototype, 'stack', {
114 set: function set(string) {
115 this._stack = string;
116 },
117 get: function get() {
118 if (this._stack) {
119 return this._stack;
120 }
121 return this.formatStack();
122 },
123 configurable: true,
124 enumerable: true
125 });
126 }
127
128 // CasperJS version, extracted from package.json - see http://semver.org/ 100 // CasperJS version, extracted from package.json - see http://semver.org/
129 phantom.casperVersion = (function getVersion(path) { 101 phantom.casperVersion = (function getVersion(path) {
130 var parts, patchPart, pkg, pkgFile; 102 var parts, patchPart, pkg, pkgFile;
...@@ -167,46 +139,12 @@ phantom.loadCasper = function loadCasper() { ...@@ -167,46 +139,12 @@ phantom.loadCasper = function loadCasper() {
167 phantom.getScriptCode = function getScriptCode(file, onError) { 139 phantom.getScriptCode = function getScriptCode(file, onError) {
168 var scriptCode = fs.read(file); 140 var scriptCode = fs.read(file);
169 if (/\.coffee$/i.test(file)) { 141 if (/\.coffee$/i.test(file)) {
170 try { 142 scriptCode = CoffeeScript.compile(scriptCode);
171 scriptCode = CoffeeScript.compile(scriptCode);
172 } catch (e) {
173 this.processScriptError(e, file, onError);
174 }
175 } 143 }
176 // trick to locate source file location on error
177 scriptCode += ";var __fe__ = new CasperError('__sourceId__')";
178 scriptCode += ";__fe__.fileName = '" + file.replace(/\\+/g, '/') + "'";
179 scriptCode += ";throw __fe__;";
180 return scriptCode; 144 return scriptCode;
181 }; 145 };
182 146
183 /** 147 /**
184 * Processes a given thrown Error; handles special cases and provides an
185 * optional callback argument.
186 *
187 * By default, the standard behavior on uncaught error is to print the
188 * error stack trace to the console and exit PhantomJS.
189 *
190 * @param Error error The Error instance
191 * @param String file A file path to associate to this error
192 * @param Function callback An optional callback
193 */
194 phantom.processScriptError = function processScriptError(error, file, callback) {
195 if (error.sourceId && !this.sourceIds.hasOwnProperty(error.sourceId)) {
196 this.sourceIds[error.sourceId] = file;
197 }
198 if (error.message === "__sourceId__") {
199 return;
200 }
201 if (typeof callback === "function") {
202 callback(error, file);
203 } else {
204 console.error(error.stack);
205 this.exit(1);
206 }
207 };
208
209 /**
210 * Patching require() to allow loading of other modules than PhantomJS' 148 * Patching require() to allow loading of other modules than PhantomJS'
211 * builtin ones. 149 * builtin ones.
212 * Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/ 150 * Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/
...@@ -266,13 +204,9 @@ phantom.loadCasper = function loadCasper() { ...@@ -266,13 +204,9 @@ phantom.loadCasper = function loadCasper() {
266 if (file in requireCache) { 204 if (file in requireCache) {
267 return requireCache[file].exports; 205 return requireCache[file].exports;
268 } 206 }
269 try { 207 var scriptCode = phantom.getScriptCode(file);
270 var scriptCode = phantom.getScriptCode(file); 208 var fn = new Function('require', 'module', 'exports', scriptCode);
271 var fn = new Function('require', 'module', 'exports', scriptCode); 209 fn(_require, module, module.exports);
272 fn(_require, module, module.exports);
273 } catch (e) {
274 phantom.processScriptError(e, file);
275 }
276 requireCache[file] = module; 210 requireCache[file] = module;
277 return module.exports; 211 return module.exports;
278 }; 212 };
...@@ -288,6 +222,27 @@ phantom.loadCasper = function loadCasper() { ...@@ -288,6 +222,27 @@ phantom.loadCasper = function loadCasper() {
288 phantom.casperLoaded = true; 222 phantom.casperLoaded = true;
289 }; 223 };
290 224
225 /**
226 * Custom global error handler.
227 */
228 phantom.onError = function phantom_onError(msg, backtrace) {
229 var c = require('colorizer').create();
230 if (msg) {
231 console.error(c.colorize(msg, 'RED_BAR', 80));
232 }
233 backtrace.forEach(function(item) {
234 var message = require('fs').absolute(item.file) + ":" + c.colorize(item.line, "COMMENT");
235 if (item['function']) {
236 message += " in " + c.colorize(item['function'], "PARAMETER");
237 }
238 console.error(" " + message);
239 });
240 phantom.exit(1);
241 };
242
243 /**
244 * Initializes the CasperJS Command Line Interface.
245 */
291 phantom.initCasperCli = function initCasperCli() { 246 phantom.initCasperCli = function initCasperCli() {
292 var fs = require("fs"); 247 var fs = require("fs");
293 248
...@@ -317,16 +272,11 @@ phantom.initCasperCli = function initCasperCli() { ...@@ -317,16 +272,11 @@ phantom.initCasperCli = function initCasperCli() {
317 phantom.exit(1); 272 phantom.exit(1);
318 } 273 }
319 274
320
321 // filter out the called script name from casper args 275 // filter out the called script name from casper args
322 phantom.casperArgs.drop(phantom.casperScript); 276 phantom.casperArgs.drop(phantom.casperScript);
323 277
324 // passed casperjs script execution 278 // passed casperjs script execution
325 try { 279 phantom.injectJs(phantom.casperScript);
326 new Function(phantom.getScriptCode(phantom.casperScript))();
327 } catch (e) {
328 phantom.processScriptError(e, phantom.casperScript);
329 }
330 }; 280 };
331 281
332 if (!phantom.casperLoaded) { 282 if (!phantom.casperLoaded) {
......
...@@ -366,13 +366,10 @@ var Tester = function Tester(casper, options) { ...@@ -366,13 +366,10 @@ var Tester = function Tester(casper, options) {
366 try { 366 try {
367 new Function('casper', phantom.getScriptCode(file))(casper); 367 new Function('casper', phantom.getScriptCode(file))(casper);
368 } catch (e) { 368 } catch (e) {
369 var self = this; 369 // do not abort the whole suite, just fail fast displaying the
370 phantom.processScriptError(e, file, function onTestScriptError(error) { 370 // caught error and process next suite
371 // do not abort the whole suite, just fail fast displaying the 371 this.fail(e);
372 // caught error and process next suite 372 this.done();
373 self.fail(e);
374 self.done();
375 });
376 } 373 }
377 }; 374 };
378 375
......
1 var error;
2
3 function foo() {
4 bar();
5 }
6
7 function bar() {
8 throw new CasperError('bar');
9 }
10
11 try {
12 foo();
13 } catch (e) {
14 error = e;
15 }
16
17 casper.test.assertType(error.stack, "string", "CasperError() has a stack string property set");
18 casper.test.assertMatch(error.stack, /^CasperError: bar\s/, "CasperError() has the expected stack value");
19
20 casper.test.done();