2

We are using the superb WebdriverJS (with Selenium) to perform acceptance testing on our web app. Everything works fine, and our tests execute successfully when we use Firefox and Safari.

However, when we use PhantomJS, our tests fail with unhelpful errors. It's almost as if... Javascript isn't even running inside the client page! Something that would cause this would be if PhantomJS' javascript environment ran into errors. Unfortunately, I can't seem to find a way to access Javascript errors when using PhantomJS with WebdriverJS.

If we were using PhantomJS directly, we could simply do (from the PhantomJS site):

page.onError = function(msg, trace) {
  console.log(msg);
  trace.forEach(function(item) {
    console.log('  ', item.file, ':', item.line);
  });
}

Unfortunately, I don't know how to access this mysterious page object when using PhantomJS within WebdriverJS. Any thoughts?

oberlies
  • 11,503
  • 4
  • 63
  • 110
rinogo
  • 8,491
  • 12
  • 61
  • 102
  • you usually get the whole error stack within the error parameter after each command (e.g. .click('#elem',function(_err_ , ...)). Does this give you any information? Which unhelpful errors are you getting? – ChristianB Apr 10 '14 at 21:43
  • According to my understanding, there are two JS engines/environments running here - the "host" that runs PhantomJS' tests, and the "client" that runs the actual code of the webpage. I think you're referring to the "host" errors, which I have no trouble accessing. What I need, instead, are the "client" errors... – rinogo Apr 10 '14 at 23:04

2 Answers2

3

You can actually access JS errors in your PhantomJS stdout log at INFO level.

$ phantomjs --webdriver 4444 --webdriver-loglevel=INFO

You can even push things forward by setting the log level to DEBUG and see what actually PhantomJS does to execute the commands you send through Webdriver / Ghostdriver.

xBill
  • 233
  • 1
  • 7
  • So, just to be totally clear, you can confirm that this works even when using PhantomJS with WebdriverJS? Thanks! – rinogo Feb 25 '16 at 17:09
  • 1
    Yes. We use PuantomJS with webdriver.io, which is very similar to WebdriverJS. But anyway, the logs come out of PhantomJS' output, no matter which driver you use to pilot it. – xBill Feb 29 '16 at 08:43
2

I figured out a workable solution! Essentially, it involves using an onerror event handler to intercept (and store) the Javascript errors. Then, once the DOM is ready, we report the errors via hidden DOM elements. This allows Selenium to look for specific elements (e.g. ".javascript-errors"), which is something it's naturally quite good at. Thanks go to myriad other blog posts and SO questions for getting me to this point.

The code:

//For detecting and reporting Javascript errors via Selenium.  Note that this should be in its own file to allow this code to reliably detect syntax errors in other files.
var errors = [];

//Handle all errors
window.onerror = function(message, url, line) {
    errors.push({"message":message, "url":url, "line":line});
}

//Report errors visually via HTML once the DOM is ready
window.onload = function() {
    if(errors.length==0)
        return;

  var div = document.createElement("div");
  div.className = 'javascript-errors';
  div.innerHTML = '';
    var style = "position:absolute; left:-10000px; top:auto; width:1px; height:1px;"; //CSS to hide the errors; we can't use display:none, or Selenium won't be able to read the error messages.  Adapted from http://webaim.org/techniques/css/invisiblecontent/

    for(var i=0; i<errors.length; i++)
      div.innerHTML += '<div class="javascript-error" style="' + style +'"><span class="message">' + errors[i].message.replace('<', '&lt;').replace('>', '&gt;') + '</span><br/><span class="url">' + errors[i].url + '</span><br/><span class="line">' + errors[i].line + '</span></div>';

    document.body.appendChild(div);
}
rinogo
  • 8,491
  • 12
  • 61
  • 102
  • Great! But unfortunately you have to inject this code every time when loading a new page. I implemented an experimental feature in webdriverjs which enables [eventhandling on browser side](https://github.com/camme/webdriverjs#eventhandling-on-browser-side) - so you wont need to inject your code again and again. It currently only works in chrome but I will try to roll it out to other browser – ChristianB Apr 11 '14 at 13:07
  • My biggest concern is with PhantomJS - most browsers will fail their unit tests if there's a Javascript-level error. Finding that error (by hand) is easy with standard browsers. However, it's impossible with PhantomJS unless you have something like what we're talking about. I'd love to see if you can get your code working in PhantomJS! – rinogo Apr 11 '14 at 14:52
  • if using firefoxDriver, you can dump javascript error console into a file by system property `webdriver.log.file` when starting selenium server. see [FirefoxDriver](https://code.google.com/p/selenium/wiki/FirefoxDriver) – shawnzhu Apr 13 '14 at 14:34