AJAX testing with Jasmine 2.0 - 01

Diligently following along the lines traced by online guides and screen casts I’ve written my tests, when it came to mocking AJAX requests in a jasmine-jquery test suite, like

it("should load new content when an internal link is clicked", function() {
    // set up the spies
    spyOn($, 'ajax').and.returnValue('<p>some new lorem</p>');
    spyOn($, 'isUrlExternal').and.returnValue(false);

    // ajaxify is the jQuery plugin I'm developing
    $frag.ajaxify().find('a[href="/some/url"]').trigger('click');

    // check that the response content has been used
    expect($frag.find("#content").text()).toBe('some new lorem');
});

but that will not work at all. Jasmine 2.0 is out and things will be done differently, specifically using the jasmine-ajax plugin.

Adding the plugin to the Gruntfile

That’s an easy one and the position of the jasmine-ajax plugin in the stack will make no difference since the jasmine source file is loaded, in a grunt managed context, before any other source file.

jasmine: {
    src: 'assets/js/src/*.js',
    options: {
        vendor: [
        'assets/js/vendor/jQuery/jquery.js',
        'assets/js/vendor/jasmine/jasmine-jquery.js',
        'assets/js/vendor/jasmine/jasmine-ajax.js',
        'assets/js/vendor/jQuery/jquery.urlInternal.min.js'
        ],
        specs: 'assets/js/spec/*.js',
        keepRunner: true
    }
}

Rewriting tests

So, following along the lines of the jasmine-ajax tutorial the way to make AJAX-based tests work might be

it("should load new content when an internal link is clicked", function() {

    // mock the check
    spyOn($, 'isUrlExternal').and.returnValue(false);

    // make the ajax request, will use the 'load' method internally
    $frag.ajaxify().find('a[href="/some/url"]').trigger('click');

    // mock the ajax request response
    jasmine.Ajax.requests.mostRecent().response({
        "status": 200,
        "content/type": "text/html",
        "responseText": "<p>some new lorem</p>"
    });

    // BAD testing code, BAD, temporary solution
    // check the new content after some time has passed
    setTimeout(function(){
        expect($frag.find("#content").text()).toBe('some new lorem');
    }, 5000);
});

But this code smells very bad. It actually works but it’s not good at all for a long list of reasons.
I’ll dig deeper into the question to make my code smell less and will probably allow for callback injection in the plugin.

Bonus: keeping the spec runner HTML file

In many online tutorials an HTML spec runner file is used in place of grunt ugly and utilitarian CLI; by default grunt-contrib-jasmine will delete the spec runner after each test run but setting keepRunner to true will avoid that.
Opening the HTML spec runner file in a browser will raise an error for each spec if any kind of fixture loading is involved: simply put fixtures are loaded using AJAX methods and file access is not allowed from HTML files by default.
In Chrome the problem is quickly solved opening Chrome from the command line, Mac here, like

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files &

the hint comes from StackOverflow and I will take no credit for it.