Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to catch new window opend by window.open(url, _blank) in PhantomJS?

I'd like to check with PhantomJS whether my script correctly opens a new window/tab on click. The open is triggerd by a js event listener and opened through window.open(url, "_blank").

How can I listen for the new window with PhantomJS?

like image 347
Manuel Avatar asked Jan 16 '15 08:01

Manuel


1 Answers

There seem to be three ways to do this:

onPageCreated

CasperJS solves this by using page.onPageCreated. So when window.open is called in the page, a new page is created and page.onPageCreated is triggered with the newly created page.

page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        page.onPageCreated = function(newPage){
            newPage.onLoadFinished = function(){
                console.log(newPage.url);
                phantom.exit();
            };
        };
        page.evaluate(function(url){
            window.open(url+"?something=other", "_blank");
        }, address);
    }
})

pages

PhantomJS' page has a pages property which handles of the child pages. So, when you open a new page/tab, a new webpage object is created for that page. You need to try to add an onLoadFinished event listener to the page before it fires (no promises). It can be hard and when the window.open is called with an unknown delay from the page context.

This can be fixed by using something like waitFor to wait for the new page to appear and attach the event handler before the page is loaded. Here is the complete code with a small adjustment. The retry interval is reduced to 50ms from 250ms.

var page = require('webpage').create();
var address = "http://example.com/";

function waitFor(testFx, onReady, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
        start = new Date().getTime(),
        condition = false,
        interval = setInterval(function() {
            if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
                // If not time-out yet and condition not yet fulfilled
                condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
            } else {
                if(!condition) {
                    // If condition still not fulfilled (timeout but condition is 'false')
                    console.log("'waitFor()' timeout");
                    phantom.exit(1);
                } else {
                    // Condition fulfilled (timeout and/or condition is 'true')
                    console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
                    typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
                    clearInterval(interval); //< Stop this interval
                }
            }
        }, 50); //< repeat check every 50ms
};

page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        console.log("p:", page.ownsPages, typeof page.pages, page.pages.length, page.pages);
        waitFor(function test(){
            return page.pages && page.pages[0];
        }, function ready(){
            page.pages[0].onLoadFinished = function(){
                console.log("p:", page.ownsPages, typeof page.pages, page.pages.length, page.pages);
                console.log("inner:", page.pages[0].url);
                phantom.exit();
            };
        });
        page.evaluate(function(url){
            window.open(url+"?something=other", "_blank");
        }, address);
    }
})

Proxy solution

The window.open function can be proxied and after the opening the page in the page context, an event handler can be registered in phantom context signaled through window.callPhantom and caught in onCallback.

page.onInitialized = function(){
    page.evaluate(function(){
        var _oldOpen = window.open;
        window.open = function(url, type){
            _oldOpen.call(window, url, type);
            window.callPhantom({type: "open"});
        };
    });
};
page.onCallback = function(data){
    // a little delay might be necessary
    if (data.type === "open" && page.pages.length > 0) {
        var newPage = page.pages[page.pages.length-1];
        newPage.onLoadFinished = function(){
            console.log(newPage.url);
            phantom.exit();
        };
    }
};
page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        page.evaluate(function(url){
            window.open(url+"?something=other", "_blank");
        }, address);
    }
})
like image 159
Artjom B. Avatar answered Nov 15 '22 02:11

Artjom B.