I'm developing a javascript application which listens to window.onerror() and reports to an endpoint me and my team are building. I'm this using grunt-contrib-jasmine for the unit test side of things and am running into issues since my test cases expect errors to be thrown.
When running test cases with phantom, this clutters up my output unnecessarily: http://cl.ly/image/1n3k3z2g2V2l
In the instance of the reference error, my test looks like this:
it("should report reference errors", function() {
spyOn(window.fred, 'sendRequest').andCallFake(function(details) {
expect(details.type).toEqual('js-error');
expect(details.message).toBeNonEmptyString();
});
runs(function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = 'reference_error;';
document.body.appendChild(script);
});
waits(2000);
runs(function() {
expect(window.fred.sendRequest).toHaveBeenCalled();
});
});
I want to be able to handle the ReferenceError which gets thrown at some point in the code. As best I can see, this happens indirectly at document.body.appendChild(script);. Jasmine's expect(fn).toThrow(e) looks like it would do the job if I used it here however the test still fails. I've also tried surround it with a try { ... } catch (err) { ... } but to no avail.
My question, does anyone have any suggestions on how to deal with this?
Normally jasmine will try to catch any exceptions that are thrown by your specs and have those mark the spec as failed. However, because this exception is being thrown by the browser when it tries to parse your script tag, jasmine isn't in the call stack. This is why toThrow doesn't work.
Also, as long as your onerror method returns true the browser should not do it's default error action, in this case log to the console.
Related
I am creating HTTP tests with frisby.js which works on top of jasmine.js.
I also have to create some mongoDB objects to test against.
The problem is when I want to clean up these DB objects. When one of the expects fail I want to intercept that and call my own cleanup function. This means that after each failed test, I won't be able to remove the test objects from the DB.
The afterEach function in jasmine does not work properly and jasmine does not have any support for afterAll or beforeAll yet.
That is why I have made the tests as they are today.
it("testing userform get with correct userID and expect correct return", function() {
var innerUserId = userID;
frisby.create('Should retrieve correct userform and return 200 when using a valid userID')
.get(url.urlify('/api/userform', {id: innerUserId}))
.expectStatus(200)
.afterJSON(function(userform){
// If any of these fail, the after function wont run.
// I want to intercept the error so that I can make sure that the cleanUp function is called
// afterEach does not work. I have tried with done()
var useridJSON = userform.UserId.valueOf();
var firstnameJSON = userform.firstname.valueOf();
var surnameJSON = userform.surname.valueOf();
expect(firstnameJSON).toMatch(testUser.firstName);
expect(surnameJSON).toMatch(testUser.surname);
expect(useridJSON).toMatch(innerUserId);
})
.after(function(){
cleanUp(innerUserId);
})
.toss();
});
I am wondering if there is a way to intercept the error for "expect" in frisby or jasmine so that I can make a call to my own cleanup function before exiting.
Full example here
The quickest solution to this problem is to wrap the error code in a try-catch.
This is because if a javascript error occurs, jasmine will NOT keep running assertions. This is different from an assertion error. If an assertion error occurs, jasmine and frisby will keep on testing all the other assertions and then do the "after"-function.
.afterJSON(function(userform){
try {
var useridJSON = userform.UserId.valueOf();
var firstnameJSON = userform.firstname.valueOf();
var surnameJSON = userform.surname.valueOf();
catch(e) {
cleanUp(innerUserId);
// Can do a throw(e.message); here aswell
}
expect(firstnameJSON).toMatch(testUser.firstName);
expect(surnameJSON).toMatch(testUser.surname);
expect(useridJSON).toMatch(innerUserId);
})
This is not the pretty way, but works.
I ended up adding the throw(e) and placed the expects in a finally scope. This way I got jasmine to present all the errors that occured in the test.
As for "before exiting", how about this:
process.on('uncaughtException', function(err) {
console.error(' Caught exception: ' + err);
});
I noticed that qUnit doesn't give any notice when an exception happens in a later part of the test. For example, running this in a test():
stop();
function myfun(ed) {
console.log('resumed');
start(); //Resume qunit
ok(1,'entered qunit again');
ok(ed.getContent()== 'expected content') // < causes exception, no getContent() yet.
}
R.tinymce.onAddEditor.add(myfun)
in an inner iframe on the page will cause an exception (TypeError: ed.getContent is not a function),
but nothing in Qunit status area tells this. I see 0 failures.
(R being the inner iframe, using technique here: http://www.mattevanoff.com/2011/01/unit-testing-jquery-w-qunit/) Would I be correct in assuming this isn't the best way to go for testing sequences of UI interaction that cause certain results? Is it always better to use something like selenium, even for some mostly-javascript oriented frontend web-app tests?
As a side note, the Firefox console shows the console.log below the exception here, even though it happened first... why?
If you look into qUnit source code, there are two mechanisms handling exceptions. One is controlled by config.notrycatch setting and will wrap test setup, execution and teardown in try..catch blocks. This approach won't help much with exceptions thrown by asynchronous tests however, qUnit isn't the caller there. This is why there is an additional window.onerror handler controlled by Test.ignoreGlobalErrors setting. Both settings are false by default so that both kinds of exceptions are caught. In fact, the following code (essentially same as yours but without TinyMCE-specific parts) produces the expected results for me:
test("foo", function()
{
stop();
function myfun(ed)
{
start();
ok(1, 'entered qunit again');
throw "bar";
}
setTimeout(myfun, 1000);
});
I first see a passed tests with the message "entered qunit again" and then a failed one with the message: "uncaught exception: bar." As to why this doesn't work for you, I can see the following options:
Your qUnit copy is more than two years old, before qUnit issue 134 was fixed and a global exception handler added.
Your code is changing Test.ignoreGlobalErrors setting (unlikely).
There is an existing window.onerror handler that returns true and thus tells qUnit that the error has been handled. I checked whether TinyMCE adds one by default but it doesn't look like it does.
TinyMCE catches errors in event handlers when calling them. This is the logical thing to do when dealing with multiple callbacks, the usual approach is something like this:
for (var i = 0; i < callbacks.length; i++)
{
try
{
callbacks[i]();
}
catch (e)
{
console.error(e);
}
}
By redirecting all exceptions to console.error this makes sure that exceptions are still reported while all callbacks will be called even if one of them throws an exception. However, since the exception is handled jQuery can no longer catch it. Again, I checked whether TinyMCE implements this pattern - it doesn't look like it.
Update: Turns out there is a fifth option that I didn't think of: the exception is fired inside a frame and qUnit didn't set up its global error handler there (already because tracking frame creation is non-trivial, a new frame can be created any time). This should be easily fixed by adding the following code to the frame:
window.onerror = function()
{
if (parent.onerror)
{
// Forward the call to the parent frame
return parent.onerror.apply(parent, arguments);
}
else
return false;
}
Concerning your side-note: the console object doesn't guarantee you any specific order in which messages appear. In fact, the code console.log("foo");throw "bar"; also shows the exception first, followed by the log message. This indicates that log messages are queued and handled delayed, probably for performance reasons. But you would need to look into the implementation of the console object in Firefox to be certain - this is an implementation detail.
I would like to pass errors to an alert to warn the user they made mistake in their code even if they don't have console open.
var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
var head = doc.getElementsByTagName('head')[0];
var scriptElement = doc.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.text = scripts;
try{
head.appendChild(scriptElement);
}
catch(e){ alert("error:"+e.message +" linenumber:"+e.lineNumber);}
The appendChild throws an error when the scripts contain an error. It goes straight to the console though, and I want it to display in an alert, because it is for kids and they might not check the console. The try catch block does not catch the error.
I tried it with eval(scripts).
try{
eval(scripts);} catch(e){ alert("error:"+e.message +" linenumber:"+e.lineNumber);}
this does work but it means that the code is executed twice, and that is very inconvenient in some cases.
I tried monkey patching the console.error:
console.log=function(){alert("taking over the log");}
console.error=function(){alert("taking over the log");}
but that only works when I literally use console.error. Not when an actual error is thrown.
What function sends the error to the console in the case of a real error,if it isn't console.error? and can I access it and change it?
Any ideas? Help would be really appreciated.
Thanks Jenita
Whilst try ... catch will work on the code that the script runs initially, as Jenita says it won't catch Syntax Errors, and also it won't catch errors thrown by callback functions which execute later (long after the try-catch has finished). That means no errors from any functions passed to setTimeout or addEventListener.
However, you can try a different approach. Register an error listener on the window.
window.addEventListener("error", handleError, true);
function handleError(evt) {
if (evt.message) { // Chrome sometimes provides this
alert("error: "+evt.message +" at linenumber: "+evt.lineno+" of file: "+evt.filename);
} else {
alert("error: "+evt.type+" from element: "+(evt.srcElement || evt.target));
}
}
This will be called when an exception is thrown from a callback function. But it will also trigger on general DOM errors such as images failing to load, which you may not be interested in.
It should also fire on Syntax Errors but only if it was able to run first so you should put it in a separate script from the one that may contain typos! (A Syntax Error later in a script will prevent valid lines at the top of the same script from running.)
Unfortunately, I never found a way to get a line number from the evt in Firefox. (Edit: Poke around, I think it might be there now.)
I discovered this when trying to write FastJSLogger, an in-page logger I used back when the browser devtools were somewhat slow.
Desperate to catch line numbers, I started to experiment with wrappers for setTimeout and addEventListener that would re-introduce try-catch around those calls. For example:
var realAddEventListener = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function(type,handler,capture,other){
var newHandler = function(evt) {
try {
return handler.apply(this,arguments);
} catch (e) {
alert("error handling "+type+" event:"+e.message +" linenumber:"+e.lineNumber);
}
};
realAddEventListener.call(this,type,newHandler,capture,other);
};
Obviously this should be done before any event listeners are registered, and possibly even before libraries like jQuery are loaded, to prevent them from grabbing a reference to the real addEventListener before we have been able to replace it.
Ok so the less elegant but highly efficient way of doing this is 'refactoring' your innate console functions. Basically any error or warnings you get are being outputted there by a javascript function that is pretty similar to the familiar console.log() function. The functions that I am talking about are console.warn(), console.info() and console.error(). now let's 're-map' what each of those do:
//remap console to some other output
var console = (function(oldCons){
return {
log: function(text){
oldCons.log(text);
//custom code here to be using the 'text' variable
//for example: var content = text;
//document.getElementById(id).innerHTML = content
},
info: function (text) {
oldCons.info(text);
//custom code here to be using the 'text' variable
},
warn: function (text) {
oldCons.warn(text);
//custom code here to be using the 'text' variable
},
error: function (text) {
oldCons.error(text);
//custom code here to be using the 'text' variable
}
};
}(window.console));
//Then redefine the old console
window.console = console;
Now, generally I would highly advise against using something like this into production and limit it to debugging purposes, but since you are trying to develop a functionality that shows the output of the console, the lines are blurry there, so I'll leave it up to you.
You could wrap the script in its own try/catch, something like:
var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
var head = doc.getElementsByTagName('head')[0];
var scriptElement = doc.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.text = "try{"+scripts+"}catch(e){console.error(e);alert('Found this error: ' + e +'. Check the console.')}"
head.appendChild(scriptElement);
To shortcut a long comment section on "don't use new Function" and/or "eval is evil", this question is about how to access, if possible, error information that is related to a new Function() constructor failing. It's mostly a question to discover a limit in what the browser will let me do when trying to exploit JavaScript to the extent that the spec and standard browser implementations allow. So with that disclaimer in place:
When evaluating code through a new Function() call, is there a way to find out where in the function's content a syntax error occurs, if illegal-syntax code is being evaluated? i.e.:
try {
var generator = new Function(input);
try {
generator();
}
catch (runtimeError) {
console.error("legal code; unforeseen result: ", runtimeError);
}
}
catch (syntaxError) {
console.error("illegal code; syntax errors: ", syntaxError);
}
When the building of the generator fails, is there a way to find out (from the browser, not using jslint or another external library) what the error was or where it occurred?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError/prototype mentions that a SyntaxError has a filename and linenumber property, but these are undefined for dynamic code evaluated through a new Function() constructor from what I can tell, so relying on the error object itself seems not to be an option. Are there alternative ways to introduce the code to the browser so that the code, once we know it has syntax errors from a failing new Function call, can be used to find out where the problem is according to the JS engine used?
(Of course, if the goal was to simply find syntax errors, jslint as a preprocess step would be the go-to solution, but I'm more interested in whether or not browsers can in some way be made to report this information, even if in limited form like "there is SOME error on line/char ...")
afaik impossible to find out where it occured. but you may want to see Exception.message to fetch information what the error was.
example: http://jsbin.com/IRoDiJIV/1/watch?js
Found a solution myself using a simiar method to setting breakpoints in evaled code
In chrome dev tools's sources panel, I put the following in a conditional breakpoint on the new Function line (since it's library code and I can't change it.)
(function(eval_js, load_js) {
try {
eval(eval_js);
} catch (e) {
(function addCode(js) {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = 'data:text/javascript;charset=utf-8,' + escape(js);
document.body.appendChild(e);
console.warn("Inserted Script for ", js);
})(load_js.replace(/;/g,";\n"));
handlerCode = "";
return false;
}
return false;
})("new Function('event', handlerCode)", handlerCode)
I have
function Student(){
var that=this;
that.SaveChanges=function(){
//.....
}
function init(){
that.SaveChanges1();
}
init();
}
<script type="text/javascript">
$(document).ready(function () {
var student=new Student();
});
</script>
With jquery-1.4.4.min.js, I could not save changes, because I made error, but rest of the application work.
With jquery-1.7.1.min.js I get error Object # has no method 'SaveChanges1' and rest of the application does not work.
OR
that.SaveChanges1 is not a function
[Break On This Error]
(77 out of range 4)
What should I do to work like with jquery-1.4.4.min.js?
I think you should try NOT to make errors in your javascript... It's good that it blows up, at least it warns you that something doesn't work! Perhaps you should try running some javascript or selenium tests and perhaps a jslint check to make sure that you don't break any of your javascript functionality!
If you want to ignore your errors in some parts of your program, you can do so by using exception handling. But, you cannot just blindly ignore all errors because when a portion of your script gets an error, that portion of the script has to stop executing as there is no orderly way to continue execution after an error. The javascript interpreter doesn't know which types of errors are harmless and which types mess up the whole script.
To catch an exception in one part of the script and continue executing other parts, you can add your own exception handling like this:
try {
// your code here that might cause a run-time error
} catch(e) {
// might want to put some debugging code here so you know that an error was thrown
}
// more code here that will execute even if the previous code threw an error
Note: you can use exception handling for run-time execution errors. You cannot use it for syntax errors that prevent compilation of the javascript code because when that happens, the interpreter can't even understand your code.