enhanced stack traces, introduced the CasperError standard error, better display…
… of test suite results
Showing
10 changed files
with
109 additions
and
78 deletions
... | @@ -5,6 +5,7 @@ XXXX-XX-XX, v0.6.0 | ... | @@ -5,6 +5,7 @@ XXXX-XX-XX, v0.6.0 |
5 | ------------------ | 5 | ------------------ |
6 | 6 | ||
7 | - **BC BREAK:** `Casper.click()` now uses native Webkit mouse events instead of previous crazy utopic javascript emulation | 7 | - **BC BREAK:** `Casper.click()` now uses native Webkit mouse events instead of previous crazy utopic javascript emulation |
8 | - **BC BREAK:** All errors thrown by CasperJS core are of the new `CasperError` type | ||
8 | - **BC BREAK:** removed obsolete `replaceFunctionPlaceholders()` | 9 | - **BC BREAK:** removed obsolete `replaceFunctionPlaceholders()` |
9 | - *Deprecated*: `Casper.extend()` method has been deprecated; use natural javascript extension mechanisms instead (see samples) | 10 | - *Deprecated*: `Casper.extend()` method has been deprecated; use natural javascript extension mechanisms instead (see samples) |
10 | - `Casper.open()` can now perform HTTP `GET`, `POST`, `PUT`, `DELETE` and `HEAD` operations | 11 | - `Casper.open()` can now perform HTTP `GET`, `POST`, `PUT`, `DELETE` and `HEAD` operations |
... | @@ -13,6 +14,7 @@ XXXX-XX-XX, v0.6.0 | ... | @@ -13,6 +14,7 @@ XXXX-XX-XX, v0.6.0 |
13 | - introduced the `mouse` module to handle native Webkit mouse events | 14 | - introduced the `mouse` module to handle native Webkit mouse events |
14 | - added support for `RegExp` input in `Casper.resourceExists()` | 15 | - added support for `RegExp` input in `Casper.resourceExists()` |
15 | - added printing of source file path for any uncaught exception printed onto the console | 16 | - added printing of source file path for any uncaught exception printed onto the console |
17 | - added an emulation of stack trace printing (but PhantomJS will have to upgrade its javascript engine for it to be fully working though) | ||
16 | 18 | ||
17 | --- | 19 | --- |
18 | 20 | ... | ... |
... | @@ -89,6 +89,53 @@ if (!phantom.casperLoaded) { | ... | @@ -89,6 +89,53 @@ if (!phantom.casperLoaded) { |
89 | // Adding built-in capabilities to phantom object | 89 | // Adding built-in capabilities to phantom object |
90 | phantom.sourceIds = {}; | 90 | phantom.sourceIds = {}; |
91 | 91 | ||
92 | // custom global CasperError | ||
93 | window.CasperError = function(msg) { | ||
94 | Error.call(this); | ||
95 | try { | ||
96 | // let's get where this error has been thrown from, if we can | ||
97 | this._from = arguments.callee.caller.name; | ||
98 | } catch (e) { | ||
99 | this._from = "anonymous"; | ||
100 | } | ||
101 | this.message = msg; | ||
102 | this.name = 'CasperError'; | ||
103 | }; | ||
104 | |||
105 | // standard Error prototype inheritance | ||
106 | window.CasperError.prototype = Object.getPrototypeOf(new Error()); | ||
107 | |||
108 | // Stack formatting | ||
109 | window.CasperError.prototype.formatStack = function() { | ||
110 | var location; | ||
111 | if (this.fileName || this.sourceId) { | ||
112 | location = (this.fileName || phantom.sourceIds[this.sourceId]); | ||
113 | } else { | ||
114 | location = "unknown"; | ||
115 | } | ||
116 | location += this.line ? ':' + this.line : 0; | ||
117 | return this.toString() + '\n ' + (this._from || "anonymous") + '() at ' + location; | ||
118 | }; | ||
119 | |||
120 | // Adding stack traces to CasperError | ||
121 | // Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/ | ||
122 | // TODO: remove when phantomjs has js engine upgrade | ||
123 | if (!new CasperError().hasOwnProperty('stack')) { | ||
124 | Object.defineProperty(CasperError.prototype, 'stack', { | ||
125 | set: function(string) { | ||
126 | this._stack = string; | ||
127 | }, | ||
128 | get: function() { | ||
129 | if (this._stack) { | ||
130 | return this._stack; | ||
131 | } | ||
132 | return this.formatStack(); | ||
133 | }, | ||
134 | configurable: true, | ||
135 | enumerable: true | ||
136 | }); | ||
137 | } | ||
138 | |||
92 | phantom.getErrorMessage = function(e) { | 139 | phantom.getErrorMessage = function(e) { |
93 | return (e.fileName || this.sourceIds[e.sourceId]) + ':' + e.line + ' ' + e; | 140 | return (e.fileName || this.sourceIds[e.sourceId]) + ':' + e.line + ' ' + e; |
94 | }; | 141 | }; |
... | @@ -190,30 +237,6 @@ if (!phantom.casperLoaded) { | ... | @@ -190,30 +237,6 @@ if (!phantom.casperLoaded) { |
190 | }; | 237 | }; |
191 | })(require, phantom.casperPath); | 238 | })(require, phantom.casperPath); |
192 | 239 | ||
193 | // Adding stack traces to Error | ||
194 | // Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/ | ||
195 | // TODO: remove when phantomjs has js engine upgrade | ||
196 | if (!new Error().hasOwnProperty('stack')) { | ||
197 | Object.defineProperty(Error.prototype, 'stack', { | ||
198 | set: function(string) { | ||
199 | this._stack = string; | ||
200 | }, | ||
201 | get: function() { | ||
202 | var location; | ||
203 | if (this._stack) { | ||
204 | return this._stack; | ||
205 | } else if (this.fileName || this.sourceId) { | ||
206 | location = phantom.getErrorMessage(this); | ||
207 | } else { | ||
208 | location = "unknown"; | ||
209 | } | ||
210 | return this.toString() + '\n at ' + location; | ||
211 | }, | ||
212 | configurable: true, | ||
213 | enumerable: true | ||
214 | }); | ||
215 | } | ||
216 | |||
217 | // BC < 0.6 | 240 | // BC < 0.6 |
218 | phantom.Casper = require('casper').Casper; | 241 | phantom.Casper = require('casper').Casper; |
219 | 242 | ... | ... |
... | @@ -166,7 +166,7 @@ Casper.prototype.capture = function(targetFile, clipRect) { | ... | @@ -166,7 +166,7 @@ Casper.prototype.capture = function(targetFile, clipRect) { |
166 | targetFile = fs.absolute(targetFile); | 166 | targetFile = fs.absolute(targetFile); |
167 | if (clipRect) { | 167 | if (clipRect) { |
168 | if (!utils.isClipRect(clipRect)) { | 168 | if (!utils.isClipRect(clipRect)) { |
169 | throw new Error("clipRect must be a valid ClipRect object."); | 169 | throw new CasperError("clipRect must be a valid ClipRect object."); |
170 | } | 170 | } |
171 | previousClipRect = this.page.clipRect; | 171 | previousClipRect = this.page.clipRect; |
172 | this.page.clipRect = clipRect; | 172 | this.page.clipRect = clipRect; |
... | @@ -260,7 +260,7 @@ Casper.prototype.click = function(selector, fallbackToHref) { | ... | @@ -260,7 +260,7 @@ Casper.prototype.click = function(selector, fallbackToHref) { |
260 | */ | 260 | */ |
261 | Casper.prototype.createStep = function(fn, options) { | 261 | Casper.prototype.createStep = function(fn, options) { |
262 | if (!utils.isFunction(fn)) { | 262 | if (!utils.isFunction(fn)) { |
263 | throw new Error("createStep(): a step definition must be a function"); | 263 | throw new CasperError("createStep(): a step definition must be a function"); |
264 | } | 264 | } |
265 | fn.options = utils.isObject(options) ? options : {}; | 265 | fn.options = utils.isObject(options) ? options : {}; |
266 | this.emit('step.created', fn); | 266 | this.emit('step.created', fn); |
... | @@ -467,10 +467,10 @@ Casper.prototype.fetchText = function(selector) { | ... | @@ -467,10 +467,10 @@ Casper.prototype.fetchText = function(selector) { |
467 | Casper.prototype.fill = function(selector, vals, submit) { | 467 | Casper.prototype.fill = function(selector, vals, submit) { |
468 | submit = submit === true ? submit : false; | 468 | submit = submit === true ? submit : false; |
469 | if (!utils.isString(selector) || !selector.length) { | 469 | if (!utils.isString(selector) || !selector.length) { |
470 | throw new Error("Form selector must be a non-empty string"); | 470 | throw new CasperError("Form selector must be a non-empty string"); |
471 | } | 471 | } |
472 | if (!utils.isObject(vals)) { | 472 | if (!utils.isObject(vals)) { |
473 | throw new Error("Form values must be provided as an object"); | 473 | throw new CasperError("Form values must be provided as an object"); |
474 | } | 474 | } |
475 | this.emit('fill', selector, vals, submit); | 475 | this.emit('fill', selector, vals, submit); |
476 | var fillResults = this.evaluate(function(selector, values) { | 476 | var fillResults = this.evaluate(function(selector, values) { |
... | @@ -480,7 +480,7 @@ Casper.prototype.fill = function(selector, vals, submit) { | ... | @@ -480,7 +480,7 @@ Casper.prototype.fill = function(selector, vals, submit) { |
480 | values: vals | 480 | values: vals |
481 | }); | 481 | }); |
482 | if (!fillResults) { | 482 | if (!fillResults) { |
483 | throw new Error("Unable to fill form"); | 483 | throw new CasperError("Unable to fill form"); |
484 | } else if (fillResults.errors.length > 0) { | 484 | } else if (fillResults.errors.length > 0) { |
485 | (function(self){ | 485 | (function(self){ |
486 | fillResults.errors.forEach(function(error) { | 486 | fillResults.errors.forEach(function(error) { |
... | @@ -546,13 +546,13 @@ Casper.prototype.getCurrentUrl = function() { | ... | @@ -546,13 +546,13 @@ Casper.prototype.getCurrentUrl = function() { |
546 | */ | 546 | */ |
547 | Casper.prototype.getElementBounds = function(selector) { | 547 | Casper.prototype.getElementBounds = function(selector) { |
548 | if (!this.exists(selector)) { | 548 | if (!this.exists(selector)) { |
549 | throw new Error("No element matching selector found: " + selector); | 549 | throw new CasperError("No element matching selector found: " + selector); |
550 | } | 550 | } |
551 | var clipRect = this.evaluate(function(selector) { | 551 | var clipRect = this.evaluate(function(selector) { |
552 | return __utils__.getElementBounds(selector); | 552 | return __utils__.getElementBounds(selector); |
553 | }, { selector: selector }); | 553 | }, { selector: selector }); |
554 | if (!utils.isClipRect(clipRect)) { | 554 | if (!utils.isClipRect(clipRect)) { |
555 | throw new Error('Could not fetch boundaries for element matching selector: ' + selector); | 555 | throw new CasperError('Could not fetch boundaries for element matching selector: ' + selector); |
556 | } | 556 | } |
557 | return clipRect; | 557 | return clipRect; |
558 | }; | 558 | }; |
... | @@ -576,7 +576,7 @@ Casper.prototype.getGlobal = function(name) { | ... | @@ -576,7 +576,7 @@ Casper.prototype.getGlobal = function(name) { |
576 | return result; | 576 | return result; |
577 | }, {'name': name}); | 577 | }, {'name': name}); |
578 | if ('error' in result) { | 578 | if ('error' in result) { |
579 | throw new Error(result.error); | 579 | throw new CasperError(result.error); |
580 | } else if (utils.isString(result.value)) { | 580 | } else if (utils.isString(result.value)) { |
581 | return JSON.parse(result.value); | 581 | return JSON.parse(result.value); |
582 | } else { | 582 | } else { |
... | @@ -662,20 +662,20 @@ Casper.prototype.open = function(location, settings) { | ... | @@ -662,20 +662,20 @@ Casper.prototype.open = function(location, settings) { |
662 | }; | 662 | }; |
663 | } | 663 | } |
664 | if (!utils.isObject(settings)) { | 664 | if (!utils.isObject(settings)) { |
665 | throw new Error("open(): request settings must be an Object"); | 665 | throw new CasperError("open(): request settings must be an Object"); |
666 | } | 666 | } |
667 | // http method | 667 | // http method |
668 | // taken from https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L302 | 668 | // taken from https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L302 |
669 | var methods = ["get", "head", "put", "post", "delete"]; | 669 | var methods = ["get", "head", "put", "post", "delete"]; |
670 | if (settings.method && (!utils.isString(settings.method) || methods.indexOf(settings.method) === -1)) { | 670 | if (settings.method && (!utils.isString(settings.method) || methods.indexOf(settings.method) === -1)) { |
671 | throw new Error("open(): settings.method must be part of " + methods.join(', ')); | 671 | throw new CasperError("open(): settings.method must be part of " + methods.join(', ')); |
672 | } | 672 | } |
673 | // http data | 673 | // http data |
674 | if (settings.data) { | 674 | if (settings.data) { |
675 | if (utils.isObject(settings.data)) { // query object | 675 | if (utils.isObject(settings.data)) { // query object |
676 | settings.data = qs.encode(settings.data); | 676 | settings.data = qs.encode(settings.data); |
677 | } else if (!utils.isString(settings.data)) { | 677 | } else if (!utils.isString(settings.data)) { |
678 | throw new Error("open(): invalid request settings data value: " + settings.data); | 678 | throw new CasperError("open(): invalid request settings data value: " + settings.data); |
679 | } | 679 | } |
680 | } | 680 | } |
681 | // current request url | 681 | // current request url |
... | @@ -740,7 +740,7 @@ Casper.prototype.resourceExists = function(test) { | ... | @@ -740,7 +740,7 @@ Casper.prototype.resourceExists = function(test) { |
740 | testFn = test; | 740 | testFn = test; |
741 | break; | 741 | break; |
742 | default: | 742 | default: |
743 | throw new Error("Invalid type"); | 743 | throw new CasperError("Invalid type"); |
744 | } | 744 | } |
745 | return this.resources.some(testFn); | 745 | return this.resources.some(testFn); |
746 | }; | 746 | }; |
... | @@ -819,10 +819,10 @@ Casper.prototype.runStep = function(step) { | ... | @@ -819,10 +819,10 @@ Casper.prototype.runStep = function(step) { |
819 | */ | 819 | */ |
820 | Casper.prototype.setHttpAuth = function(username, password) { | 820 | Casper.prototype.setHttpAuth = function(username, password) { |
821 | if (!this.started) { | 821 | if (!this.started) { |
822 | throw new Error("Casper must be started in order to use the setHttpAuth() method"); | 822 | throw new CasperError("Casper must be started in order to use the setHttpAuth() method"); |
823 | } | 823 | } |
824 | if (!utils.isString(username) || !utils.isString(password)) { | 824 | if (!utils.isString(username) || !utils.isString(password)) { |
825 | throw new Error("Both username and password must be strings"); | 825 | throw new CasperError("Both username and password must be strings"); |
826 | } | 826 | } |
827 | this.page.settings.userName = username; | 827 | this.page.settings.userName = username; |
828 | this.page.settings.password = password; | 828 | this.page.settings.password = password; |
... | @@ -899,10 +899,10 @@ Casper.prototype.start = function(location, then) { | ... | @@ -899,10 +899,10 @@ Casper.prototype.start = function(location, then) { |
899 | */ | 899 | */ |
900 | Casper.prototype.then = function(step) { | 900 | Casper.prototype.then = function(step) { |
901 | if (!this.started) { | 901 | if (!this.started) { |
902 | throw new Error("Casper not started; please use Casper#start"); | 902 | throw new CasperError("Casper not started; please use Casper#start"); |
903 | } | 903 | } |
904 | if (!utils.isFunction(step)) { | 904 | if (!utils.isFunction(step)) { |
905 | throw new Error("You can only define a step as a function"); | 905 | throw new CasperError("You can only define a step as a function"); |
906 | } | 906 | } |
907 | // check if casper is running | 907 | // check if casper is running |
908 | if (this.checker === null) { | 908 | if (this.checker === null) { |
... | @@ -1002,10 +1002,10 @@ Casper.prototype.thenOpenAndEvaluate = function(location, fn, context) { | ... | @@ -1002,10 +1002,10 @@ Casper.prototype.thenOpenAndEvaluate = function(location, fn, context) { |
1002 | */ | 1002 | */ |
1003 | Casper.prototype.viewport = function(width, height) { | 1003 | Casper.prototype.viewport = function(width, height) { |
1004 | if (!this.started) { | 1004 | if (!this.started) { |
1005 | throw new Error("Casper must be started in order to set viewport at runtime"); | 1005 | throw new CasperError("Casper must be started in order to set viewport at runtime"); |
1006 | } | 1006 | } |
1007 | if (!utils.isNumber(width) || !utils.isNumber(height) || width <= 0 || height <= 0) { | 1007 | if (!utils.isNumber(width) || !utils.isNumber(height) || width <= 0 || height <= 0) { |
1008 | throw new Error(f("Invalid viewport: %dx%d", width, height)); | 1008 | throw new CasperError(f("Invalid viewport: %dx%d", width, height)); |
1009 | } | 1009 | } |
1010 | this.page.viewportSize = { | 1010 | this.page.viewportSize = { |
1011 | width: width, | 1011 | width: width, |
... | @@ -1194,7 +1194,7 @@ Casper.prototype.waitWhileVisible = function(selector, then, onTimeout, timeout) | ... | @@ -1194,7 +1194,7 @@ Casper.prototype.waitWhileVisible = function(selector, then, onTimeout, timeout) |
1194 | Casper.extend = function(proto) { | 1194 | Casper.extend = function(proto) { |
1195 | console.warn('Casper.extend() has been deprecated since 0.6; check the docs'); | 1195 | console.warn('Casper.extend() has been deprecated since 0.6; check the docs'); |
1196 | if (!utils.isObject(proto)) { | 1196 | if (!utils.isObject(proto)) { |
1197 | throw new Error("extends() only accept objects as prototypes"); | 1197 | throw new CasperError("extends() only accept objects as prototypes"); |
1198 | } | 1198 | } |
1199 | utils.mergeObjects(Casper.prototype, proto); | 1199 | utils.mergeObjects(Casper.prototype, proto); |
1200 | }; | 1200 | }; |
... | @@ -1254,7 +1254,7 @@ function createPage(casper) { | ... | @@ -1254,7 +1254,7 @@ function createPage(casper) { |
1254 | } | 1254 | } |
1255 | if (casper.options.clientScripts) { | 1255 | if (casper.options.clientScripts) { |
1256 | if (!utils.isArray(casper.options.clientScripts)) { | 1256 | if (!utils.isArray(casper.options.clientScripts)) { |
1257 | throw new Error("The clientScripts option must be an array"); | 1257 | throw new CasperError("The clientScripts option must be an array"); |
1258 | } else { | 1258 | } else { |
1259 | casper.options.clientScripts.forEach(function(script) { | 1259 | casper.options.clientScripts.forEach(function(script) { |
1260 | if (casper.page.injectJs(script)) { | 1260 | if (casper.page.injectJs(script)) { | ... | ... |
... | @@ -47,7 +47,7 @@ exports.parse = function(phantomArgs) { | ... | @@ -47,7 +47,7 @@ exports.parse = function(phantomArgs) { |
47 | } else if (typeof what === "string") { | 47 | } else if (typeof what === "string") { |
48 | return this.options[what]; | 48 | return this.options[what]; |
49 | } else { | 49 | } else { |
50 | throw new Error("Unsupported cli arg getter " + typeof what); | 50 | throw new CasperError("Unsupported cli arg getter " + typeof what); |
51 | } | 51 | } |
52 | } | 52 | } |
53 | }; | 53 | }; | ... | ... |
... | @@ -47,7 +47,7 @@ EventEmitter.prototype.emit = function() { | ... | @@ -47,7 +47,7 @@ EventEmitter.prototype.emit = function() { |
47 | if (arguments[1] instanceof Error) { | 47 | if (arguments[1] instanceof Error) { |
48 | throw arguments[1]; // Unhandled 'error' event | 48 | throw arguments[1]; // Unhandled 'error' event |
49 | } else { | 49 | } else { |
50 | throw new Error("Uncaught, unspecified 'error' event."); | 50 | throw new CasperError("Uncaught, unspecified 'error' event."); |
51 | } | 51 | } |
52 | return false; | 52 | return false; |
53 | } | 53 | } |
... | @@ -98,7 +98,7 @@ EventEmitter.prototype.emit = function() { | ... | @@ -98,7 +98,7 @@ EventEmitter.prototype.emit = function() { |
98 | // EventEmitter.prototype.emit() is also defined there. | 98 | // EventEmitter.prototype.emit() is also defined there. |
99 | EventEmitter.prototype.addListener = function(type, listener) { | 99 | EventEmitter.prototype.addListener = function(type, listener) { |
100 | if ('function' !== typeof listener) { | 100 | if ('function' !== typeof listener) { |
101 | throw new Error('addListener only takes instances of Function'); | 101 | throw new CasperError('addListener only takes instances of Function'); |
102 | } | 102 | } |
103 | 103 | ||
104 | if (!this._events) this._events = {}; | 104 | if (!this._events) this._events = {}; |
... | @@ -145,7 +145,7 @@ EventEmitter.prototype.on = EventEmitter.prototype.addListener; | ... | @@ -145,7 +145,7 @@ EventEmitter.prototype.on = EventEmitter.prototype.addListener; |
145 | 145 | ||
146 | EventEmitter.prototype.once = function(type, listener) { | 146 | EventEmitter.prototype.once = function(type, listener) { |
147 | if ('function' !== typeof listener) { | 147 | if ('function' !== typeof listener) { |
148 | throw new Error('.once only takes instances of Function'); | 148 | throw new CasperError('.once only takes instances of Function'); |
149 | } | 149 | } |
150 | 150 | ||
151 | var self = this; | 151 | var self = this; |
... | @@ -162,7 +162,7 @@ EventEmitter.prototype.once = function(type, listener) { | ... | @@ -162,7 +162,7 @@ EventEmitter.prototype.once = function(type, listener) { |
162 | 162 | ||
163 | EventEmitter.prototype.removeListener = function(type, listener) { | 163 | EventEmitter.prototype.removeListener = function(type, listener) { |
164 | if ('function' !== typeof listener) { | 164 | if ('function' !== typeof listener) { |
165 | throw new Error('removeListener only takes instances of Function'); | 165 | throw new CasperError('removeListener only takes instances of Function'); |
166 | } | 166 | } |
167 | 167 | ||
168 | // does not use listeners(), so no side effect of creating _events[type] | 168 | // does not use listeners(), so no side effect of creating _events[type] |
... | @@ -231,7 +231,7 @@ EventEmitter.prototype.filter = function() { | ... | @@ -231,7 +231,7 @@ EventEmitter.prototype.filter = function() { |
231 | EventEmitter.prototype.setFilter = function(type, filterFn) { | 231 | EventEmitter.prototype.setFilter = function(type, filterFn) { |
232 | if (!this._filters) this._filters = {}; | 232 | if (!this._filters) this._filters = {}; |
233 | if ('function' !== typeof filterFn) { | 233 | if ('function' !== typeof filterFn) { |
234 | throw new Error('setFilter only takes instances of Function'); | 234 | throw new CasperError('setFilter only takes instances of Function'); |
235 | } | 235 | } |
236 | if (!this._filters[type]) { | 236 | if (!this._filters[type]) { |
237 | this._filters[type] = filterFn; | 237 | this._filters[type] = filterFn; | ... | ... |
... | @@ -41,7 +41,7 @@ exports.create = function(fn) { | ... | @@ -41,7 +41,7 @@ exports.create = function(fn) { |
41 | */ | 41 | */ |
42 | var FunctionArgsInjector = function(fn) { | 42 | var FunctionArgsInjector = function(fn) { |
43 | if (!utils.isFunction(fn)) { | 43 | if (!utils.isFunction(fn)) { |
44 | throw new Error("FunctionArgsInjector() can only process functions"); | 44 | throw new CasperError("FunctionArgsInjector() can only process functions"); |
45 | } | 45 | } |
46 | this.fn = fn; | 46 | this.fn = fn; |
47 | 47 | ||
... | @@ -64,7 +64,7 @@ var FunctionArgsInjector = function(fn) { | ... | @@ -64,7 +64,7 @@ var FunctionArgsInjector = function(fn) { |
64 | this.process = function(values) { | 64 | this.process = function(values) { |
65 | var fnObj = this.extract(this.fn); | 65 | var fnObj = this.extract(this.fn); |
66 | if (!utils.isObject(fnObj)) { | 66 | if (!utils.isObject(fnObj)) { |
67 | throw new Error("Unable to process function " + this.fn.toString()); | 67 | throw new CasperError("Unable to process function " + this.fn.toString()); |
68 | } | 68 | } |
69 | var inject = this.getArgsInjectionString(fnObj.args, values); | 69 | var inject = this.getArgsInjectionString(fnObj.args, values); |
70 | return 'function ' + (fnObj.name || '') + '(){' + inject + fnObj.body + '}'; | 70 | return 'function ' + (fnObj.name || '') + '(){' + inject + fnObj.body + '}'; | ... | ... |
... | @@ -36,62 +36,62 @@ exports.create = function(casper) { | ... | @@ -36,62 +36,62 @@ exports.create = function(casper) { |
36 | 36 | ||
37 | var Mouse = function(casper) { | 37 | var Mouse = function(casper) { |
38 | if (!utils.isCasperObject(casper)) { | 38 | if (!utils.isCasperObject(casper)) { |
39 | throw new Error('Mouse() needs a Casper instance'); | 39 | throw new CasperError('Mouse() needs a Casper instance'); |
40 | } | 40 | } |
41 | 41 | ||
42 | var supportedEvents = ['mouseup', 'mousedown', 'click', 'mousemove']; | 42 | var supportedEvents = ['mouseup', 'mousedown', 'click', 'mousemove']; |
43 | 43 | ||
44 | var computeCenter = function(selector) { | 44 | function computeCenter(selector) { |
45 | var bounds = casper.getElementBounds(selector); | 45 | var bounds = casper.getElementBounds(selector); |
46 | if (utils.isClipRect(bounds)) { | 46 | if (utils.isClipRect(bounds)) { |
47 | var x = Math.round(bounds.left + bounds.width / 2); | 47 | var x = Math.round(bounds.left + bounds.width / 2); |
48 | var y = Math.round(bounds.top + bounds.height / 2); | 48 | var y = Math.round(bounds.top + bounds.height / 2); |
49 | return [x, y]; | 49 | return [x, y]; |
50 | } | 50 | } |
51 | }; | 51 | } |
52 | 52 | ||
53 | var processEvent = function(type, args) { | 53 | function processEvent(type, args) { |
54 | if (!utils.isString(type) || supportedEvents.indexOf(type) === -1) { | 54 | if (!utils.isString(type) || supportedEvents.indexOf(type) === -1) { |
55 | throw new Error('Mouse.processEvent(): Unsupported mouse event type: ' + type); | 55 | throw new CasperError('Mouse.processEvent(): Unsupported mouse event type: ' + type); |
56 | } | 56 | } |
57 | args = Array.prototype.slice.call(args); // cast Arguments -> Array | 57 | args = Array.prototype.slice.call(args); // cast Arguments -> Array |
58 | casper.emit('mouse.' + type.replace('mouse', ''), args); | 58 | casper.emit('mouse.' + type.replace('mouse', ''), args); |
59 | switch (args.length) { | 59 | switch (args.length) { |
60 | case 0: | 60 | case 0: |
61 | throw new Error('Mouse.processEvent(): Too few arguments'); | 61 | throw new CasperError('Mouse.processEvent(): Too few arguments'); |
62 | case 1: | 62 | case 1: |
63 | // selector | 63 | // selector |
64 | var selector = args[0]; | 64 | var selector = args[0]; |
65 | if (!utils.isString(selector)) { | 65 | if (!utils.isString(selector)) { |
66 | throw new Error('Mouse.processEvent(): No valid CSS selector passed: ' + selector); | 66 | throw new CasperError('Mouse.processEvent(): No valid CSS selector passed: ' + selector); |
67 | } | 67 | } |
68 | casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(selector))); | 68 | casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(selector))); |
69 | break; | 69 | break; |
70 | case 2: | 70 | case 2: |
71 | // coordinates | 71 | // coordinates |
72 | if (!utils.isNumber(args[0]) || !utils.isNumber(args[1])) { | 72 | if (!utils.isNumber(args[0]) || !utils.isNumber(args[1])) { |
73 | throw new Error('Mouse.processEvent(): No valid coordinates passed: ' + args); | 73 | throw new CasperError('Mouse.processEvent(): No valid coordinates passed: ' + args); |
74 | } | 74 | } |
75 | casper.page.sendEvent(type, args[0], args[1]); | 75 | casper.page.sendEvent(type, args[0], args[1]); |
76 | break; | 76 | break; |
77 | default: | 77 | default: |
78 | throw new Error('Mouse.processEvent(): Too many arguments'); | 78 | throw new CasperError('Mouse.processEvent(): Too many arguments'); |
79 | } | 79 | } |
80 | }; | 80 | } |
81 | 81 | ||
82 | this.click = function() { | 82 | this.click = function click() { |
83 | processEvent('click', arguments); | 83 | processEvent('click', arguments); |
84 | }; | 84 | }; |
85 | 85 | ||
86 | this.down = function() { | 86 | this.down = function down() { |
87 | processEvent('mousedown', arguments); | 87 | processEvent('mousedown', arguments); |
88 | }; | 88 | }; |
89 | 89 | ||
90 | this.move = function() { | 90 | this.move = function move() { |
91 | processEvent('mousemove', arguments); | 91 | processEvent('mousemove', arguments); |
92 | }; | 92 | }; |
93 | 93 | ||
94 | this.up = function() { | 94 | this.up = function up() { |
95 | processEvent('mouseup', arguments); | 95 | processEvent('mouseup', arguments); |
96 | }; | 96 | }; |
97 | }; | 97 | }; | ... | ... |
... | @@ -31,6 +31,7 @@ | ... | @@ -31,6 +31,7 @@ |
31 | var fs = require('fs'); | 31 | var fs = require('fs'); |
32 | var events = require('events'); | 32 | var events = require('events'); |
33 | var utils = require('utils'); | 33 | var utils = require('utils'); |
34 | var f = utils.format; | ||
34 | 35 | ||
35 | exports.create = function(casper, options) { | 36 | exports.create = function(casper, options) { |
36 | return new Tester(casper, options); | 37 | return new Tester(casper, options); |
... | @@ -47,7 +48,7 @@ var Tester = function(casper, options) { | ... | @@ -47,7 +48,7 @@ var Tester = function(casper, options) { |
47 | this.options = utils.isObject(options) ? options : {}; | 48 | this.options = utils.isObject(options) ? options : {}; |
48 | 49 | ||
49 | if (!utils.isCasperObject(casper)) { | 50 | if (!utils.isCasperObject(casper)) { |
50 | throw new Error("Tester needs a Casper instance"); | 51 | throw new CasperError("Tester needs a Casper instance"); |
51 | } | 52 | } |
52 | 53 | ||
53 | // locals | 54 | // locals |
... | @@ -309,7 +310,7 @@ var Tester = function(casper, options) { | ... | @@ -309,7 +310,7 @@ var Tester = function(casper, options) { |
309 | this.exec = function(file) { | 310 | this.exec = function(file) { |
310 | file = this.filter('exec.file', file) || file; | 311 | file = this.filter('exec.file', file) || file; |
311 | if (!fs.isFile(file) || !utils.isJsFile(file)) { | 312 | if (!fs.isFile(file) || !utils.isJsFile(file)) { |
312 | throw new Error("Can only exec() files with .js or .coffee extensions"); | 313 | throw new CasperError("Can only exec() files with .js or .coffee extensions"); |
313 | } | 314 | } |
314 | this.currentTestFile = file; | 315 | this.currentTestFile = file; |
315 | try { | 316 | try { |
... | @@ -391,15 +392,20 @@ var Tester = function(casper, options) { | ... | @@ -391,15 +392,20 @@ var Tester = function(casper, options) { |
391 | this.assert(true, message); | 392 | this.assert(true, message); |
392 | }; | 393 | }; |
393 | 394 | ||
394 | this.renderFailureDetails = function() { | 395 | /** |
395 | if (this.testResults.failures.length === 0) { | 396 | * Renders a detailed report for each failed test. |
397 | * | ||
398 | * @param Array failures | ||
399 | */ | ||
400 | this.renderFailureDetails = function(failures) { | ||
401 | if (failures.length === 0) { | ||
396 | return; | 402 | return; |
397 | } | 403 | } |
398 | casper.echo("\nFailed test details\n"); | 404 | casper.echo(f("\nDetails for the %d failed tests:\n", failures.length), "PARAMETER"); |
399 | this.testResults.failures.forEach(function(failure) { | 405 | failures.forEach(function(failure) { |
400 | casper.echo('In ' + failure.file + ':'); | 406 | casper.echo('In ' + failure.file + ':'); |
401 | var message; | 407 | var message; |
402 | if (utils.isType(failure.message, "error")) { | 408 | if (utils.isType(failure.message, "object") && failure.message.stack) { |
403 | message = failure.message.stack; | 409 | message = failure.message.stack; |
404 | } else { | 410 | } else { |
405 | message = failure.message; | 411 | message = failure.message; |
... | @@ -432,7 +438,7 @@ var Tester = function(casper, options) { | ... | @@ -432,7 +438,7 @@ var Tester = function(casper, options) { |
432 | } | 438 | } |
433 | casper.echo(this.colorize(utils.fillBlanks(result), style)); | 439 | casper.echo(this.colorize(utils.fillBlanks(result), style)); |
434 | if (this.testResults.failed > 0) { | 440 | if (this.testResults.failed > 0) { |
435 | this.renderFailureDetails(); | 441 | this.renderFailureDetails(this.testResults.failures); |
436 | } | 442 | } |
437 | if (save && utils.isFunction(require)) { | 443 | if (save && utils.isFunction(require)) { |
438 | try { | 444 | try { |
... | @@ -454,7 +460,7 @@ var Tester = function(casper, options) { | ... | @@ -454,7 +460,7 @@ var Tester = function(casper, options) { |
454 | this.runSuites = function() { | 460 | this.runSuites = function() { |
455 | var testFiles = [], self = this; | 461 | var testFiles = [], self = this; |
456 | if (arguments.length === 0) { | 462 | if (arguments.length === 0) { |
457 | throw new Error("No test suite to run"); | 463 | throw new CasperError("No test suite to run"); |
458 | } | 464 | } |
459 | Array.prototype.forEach.call(arguments, function(path) { | 465 | Array.prototype.forEach.call(arguments, function(path) { |
460 | if (!fs.exists(path)) { | 466 | if (!fs.exists(path)) { | ... | ... |
... | @@ -268,7 +268,7 @@ exports.isString = isString; | ... | @@ -268,7 +268,7 @@ exports.isString = isString; |
268 | */ | 268 | */ |
269 | function isType(what, typeName) { | 269 | function isType(what, typeName) { |
270 | if (typeof typeName !== "string" || !typeName) { | 270 | if (typeof typeName !== "string" || !typeName) { |
271 | throw new Error("You must pass isType() a typeName string"); | 271 | throw new CasperError("You must pass isType() a typeName string"); |
272 | } | 272 | } |
273 | return betterTypeOf(what).toLowerCase() === typeName.toLowerCase(); | 273 | return betterTypeOf(what).toLowerCase() === typeName.toLowerCase(); |
274 | } | 274 | } | ... | ... |
... | @@ -33,12 +33,12 @@ | ... | @@ -33,12 +33,12 @@ |
33 | this.test.assertEquals(results.testdown, [200, 100], 'Mouse.down() has pressed button to the specified position'); | 33 | this.test.assertEquals(results.testdown, [200, 100], 'Mouse.down() has pressed button to the specified position'); |
34 | 34 | ||
35 | t.comment('Mouse.up()'); | 35 | t.comment('Mouse.up()'); |
36 | this.mouse.up(200, 100); | 36 | this.mouse.up(200, 200); |
37 | results = this.getGlobal('results'); | 37 | results = this.getGlobal('results'); |
38 | this.test.assertEquals(results.testup, [200, 100], 'Mouse.up() has released button to the specified position'); | 38 | this.test.assertEquals(results.testup, [200, 100], 'Mouse.up() has released button to the specified position'); |
39 | 39 | ||
40 | t.comment('Mouse.move()'); | 40 | t.comment('Mouse.move()'); |
41 | this.mouse.move(200, 100); | 41 | this.mouse.move(200); |
42 | results = this.getGlobal('results'); | 42 | results = this.getGlobal('results'); |
43 | this.test.assertEquals(results.testmove, [200, 100], 'Mouse.move() has moved to the specified position'); | 43 | this.test.assertEquals(results.testmove, [200, 100], 'Mouse.move() has moved to the specified position'); |
44 | }); | 44 | }); | ... | ... |
-
Please register or sign in to post a comment