assertEval throws "TypeError: 'null' is not an object" #133

Closed
benbuckman opened this Issue Jun 2, 2012 · 12 comments

Projects

None yet

2 participants

@benbuckman

I'm running a test with many assert and evaluate calls that run correctly, but at some point it breaks:

casper.then(function(){
  this.test.assertEval(function(){
    return true;
  }, 'testing assertEval');

  this.test.assert(function(){
    return this.evaluate(function(){ return true; });
  }, 'testing assert with evaluate');
});

These both throw an error:

FAIL TypeError: 'null' is not an object
type: uncaughtError
error: "TypeError: 'null' is not an object"
TypeError: 'null' is not an object
... :678 in injectClientUtils
... :444 in evaluate
... :180 in assertEval

Looking in Casper.prototype.injectClientUtils in casper.js, it's running this.page.evaluate, but in the assertion where it fails, this.page is null.

Have you seen this one before?

@benbuckman

Adding in some logging -

    this.assertEval = this.assertEvaluate = function assertEval(fn, message, params) {
        console.log('in assertEval, page is', typeof this.page, typeof casper.page, typeof this.evaluate, typeof casper.evaluate);
        ...

It then outputs (before crashing),

in assertEval, page is undefined object undefined function

An evaluate right before the exception-causing assertion works fine. Very strange.

@n1k0
Member
n1k0 commented Jun 2, 2012

Hmm. This is the correct way of testing:

// file: test.js
casper.start();

casper.then(function(){
    this.test.assertEval(function(){
        return true;
    }, 'testing assertEval');

    this.test.assert(this.evaluate(function() {
        return true;
    }), 'testing assert with evaluate');
});

casper.run(function() {
    this.test.done();
});

Running:

$ casperjs test test.js 
Test file: test.js                                                                 
PASS testing assertEval
PASS testing assert with evaluate
PASS 2 tests executed, 2 passed, 0 failed.                                      

It's working side by my side using latest master. Can you confirm it was a mistake by your side? And if so, please close the issue :)

@n1k0
Member
n1k0 commented Jun 2, 2012

As a side note, please make sure the start() method is called before calling assertion methods.

@benbuckman

Thanks. I'm running an "extended" casper object as instructed here: http://casperjs.org/#extending

I was redefining casper as casper again; in this example I've reduced it to the simplest case where it breaks. I'm now extending casper to ghost (running out of ghost names here :)) --

var casper = require('casper').create();

// now extend casper to ghost
// FROM HERE ON USE ghost INSTEAD OF casper
var ghost = Object.create(casper);

ghost.start();  

ghost.then(function(){
  this.test.assertEval(function(){
    return true;
  }, 'testing assertEval');

  this.test.assert(this.evaluate(function() {
    return true;
  }), 'testing assert with evaluate');
});

ghost.run(function() {
  this.test.done();
});

And it fails (first 3 lines I added to debug):

in assertEval, page is undefined object undefined function
in evaluate, page is null! [object Object]
in injectClientUtils, page is null! [object Object]
FAIL TypeError: 'null' is not an object
type: uncaughtError
error: "TypeError: 'null' is not an object"
TypeError: 'null' is not an object
...:454 in evaluate
...:181 in assertEval

Any ideas? I'd like to be able to add custom methods to the casper object, is there a better way to do it?

@benbuckman

It looks like if I simply comment out the line,

casper = Object.create(casper);
(or)
ghost = Object.create(casper);

and stick with the casper object, i can add methods as before and it works fine.

So the bug has something to do with the Extending mechanism described in the documentation.

@n1k0 n1k0 was assigned Jun 3, 2012
@n1k0
Member
n1k0 commented Jun 3, 2012

I confirm the issue. I have to idea why the prototype is not ported to the new instance… I'll be investigating.

@n1k0
Member
n1k0 commented Jun 3, 2012

For some reason — and I'm suspecting it's related to the way native PhantomJS' WebPage prototype behaves — this is the way to do:

var casper = require('casper').create();
casper.start(); // note we're starting the `casper` instance, not the `ghost` one

var ghost = Object.create(casper);

ghost.thenOpen('http://google.fr/', function onStart() {
    this.test.assertEval(function() {
        return true;
    }, 'testing assertEval');

    this.test.assert(this.evaluate(function() {
        return true;
    }), 'testing assert with evaluate');
});

ghost.run();

I've no really idea how to fix this, I don't even know if this should be considered as a casperjs bug…

@benbuckman

Thanks for looking into it.
Is it actually necessary to have the Object.create(casper) extension syntax? Adding methods directly to casper seems to work just as well.

@n1k0
Member
n1k0 commented Jun 3, 2012

It's just matter of having a clone instance rather than a reference to the original object… you're most likely not needing it 90% of time I guess.

Adding methods to an original casper instance works indeed perfectly.

@n1k0
Member
n1k0 commented Jun 3, 2012

As a side note, since I wrote this part of the documentation, I've ported the inherits() method of nodejs into the utils module, so you could also use it to create new Casper derived classes:

var Casper = require('casper').Casper;
var utils = require('utils');

function Ghost(options) {
    Casper.call(this, options);
}
utils.inherits(Ghost, Casper);

var ghost = new Ghost({
    verbose: true,
    logLevel: "debug"
});

ghost.start();

ghost.then(function() {
    this.test.assert(true, 'testing assert');
});

ghost.run(function() {
    this.test.renderResults(true);
});

I'll update the documentation in order to promote this approach — and the monkey-patching one.

@n1k0 n1k0 closed this in bebe38d Jun 3, 2012
@n1k0
Member
n1k0 commented Jun 3, 2012

The extending section is now up to date : http://casperjs.org/extending.html (force refresh the page as github applies client-side caching)

@benbuckman

Nice! Thanks for maintaining this so actively.

@hubpan hubpan pushed a commit to hubpan/casperjs that referenced this issue Feb 7, 2014
@detro @ariya detro + ariya Adding "window.phantomCallback()" within the page context.
The callback is harmless: if the user registers a "page.onCallback = [Function]",
that will receive any JS type passed via "phantomCallback()".
Also, if the handler for ".onCallback" returns a value, that is passed back as a
return value of "phantomCallback()".

Also, added "page.onConfirm" and "page.onPrompt".
This solves [Issue #133](http://code.google.com/p/phantomjs/issues/detail?id=133).
caf1365
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment