Testing that Google Analytics is firing on a page with CasperJS?

732 views
Skip to first unread message

Victor Hooi

unread,
Dec 6, 2013, 1:13:02 AM12/6/13
to casp...@googlegroups.com
Hi,

I'm looking at using CasperJS for functional tests for a webpage.

Part of that is testing to make sure the Google Analytics calls have been fired correctly.

This blog post:


mentions using SinonJS (http://sinonjs.org/) with PhantomJS to test those calls.

However, the code seems to be more oriented towards unit tests, and using PhantomJS.

Basically, I just want to step through some pages in CasperJS, clicking on buttons, and make sure that the GA events are firing.

Is there an easy way to do this within CasperJS?

Cheers,
Victor

Victor Hooi

unread,
Dec 8, 2013, 10:44:38 PM12/8/13
to casp...@googlegroups.com
Hi,

Ok, so I've got SinonJS injecting into the page via options.clientScripts:

casper.options.clientScripts.push("./sinon-1.7.3.js");

(This SO post mentioned using .push() so as not to break other options - http://stackoverflow.com/questions/18707588/casperjs-using-casper-options-and-specifically-injecting-casper-clientscripts - if that's always required, perhaps it's something that should be noted in the CasperJS docs?). 

Also, I'm still not clear on the difference between using options.clientScripts versus using casper.page.injectJS?

Anyhow, now I'm using casper.evaluate to run the spy:

        casper.evaluate(function() {
            var spy = sinon.spy(_gaq, "push");
            console.log(spy.called);
            this.log(spy.called, 'debug');
        });

However, the console.log output doesn't seem to get pass through to CasperJS, and the CasperJS logging line this.log doesn't seem to work either (fails silently?).

Is there any other way to pass the results from inside the evaluate() through to CasperJS?

Cheers,
Victor

Alon Nisser

unread,
Dec 9, 2013, 7:52:47 AM12/9/13
to casp...@googlegroups.com
yes there is.

you can add this function in your code:

function log(msg){
    console.log(msg);
}

and inside the casper.testsuite (or casper.instance whatever you are doing) add the listener:

casper.on('remote.message',log).

the event 'remote.message' is emitted within the phantomjs page execution context and we receive a 'msg' containing the log event , so If you do "console.log" in your webpage, it would emit remote.message and you could log it to the casperjs logging with the log function we added before.

good luck.

Alon Nisser

unread,
Dec 9, 2013, 7:54:12 AM12/9/13
to casp...@googlegroups.com
You can look at this example (from an opensource project I'm doing) to see how It's implemented) https://github.com/niryariv/opentaba-client/blob/master/tests/test_index_get_gush.js

Nicolas Perriault

unread,
Dec 9, 2013, 8:42:25 AM12/9/13
to casp...@googlegroups.com
On Mon, Dec 9, 2013 at 4:44 AM, Victor Hooi <victo...@gmail.com> wrote:

> - if that's always required, perhaps it's something that should be noted in
> the CasperJS docs?).

It was here already
http://docs.casperjs.org/en/latest/modules/casper.html#index-1

But I added a specific section in the testing documentation
http://docs.casperjs.org/en/latest/testing.html#setting-casper-options-in-the-test-environment

> Also, I'm still not clear on the difference between using
> options.clientScripts versus using casper.page.injectJS?

The clientScripts option is helpul to define scripts to inject in
every page requested, as described in
http://docs.casperjs.org/en/latest/modules/casper.html#clientscripts

> However, the console.log output doesn't seem to get pass through to
> CasperJS, and the CasperJS logging line this.log doesn't seem to work either
> (fails silently?).

Use __utils__.echo()
http://docs.casperjs.org/en/latest/modules/clientutils.html#echo

> Is there any other way to pass the results from inside the evaluate()
> through to CasperJS?

In your example the call to Google Analytics is asynchronous, to you
need to setup something capable of waiting for this operation to
complete and then only perform assertions.

Something like Casper#waitForResource might be a bit much closer to
what you're actually intending to test.

Also, please take the time to read the docs, they're helpful, really.

++

--
Nicolas Perriault
https://nicolas.perriault.net/
Phone: +33 (0) 660 92 08 67

Victor Hooi

unread,
Dec 9, 2013, 9:04:09 PM12/9/13
to casp...@googlegroups.com, nic...@perriault.net
Hi,

@Alon Nisser - Thanks for the tip about logging and .on('remote.message', func), I have that bit working now =). I had a look at your sample code, but that seemed to be using SinonJS's fakeserver, not spies, so not sure how applicable it would be to my use case?

@Nicolas Perriault - Yes, I did read the docs - they are helpful and good =) - but I suspect my lack of JS know-how is getting in the way.

So injectJs() is for injecting into a single page, whilst clientScripts is useful for injecting a script on all pages.

Got it, that makes sense =).

Also - at what point in time does the injection occur? For example, if I already have existing JavaScript on the page - will the injected code execute before or after that code?

The CasperJS docs (http://docs.casperjs.org/en/latest/faq.html?highlight=injectjs#can-i-use-jquery-with-casperjs) don't seem to mention this point, nor do the PhantomJS docs (https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#wiki-webpage-injectJs), but perhaps it's something that should be obvious? (I'm new to JS).

With the GA, I tried using waitForResource() on dc.js (the remote Google Analytics JavaScript file), and it timed-out. My code:

casper.start(url, function() {
        
        casper.waitForResource(/dc.gif/, function() {
            this.echo('DoubleClick JS is loaded.');
        });
        
test.assertTitleMatches(/foobar/);
        this.log('Page title is "' + this.getTitle() + '".', 'debug');

Increased the wait to 10 seconds, same result:

[warning] [phantom] Casper.waitFor() timeout
FAIL /dc.gif/ did not load in 10000ms
#    type: uncaughtError
#    file: test_purchase.js
#    error: /dc.gif/ did not load in 10000ms
#    stack: not provided

I thought this was strange, because Chrome's Dev Console definitely showed dc.js being loaded.

I added the following before casper.test.begin:

casper.on('resource.received', function(resource) {
    casper.echo(resource.url);
});

and I noticed that things like the Facebook all.js weren't being printed out, nor was Google Analytics dc.js.

I'm guessing it might have something to do with them being loaded asynchronously using parentNode.insertBefore() - but CasperJS should still load them, right?

This is the loading code for dc.js:

(function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();

Also - if I am going to use the waitForResource() - does it matter where I put it? For example, should I put it right after casper.start()/casper.open()? Is there any way that the resource could get loaded before the call, and it doesn't detect it?

Finally, assuming I do manage to get waitForResource() to detect dc.js correctly, how exactly do I get it so that evaluate() will get called before anything happens?

For example, we have this at the bottom of the HTML page:

<script type="text/javascript">
        var _gaq = _gaq || [];
</script>
<script type="text/javascript">
            var _gaq = _gaq || [];
            _gaq.push(['_setAccount', 'UA-foobar']);
            _gaq.push(['_setDomainName', 'foobar.com']);
            _gaq.push(['_setAllowLinker', true]);
            if ($.cookies.get('www')) {
                _gaq.push(['_setVar', 'www_' + $.cookies.get('www')]);
            }
            var g = getMemberGender();
            if (g){
                _gaq.push(['_setCustomVar', 2, 'Gender', g, 2]);
            }
            _gaq.push(['_trackPageview']);
            (function() {
                var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
                var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
            })();
        </script>

My understanding of the above is that it creates a _gaq array first, which the dc.js script will then replace with an object (which has it's own push() method), when that gets loaded - which means you can call _gaq.push() right from the get go.

So ideally I'd need to load the SinonJS spies at the very beginning, before anything on the page is executed. Will evaluate() do that? (I did check the docs, but it doesn't seem to mention when it gets executed, in relation to the page's JS - perhaps it's meant to be obvious?).

Also, for the spy, I'm hoping to use this:

spy = casper.evaluate(function() {

    var spy = sinon.spy(_gaq, "push");
    __utils__.echo('Has the GA been called?' + spy.called);
    return spy;
});

I did try adding that in inside of a .start() call, and it didn't print anything out at all. Have I got the syntax right?

Cheers,
Victor
Reply all
Reply to author
Forward
0 new messages