soda soda - 2 months ago 11
Javascript Question

Is there a way to listen to an event in the phantom context from page context?

For example: I open a page with PhantomJS, evaluate an asynchronous script (e.g. ajax). When it succeeds, I want to let the phantom context (outside of

page.evaluate()
) know that the asynchronous process is finished.

I don't want to use
setTimeout
and
setInteval
to wait and check continously in the phantom context that the process is finished.

Answer

That is exactly what the onCallback and window.callPhantom() pair is for.

So, if you have an asynchronous call in the page context like an AJAX request, you can do this:

page.onCallback = function(data){
    console.log("finished: " + data.text);
    phantom.exit();
};
page.evaluate(function(){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/");
    xhr.onreadystatechange = function () {
        var DONE = this.DONE || 4;
        if (this.readyState === DONE){
            window.callPhantom({text: this.responseText});
        }
    };
    xhr.send();
});

The other way to use this is to add the call to your production JavaScript to make testing easier. If you're writing a web application it is sometimes complicated to find a selector which denotes when a page is fully loaded. To make testing such an application easier, you can have something like this in the page JavaScript:

finishOffPage(function callback(){
    if (typeof window.callPhantom === "function") {
        window.callPhantom({type: "loadFinished"});
    }
});

Then you can write tests like this:

page.onCallback = function(data){
    if (data.type === "loadFinished") {
        // do some testing
    }
};
page.open(url);

This is an example where such a thing can be added dynamically: wait for angular app to be fully rendered from phantom script