1

I just started working with CasperJs, and I'm having difficulty debugging it because so many coding mistakes seem to cause the script to exit without providing an error message. When you use verbose mode, you get the messages you should get up until the offending line of code, and at that point it just quits.

For example, if I execute the code:

var casper = require('casper').create({
    verbose: true,
    logLevel: "debug"
});

casper.start('https://www.google.com/#q=stackoverflow', function(){

});
casper.wait(2000, function(){
});


casper.then(function() {
    hrefAr = this.evaluate(getLinks);
    this.log(hrefAr.length + ' links found', 'info');
    this.exit();
});

casper.run(function() {
    this.exit();
});

function getLinks() {
    var links = document.querySelectorAll('a');
    return Array.prototype.map.call(links, function(e) {
        return e.getAttribute('href');
    });
}

I get the following results:

...a bunch of info & debug messages, followed by...
[info] [phantom] Step 4/4 https://www.google.com/search?q=stackoverflow&cad=h (HTTP 200)
[info] [phantom] 89 links found
[debug] [phantom] Navigation requested: url=about:blank, type=Other, lock=true, isMainFrame=true
[debug] [phantom] url changed to "about:blank"

If I add a log statement to the function getLinks...

...code as shown above...
function getLinks() {
    this.log('getLinks ran', 'info');
    var links = document.querySelectorAll('a');
...code as shown above...

...this causes to script to fail, like so:

...the same info & debug messages...
[info] [phantom] Step 4/4 https://www.google.com/search?q=stackoverflow&cad=h (HTTP 200)
...NO LOGS, ECHOS, OR RESULTS PAST THIS POINT, JUST THESE TWO CLOSING STATEMENTS...
[debug] [phantom] Navigation requested: url=about:blank, type=Other, lock=true, isMainFrame=true
[debug] [phantom] url changed to "about:blank"

It doesn't tell you that anything has gone wrong, it just sends you back to blank and ends execution.

Is there a way to get better error reporting? Or any error reporting?


When I try to implement the workaround below using the following code:

var casper = require('casper').create({
    verbose: true,
    logLevel: "debug"
});

casper.start('https://www.google.com/#q=stackoverflow', function(){

});
casper.wait(2000, function(){
});


casper.then(function() {
    reportErrors(function() {
        hrefAr = this.evaluate(getLinks);
        this.log(hrefAr.length + ' links found', 'info');
        this.exit();
    });
});

casper.run(function() {
    this.exit();
});

function getLinks() {

        //this.log('getLinks ran', 'info');
        var links = document.querySelectorAll('a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });

}

function reportErrors(f) {
  var ret = null;
  try {
    ret = f();
  } catch (e) {
    casper.echo("ERROR: " + e);
    casper.exit();
  }
  return ret;
}

I get...

...info & debug messages shown above...
[info] [phantom] Step 4/4 https://www.google.com/search?q=stackoverflow&cad=h (HTTP 200)
ERROR: TypeError: undefined is not a constructor (evaluating 'this.evaluate(getLinks)') 
--THIS IS WHERE MY LINK COUNT WOULD BE REPORTED
[debug] [phantom] Navigation requested: url=about:blank, type=Other, lock=true, isMainFrame=true
[debug] [phantom] url changed to "about:blank"
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Rebecca
  • 489
  • 1
  • 6
  • 11
  • 2
    Instead of `this.evaluate`, `this.log`, `this.exit` you can use `casper.evaluate`, etc. – Leventix Feb 25 '16 at 21:04
  • I've also been finding @NiKo's method for printing console.log events to the screen (http://stackoverflow.com/a/11957179/1542008) extremely useful. For noobs like me, you just paste the casper.on statement into the bottom of the page, and it will do its work from there. If you're on a PC (and therefore do not have colored output for casper.log events (http://docs.casperjs.org/en/latest/modules/colorizer.html#index-1)), it seems easier (to me) to add the casper.on statement and then use console.log for all the log messages in your code, bypassing casper.log altogether. – Rebecca Feb 25 '16 at 23:33

2 Answers2

3

There is an open PhantomJS issue on this.

You can get around it by wrapping each then & similar with a reportErrors function, e.g.:

function reportErrors(f) {
  var ret = null;
  try {
    ret = f();
  } catch (e) {
    casper.echo("ERROR: " + e);
    casper.exit();
  }
  return ret;
}

casper.then(function() {
  reportErrors(function() {
    ...
  });
});
Leventix
  • 3,789
  • 1
  • 32
  • 41
  • Thanks. This was very helpful for me and caught that a line of code that wasn't even suspicious to me was causing crazy problems. `CasperError: casper.test property is only available using the \`casperjs test\` command` – Ryan May 25 '16 at 15:15
  • Actually I had to use the version of your function edited here: http://stackoverflow.com/a/35638319/470749 – Ryan May 25 '16 at 15:20
2

Until the error swallowing in PhantomJS 2.x is fixed, you can try several things:

  • Use PhantomJS 1.9.8. The underlying QtWebKit engine is more than 5 years old by now, but it still works rather well most of the time. You can add commandline options such as --ignore-ssl-errors=true if there are SSL/TLS problems. CasperJS supports changing the PhantomJS version on demand for the current terminal session by setting the PHANTOMJS_EXECUTABLE environment variable to the executable or path of executable that you want to use. For example, on Windows: set PHANTOMJS_EXECUTABLE=phantomjs198 (I have them numbered and in the PATH).

  • If it's the syntax errors, you're worried about, then run a linter over your code first. I can recommend eslint and jshint.

  • Use additional events to detect errors (with PhantomJS 1.9.8): resource.error, page.error, remote.message and casper.page.onResourceTimeout. Example


Regarding your special function. this has not meaning in the callback of reportErrors. You need to bind another object:

casper.reportErrors = function (f) {
  var ret = null;
  try {
    ret = f.call(this);
  } catch (e) {
    this.echo("ERROR: " + e);
    this.exit();
  }
  return ret;
}

and then you can use it as before:

casper.then(function() {
    this.reportErrors(function() {
        hrefAr = this.evaluate(getLinks);
        this.log(hrefAr.length + ' links found', 'info');
        this.exit();
    });
});
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • This version does return an error when you introduce an error (and runs without error when everything else is correct). However, the error is always "ERROR: TypeError: null is not an object (evaluating 'hrefAr.length')". This was the case when I started getLinks with "var test = notdefined;" and when I started getLinks with "this.log('getLinks ran', 'info')". But at least it tells me that there was an error! – Rebecca Feb 25 '16 at 23:19
  • Okay, so now I'm seeing the error "Error: ReferenceError: Can't find variable: notdefined" before the hrefAr error when I run the script with the "var test=notdefined" statement. – Rebecca Feb 25 '16 at 23:59