Jasmine – how to mock window.location

I got some javascript code that I wanna test using Jasmine:

Cherie.Controllers.Help = function () {
    var $question = $('.question'),
        $help_section = $('#help_section');

    var event_handlers = {
        hide_event:{
            highlight_questions:function () {
                if (window.location.pathname == "/help") {
                    $question.each(function () {
                        if ($(this).attr('class').indexOf('highlight') == -1)
                            $(this).hide();
                    })
                }

            }
        },

        click_event:{
            question_list:function () {
                $help_section.on('click', '.question', function () {
                    $(this).next('.answer').toggle();
                    $this.find('.toggle').toggleClass('hide');
                });
            }
        }
    }

    $.each(event_handlers, function(i,handler_type){ $.each(handler_type, function(i,handler){ handler(); }) });
}

$(function() {
    Cherie.Controllers.Help();

});

I am gonna start with testing the hide_event, so I got to mock window.location.pathname… window.location can be written without the window prefix, and location is an object, which makes pathname a property (location.property), so we can’t “spyon” it, since spyon is used for mocking/stubbing functions, so let’s work around it so that we can use spyOn.

make a helper function:

var get_current_location = {
    get_pathname: function () {
        return window.location.pathname;
    }
};

and now the new code looks like this:

        hide_event:{
            highlight_questions:function () {
                if (get_current_location.get_pathname() == "/help") {
                    $question.each(function () {
                        if ($(this).attr('class').indexOf('highlight') == -1)
                            $(this).hide();
                    })
                }

            }
        }

great, on to jasmine…

describe("help.js", function () {
    beforeEach(function () {
        loadFixtures('help.html');
    });

    describe("hide_event", function (){
        it("should hide non-highlighted questions", function () {
            spyOn(get_current_location, 'get_pathname').andReturn("/help");
            Cherie.Controllers.Help();
            expect($('.highlight')).toBeVisible();
            expect($('.question:not(.highlight)')).toBeHidden();
        });

    });

    describe("click_event", function(){
        beforeEach(function(){
            Cherie.Controllers.Help();
            $('.question').first().click();
        });

        it("should show the corresponding answer", function(){
            expect($('.answer').first()).toBeVisible();
        });

        it("should show a '-' image with .hide", function(){
            expect($('.toggle').first()).toHaveClass('hide');
        });

        it("should show a '+' image without .hide on 2nd click", function(){
            $('.question').first().click();
            expect($('.toggle').first()).not.toHaveClass('hide');
        })
    });


});

& it’s alllll good (:

sidenote: The reason we have it as javascript is because we use the same partial across several different pages (not all questions are hidden); another way to implement it w/o javascript is to use conditionals along with css such as .hidden{display:none;}

hide_question = request.path == "/help" ? "hidden" : "" 

and then add the following to certain question list items.

{:class => "#{hide_question}"} 

There are a couple different ways to do this but you get the gist (; which one do you like better? javascript or rails way?

Advertisements

2 comments

  1. Many thanks! It’s a great solution.

    Note: In Jasmine 2 the “.andReturn” syntax is depracated and would be:
    spyOn(get_current_location, ‘get_pathname’).and.returnValue(“/help”);

would you like to leave a comment?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s