The stack trace of an exception in a log file is a sign that something went wrong. Even if nobody complains it is wise to take a bit time and investigate it before it really hurts.
Exactly the same applies to JavaScript errors in a web application. I believe that each JavaScript error should be considered as a bug and they should all be fixed before delivering, no matter if they directly impact the user experience or not.
Many great tools (JavaScript Console, Firebug, WebDeveloper Toolbar, …) allow to see the JavaScript errors while developing with Firefox as well as with other browsers and a wisely used error handler can report JavaScript errors to the server in production.
Sadly the support is not good while running automated tests. HtmlUnit provides a great help for that (see for instance this) but often integration tests should run in “real” browsers and not in HtmlUnit (for some good and many bad reasons). I don’t know currently any browser automation tool providing access to the JavaScript errors and to other information available in browsers like Firefox or Chrome that can be helpful for developers conscientious of the quality of their work
WebDriver is here not better than its concurrents and the issue “API for checking for JavaScript errors on the page” is opened since 2 1/2 years without any sign of life from the project’s members. The Selenium 1 issues SEL-522 and SEL-613 are equally asleep. Luckily WebDriver provides the possibility to configure additional extensions when using the FirefoxDriver, what allows to improve the situation.
Tester’s friendly application and injected error handler
The simpliest way to catch JavaScript errors for later retrieval is to add something like that
<script type="text/javascript">
window.jsErrors = [];
window.onerror = function(errorMessage) {
window.jsErrors[window.jsErrors.length] = errorMessage;
}
</script>
in every page of your application (see for instance this post).
This allows you to retrieve the captured errors with
((JavascriptExecutor) driver).executeScript("return window.jsErrors");
This is a simple solution but it has different drawbacks. First you need to add this code at the beginning of all your HTML pages. If you’re in the ideal situation to be both developer and tester of the application it’s “just” one Ctrl+C and a lot of Ctrl+V. If you need to convince your colleagues or your management that the web pages should be modified for testability, it will be more difficult. From my experience it is not always easy to make such a requirement accepted.
To overcome this problem we could imagine injecting the handler while running the test:
String script = "window.collectedErrors = [];"
+ "window.onerror = function(errorMessage) { "
+ "window.collectedErrors[window.collectedErrors.length] = errorMessage;"
+ "}";
((JavascriptExecutor) driver).executeScript(script);
but besides the fact that it has to be done on every page, we can’t do it early enough: a lot of JavaScript may already have been executed in the page when our executeScript statement is reached.
The second problem of an error handler per window is the difficult access to the captured errors. The JavaScript errors won’t necessarily occur in the currently focused window forcing you to iterate through all (i)frames and windows to check for errors. This can still be seen as a minor issue but the real problem is that captured errors get lost when a new document is loaded. At the end this solution allows to capture some JavaScript errors but not all JavaScript errors.
Add support within the FirefoxDriver
Firefox allows extensions to get notified of JavaScript errors, no matter where they occur.
This mechanism is used for instance by Chris Pederick’s famous Web Developer Toolbar to display a warn/error icon when an error has occurred on a page. Once you’ve succeeded in overcoming the sandbox mechanism separating extensions and loaded pages, it is quite easy to write a new Firefox extension that captures the JavaScript errors and makes them available in each content window.
WebDriver on its side allows to add custom extensions to run in a FirefoxDriver. If you simply add this errors capturing extension to the FirefoxProfile you’ll have access to all JavaScript errors from within your tests. Luckily you don’t need to write it again and you can simply use my JSErrorCollector (Apache 2 Licence). It allows to write following:
... import net.jsourcerer.webdriver.jserrorcollector.JavaScriptError; ... final FirefoxProfile profile = new FirefoxProfile(); JavaScriptError.addExtension(profile); final WebDriver driver = new FirefoxDriver(profile); // ... // navigate to some pages // ... List jsErrors = JavaScriptError.readErrors(driver); assertThat(jsErrors, is(empty())
The JavaScriptError class holds the message, source name and line number of a JavaScript error just like what you can see in Firefox’s JavaScript console.
Next steps
Other browsers
It would be nice to have a simple access to JavaScript errors as well in the other browsers supported by WebDriver. For HtmlUnit it is a no-brainer. For Chrome it should be quite simple as an extension can register a global error listener. No idea what concerns IE and Opera.
Integration
Ideally the API should be provided natively by WebDriver, let’s see what will be the interest in my JSErrorCollector (follow issue 148 to stay informed).
Retrieving the JavaScript errors is a first step on which different interesting features could be based. Verifying the absence of JavaScript errors after each test (using for instance JUnit’s @After) works fine but is tedious. One interesting feature would be to let the tests directly fail on the first JavaScript error (I’ll address wrapping drivers in a future post). Alternatively a precise dedicated reporting would have great benefits too. Some filtering features to ignore errors in third party libraries would be a nice feature as well.
JSErrorCollector is open source therefore patches are welcome. If you can’t/don’t want to contribute directly, I’m available to work on it on a contract base.
Aaron Broad said,
October 11, 2011 at 3:35 pm
How difficult would it be to use your Firefox Extension in Ruby?
I’ll be experimenting to find out this afternoon…
Marc Guillemot said,
October 11, 2011 at 5:52 pm
I don’t know WebDriver’s Ruby binding but according to http://http://code.google.com/p/selenium/wiki/RubyBindings#Firefox you can add an extension to the Firefox profile and according to
http://code.google.com/p/selenium/wiki/RubyBindings#API_Example you can execute JavaScript code as well. It will therefore be possible to use the extension from Ruby as well.
Aaron Broad said,
October 11, 2011 at 5:54 pm
Thanks.. I”ll get it working and make a github fork with a ruby example. This is a lifesaver on my current project if I get it working. Thanks for your efforts!
Gabe Kopley said,
November 17, 2011 at 12:27 am
Here’s how I am using it with Rails+Capybara+Cucumber:
https://gist.github.com/1371962
Yaci said,
November 7, 2011 at 11:43 am
I am using different extension that I found somewhere on the web. It captures the content of Firefox error console and saves it to a file.
Quite easy way of checking for JS errors is setting XRE_CONSOLE_LOG environmental variable, which will cause Firefox to automatically log all sort of errors. However in this case a log is produced after closing the browser, so there’s no way of relating an error with a page on which it occured.
Nice post btw, thanks for sharing.
Gibeden said,
November 30, 2011 at 7:13 am
Hello.
Is it possible to use your Firefox Extension in C#?
Marc Guillemot said,
November 30, 2011 at 7:28 am
Sure. The extension itself (the .xpi file) has nothing to do with Java. Just have a look in the code to see how errors are retrieved and you can do the same in C# (you can look at the Java code or at Gabe Kopley’s Ruby code). Feel free to send me a push request for your C# integration if you find it could be useful for other users.
Tim said,
February 5, 2012 at 2:27 pm
where can I find .xpi file ??
I did not find it
Marc Guillemot said,
February 9, 2012 at 7:53 am
The XPI file was available in the jar file but I agree that it was not easy to get it.
I’ve now made it directly available:
https://github.com/mguillem/JSErrorCollector/raw/master/dist/JSErrorCollector.xpi
Gibeden said,
December 15, 2011 at 3:12 pm
Hello.
How to clear all JS errors catched by JSErrorCollector?
I just want get all JS errors after some action (click e.g.), not all JS errors from start of my test case.
Marc Guillemot said,
December 15, 2011 at 3:34 pm
Just call for captured errors before the action and discard the result.
Gibeden said,
December 15, 2011 at 7:24 pm
I’m sorry, but i think i didn’t understand you =(
could you please explain more?
Gibeden said,
December 16, 2011 at 11:41 am
if you don’t mind i allowed myself to make some changes in your overlay.js on line 17:
this.list = [];
now it works exactly what i need.