Skip to content

Loading…

Add an onComplete event to the test runner #176

Closed
wheresrhys opened this Issue · 10 comments

6 participants

@wheresrhys

At the moment I'm testing a plugin against multiple versions of jQuery usnig iFrames. As the tests make extensive use of waits() I need the tests in one iframe to complete before starting the next set of tests in a new iframe (the timings get thrown way out when I run all the iframes at once)

In order to achieve this it'd be useful to add an onComplete event that is triggered at the ned of .reportRunnerResults()

@vaeroon

Not sure, this might help:

jasmine.Runner.prototype.finishCallback = function() {
            // invoke your callback here
            jasmine.getEnv().reporter.reportRunnerResults(this);
        };
@infews

I'm confused. Why doesn't reportRunnerResults work for what you need?

@wheresrhys

@vaeroon Good suggestion (thanks :) ), but it'd be good to include something in the documentation, or allow users to create an onComplete function taht gets caleld from in there if it exists

@infews Because my test (because I'm using waits() a lot) contains some asynchronous processes reportRunnerResults runs asynchronously, and execute() returns before the tests have actually completed. I need some way - a custom event would be he obvious method - to listen for the test suite's completion.

@ragaskar

It seems like a bug if reportRunnerResults comes back before all tests have completed. Can you give us a failing spec for this?

@wheresrhys

I wouldn't describe it as a bug as such, more a specific use case for which being able to supply a callback/react to an event would be useful. As test suites can contain asynchronous parts and reportRunnerResults is called after the tests have finished any code run by the user after execute() (in my case opening a new iframe and starting a new test suite with a different version of jQuery) will run before teh asynchronous test, and therefore reportRunnerResults, have run.

@mrrena

We've just started using an implementation of Jasmine / Sinon at work, and I already found and co-opted jasmine.Runner.prototype.finishCallback.

It's not a jasmine problem--if anything, it's a JavaScript problem based on its asynchronous nature--but in our case, a tested block of code contains an ajax call, and the ajax call doesn't always make it back by the time Jasmine's finishCallback completes and our custom "final cleanup" code runs. This timing collision is creating some serious pandemonium.

I really wish there was some clever way that I could figure out to make Jasmine call back after all other calls in the tested code itself have completed and/or figure out a way to roll my own callback for the same: very frustrating!

@mrrena

To follow-up, I say it's not a Jasmine issue above (and technically it's still not, because "finishCallback" is not a public method), but I do wonder why "finishCallback" is being triggered before the "waitsFor" block completes.

First, a custom method we're using:

$redirect = function(url) {
    if (sinon.spoof) {
        return sinon.dom.url = url;
    }
    else {
        window.location = url;
    }
};

"$redirect" is just a testing wrapper for "window.location = url"--if the global testing variable "sinon.spoof" is set to "true," it returns the URL, otherwise it redirects.

So before all tests are started, we set "sinon.spoof" to "true" so that "$redirect" will not redirect. (We use MooTools, so that's what the code samples will be in.)

The spec in question tests the "delegateClick" method of "myList." When an element with the id "newIndividual_btn" passes through this method, it fires off an ajax request. When the ajax request successfully completes, $redirect is triggered. Below is a simplified, cut-away view of the class/method we're testing:

var myList = new Class({

    delegateClick: function(evt) {

        var self = this;
        var target = $(evt.target);

        //New Individual
        if (target.id == 'newIndividual_btn') {

            var submitObj = {
                 'type': 'individual'
            };

            var myRequest = new ReqJSON({
                'url': '/gateway?file=contacts.views&method=newContact',
                'onSuccess': function(responseJSON) {
                    $redirect('information?id=' + responseJSON.id);
                }
            }).send(JSON.encode(submitObj));
        }
     }
});

And here's the skeleton of the spec in question:

    it ("calls valid URL", function() {
        var spy = spyOn(myList, 'delegateClick');
        // custom method below that fires a click event on the target element
        var listener = spy.listen('click', $('newIndividual_btn'));
        waitsFor(function() {
            return sinon.dom.url;
        });  
        runs(function() {
            expect(sinon.dom.url).toContain('information?id=');
        });  
    });

There's lots of custom stuff at play here, but the basic mechanism holds true for Jasmine in general: we trigger a condition in preparation for "waitsFor," but Jasmine's "finishCallback" fires before "waitsFor" completes, and, accordingly, our test runner gets redirected to the new page.

@mrrena

One last follow-up: we did some more testing, and Jasmine's "finishCallback" was NOT the culprit: sorry for the false alarm.

Logging the calls clearly shows that "finishCallback"--without fail--executes only after the "wait" and "waitsFor" have cleared (my kudos to the team on a job outstanding).

Our problem turned out to be that we had two of these specs in our runner, one of which only tested to see if the spy was called with a given listener. As this spy happened to contain a redirect, and since we weren't testing the redirect, nothing had instructed Jasmine that there was a process outstanding and it every so often, it would fire after the "finishCallback" had executed. Had we had a "waitsFor" in there, I'm pretty sure it would have consistently worked without fail.

In short, JASMINE WORKS LIKE A DREAM. :)

@karlwestin

Thanks everyone involved in this thread!
@infews – could you give a short example on how to use reportRunnerResults for some kind of similar effect to a finish callback?
Cheers guys!

@infews

Another idea - and one that should help most folks.

For 2.0, we've introduced the idea of "boot files" for Jasmine gem projects. In fact, Jasmine core now uses the Jasmine gem for devleopment. So you can see the dev_boot.js file we use.

Prior to 2.0, the startup code, including anything that needed to run around the Jasmine enviroment execution, was run in the onloadhandler. And you had no way to get to it.

Now the boot.js file, which you can hack and customize to your heart's content is there for you to insert your project-specific reporters, etc.

Closing.

@infews infews closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.